@nutshelllabs/stylesheet
v6.1.4
Published
A styles engine for Node and the browser
Readme
@nutshelllabs/stylesheet
Styles resolution engine for react-pdf. Transforms CSS-like style objects into normalized, resolved values ready for layout and rendering. Handles unit conversions, color parsing, shorthand expansion, media queries, and style flattening.
Installation
yarn add @nutshelllabs/stylesheetUsage
import stylesheet from '@nutshelllabs/stylesheet';
const container = {
width: 400,
height: 600,
orientation: 'portrait',
};
const style = {
margin: 20,
width: '50vw',
height: '20vh',
borderRadius: 5,
fontWeight: 'semibold',
borderBottom: '2 solid yellow',
'@media max-width: 500': {
backgroundColor: 'rgb(255, 0, 0)',
},
};
const computed = stylesheet(container, style);
// Result:
// {
// width: 200,
// height: 120,
// marginTop: 20,
// marginLeft: 20,
// marginRight: 20,
// marginBottom: 20,
// borderTopLeftRadius: 5,
// borderTopRightRadius: 5,
// borderBottomLeftRadius: 5,
// borderBottomRightRadius: 5,
// fontWeight: 600,
// borderBottomWidth: 2,
// borderBottomStyle: 'solid',
// borderBottomColor: 'yellow',
// backgroundColor: '#FF0000'
// }Features
Style Flattening
Merges arrays of style objects into a single object. Supports nested arrays and filters out null/undefined values.
import stylesheet, { flatten } from '@nutshelllabs/stylesheet';
const baseStyle = { margin: 10, padding: 5 };
const activeStyle = { backgroundColor: 'blue' };
// Pass an array of styles
const computed = stylesheet(container, [baseStyle, activeStyle]);
// Or use flatten directly
const merged = flatten([baseStyle, null, activeStyle, undefined]);
// => { margin: 10, padding: 5, backgroundColor: 'blue' }Media Queries
Apply styles conditionally based on container dimensions and orientation.
const style = {
fontSize: 12,
'@media max-width: 500': {
fontSize: 10,
},
'@media orientation: landscape': {
flexDirection: 'row',
},
'@media min-width: 400 and max-width: 800': {
padding: 20,
},
};Supported media features:
min-width/max-widthmin-height/max-heightorientation(portrait|landscape)
Unit Conversion
Converts various CSS units to points (the PDF standard unit).
const style = {
width: '2in', // 144pt (72pt per inch)
height: '50mm', // ~141.73pt
padding: '1cm', // ~28.35pt
margin: '10vh', // 10% of container height
fontSize: '5vw', // 5% of container width
gap: '2rem', // 36pt (default rem base: 18pt)
left: '100px', // ~100pt at 72dpi
};| Unit | Description |
| ----- | ------------------------------------ |
| pt | Points (default, 1pt = 1/72 inch) |
| in | Inches (1in = 72pt) |
| mm | Millimeters |
| cm | Centimeters |
| vh | Percentage of container height |
| vw | Percentage of container width |
| rem | Relative to rem base (default 18pt) |
| px | Pixels (converted using DPI setting) |
Color Transformation
Normalizes color values to hexadecimal format.
import { transformColor } from '@nutshelllabs/stylesheet';
transformColor('rgb(255, 0, 0)'); // => '#FF0000'
transformColor('rgba(0, 0, 255, 0.5)'); // => '#0000FF80'
transformColor('hsl(120, 100%, 50%)'); // => '#00FF00'
transformColor('hsla(0, 100%, 50%, 0.8)'); // => '#FF0000CC'
transformColor('red'); // => 'red' (passed through)Shorthand Expansion
Expands CSS shorthand properties into their individual components.
Margin & Padding
// Input
{ margin: '10 20 30 40' }
// Output
{
marginTop: 10,
marginRight: 20,
marginBottom: 30,
marginLeft: 40
}
// Also supports marginHorizontal/marginVertical
{ marginHorizontal: 20, marginVertical: 10 }
// Output
{
marginTop: 10,
marginRight: 20,
marginBottom: 10,
marginLeft: 20
}Border
// Input
{ border: '2 solid red' }
// Output
{
borderTopWidth: 2,
borderTopStyle: 'solid',
borderTopColor: 'red',
borderRightWidth: 2,
borderRightStyle: 'solid',
borderRightColor: 'red',
borderBottomWidth: 2,
borderBottomStyle: 'solid',
borderBottomColor: 'red',
borderLeftWidth: 2,
borderLeftStyle: 'solid',
borderLeftColor: 'red',
}
// Border radius expansion
{ borderRadius: 5 }
// Output
{
borderTopLeftRadius: 5,
borderTopRightRadius: 5,
borderBottomRightRadius: 5,
borderBottomLeftRadius: 5,
}Flex
// Input
{ flex: '1 0 auto' }
// Output
{
flexGrow: 1,
flexShrink: 0,
flexBasis: 'auto'
}
// Single value
{ flex: 1 }
// Output
{ flexGrow: 1 }Gap
// Input
{ gap: '10 20' }
// Output
{
rowGap: 10,
columnGap: 20
}Object Position
// Input
{ objectPosition: '50% 25%' }
// Output
{
objectPositionX: 0.5,
objectPositionY: 0.25
}Transform Origin
// Input
{ transformOrigin: 'center top' }
// Output
{
transformOriginX: '50%',
transformOriginY: '0%'
}Transform Parsing
Parses CSS transform strings into structured transform arrays.
// Input
{
transform: 'translate(10, 20) rotate(45) scale(2)';
}
// Output
{
transform: [
{ operation: 'translate', value: [10, 20] },
{ operation: 'rotate', value: [45] },
{ operation: 'scale', value: [2, 2] },
];
}Supported transform functions:
translate(x, y)- TranslationtranslateX(x)/translateY(y)- Single-axis translationrotate(angle)- Rotation in degreesscale(x, y)- Scaling (y defaults to x if omitted)scaleX(x)/scaleY(y)- Single-axis scalingskew(x, y)- Skewing in degreesskewX(x)/skewY(y)- Single-axis skewingmatrix(a, b, c, d, e, f)- Full transformation matrix
Font Weight Resolution
Converts font weight keywords to numeric values.
| Keyword | Numeric Value |
| -------------------------- | ------------- |
| thin, hairline | 100 |
| ultralight, extralight | 200 |
| light | 300 |
| normal | 400 |
| medium | 500 |
| semibold, demibold | 600 |
| bold | 700 |
| ultrabold, extrabold | 800 |
| heavy, black | 900 |
API Reference
Default Export
stylesheet(container, style)
Resolves styles for a given container.
Parameters:
container- Container dimensions and settingsstyle- Style object or array of style objects
Returns: Resolved style object with expanded shorthands and converted units
Named Exports
flatten(styles)
Flattens an array of style objects into a single merged object.
import { flatten } from '@nutshelllabs/stylesheet';
flatten([{ margin: 10 }, null, { padding: 5 }]);
// => { margin: 10, padding: 5 }transformColor(value)
Transforms RGB/HSL color strings to hexadecimal format.
import { transformColor } from '@nutshelllabs/stylesheet';
transformColor('rgb(255, 128, 0)'); // => '#FF8000'Types
Container
type Container = {
width: number; // Container width in points
height: number; // Container height in points
dpi?: number; // DPI for px unit conversion (default: 72)
remBase?: number; // Base size for rem units (default: 18)
orientation?: 'landscape' | 'portrait';
};Style
The input style object supporting all CSS-like properties:
type Style = {
// Dimensions
width?: number | string;
height?: number | string;
minWidth?: number | string;
maxWidth?: number | string;
minHeight?: number | string;
maxHeight?: number | string;
// Flexbox
flex?: number | string;
flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse';
flexWrap?: 'nowrap' | 'wrap' | 'wrap-reverse';
flexGrow?: number | string;
flexShrink?: number | string;
flexBasis?: number | string;
alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline';
alignSelf?:
| 'auto'
| 'flex-start'
| 'flex-end'
| 'center'
| 'baseline'
| 'stretch';
alignContent?:
| 'flex-start'
| 'flex-end'
| 'center'
| 'stretch'
| 'space-between'
| 'space-around'
| 'space-evenly';
justifyContent?:
| 'flex-start'
| 'flex-end'
| 'center'
| 'space-around'
| 'space-between'
| 'space-evenly';
// Gap
gap?: number | string;
rowGap?: number | string;
columnGap?: number | string;
// Spacing
margin?: number | string;
marginTop?: number | string;
marginRight?: number | string;
marginBottom?: number | string;
marginLeft?: number | string;
marginHorizontal?: number | string;
marginVertical?: number | string;
padding?: number | string;
paddingTop?: number | string;
paddingRight?: number | string;
paddingBottom?: number | string;
paddingLeft?: number | string;
paddingHorizontal?: number | string;
paddingVertical?: number | string;
// Borders
border?: number | string;
borderTop?: number | string;
borderRight?: number | string;
borderBottom?: number | string;
borderLeft?: number | string;
borderWidth?: number | string;
borderStyle?: 'solid' | 'dashed' | 'dotted';
borderColor?: string;
borderRadius?: number | string;
borderTopLeftRadius?: number | string;
borderTopRightRadius?: number | string;
borderBottomRightRadius?: number | string;
borderBottomLeftRadius?: number | string;
// ... and per-side border properties
// Layout
display?: 'flex' | 'none';
position?: 'absolute' | 'relative' | 'static';
top?: number | string;
right?: number | string;
bottom?: number | string;
left?: number | string;
zIndex?: number | string;
overflow?: 'hidden';
aspectRatio?: number | string;
// Colors
backgroundColor?: string;
color?: string;
opacity?: number | string;
// Text
fontFamily?: string | string[];
fontSize?: number | string;
fontStyle?: 'normal' | 'italic' | 'oblique';
fontWeight?: FontWeight;
letterSpacing?: number | string;
lineHeight?: number | string;
textAlign?: 'left' | 'right' | 'center' | 'justify';
textDecoration?:
| 'none'
| 'underline'
| 'line-through'
| 'underline line-through';
textDecorationColor?: string;
textDecorationStyle?: 'solid' | 'dashed' | 'dotted';
textTransform?:
| 'none'
| 'capitalize'
| 'lowercase'
| 'uppercase'
| 'upperfirst';
textOverflow?: 'ellipsis';
maxLines?: number | string;
// Transform
transform?: string | Transform[];
transformOrigin?: number | string;
transformOriginX?: number | string;
transformOriginY?: number | string;
// Object positioning (for images)
objectFit?: string;
objectPosition?: number | string;
objectPositionX?: number | string;
objectPositionY?: number | string;
// SVG
fill?: string;
stroke?: string;
strokeWidth?: string | number;
strokeDasharray?: string;
fillOpacity?: string | number;
strokeOpacity?: string | number;
fillRule?: 'nonzero' | 'evenodd';
textAnchor?: 'start' | 'middle' | 'end';
strokeLinecap?: 'butt' | 'round' | 'square';
strokeLinejoin?: 'miter' | 'round' | 'bevel';
visibility?: 'visible' | 'hidden' | 'collapse';
clipPath?: string;
dominantBaseline?:
| 'auto'
| 'middle'
| 'central'
| 'hanging'
| 'mathematical'
| 'text-after-edge'
| 'text-before-edge';
// Media queries
[key: `@media${string}`]: Style;
};SafeStyle
The resolved output style with normalized values:
type SafeStyle = {
// All shorthand properties expanded
// All units converted to points (numbers)
// All colors normalized
// Transforms parsed into structured arrays
};Transform
type Transform =
| { operation: 'translate'; value: [number, number] }
| { operation: 'rotate'; value: [number] }
| { operation: 'scale'; value: [number, number] }
| { operation: 'skew'; value: [number, number] }
| {
operation: 'matrix';
value: [number, number, number, number, number, number];
};FontWeight
type FontWeight =
| number
| 'thin'
| 'hairline'
| 'ultralight'
| 'extralight'
| 'light'
| 'normal'
| 'medium'
| 'semibold'
| 'demibold'
| 'bold'
| 'ultrabold'
| 'extrabold'
| 'heavy'
| 'black';License
MIT
