A CSS-in-JS library for UI package authors
Flairup is a CSS-in-JS library specifically designed for UI package authors. Unlike general CSS-in-JS solutions, Flairup focuses on solving the unique challenges of distributing reusable UI components with their styles.
When creating third-party packages, you face different challenges than when building applications. Most existing styling solutions are designed for applications, not for packages meant to be shared and consumed by others.
Battle-tested on Emoji-Picker-React, Flairup makes it easy to ship styles with your components while ensuring they work reliably in any environment.
At the heart of FlairUp is the StyleSheet object, which serves as a singleton for your entire package. This means that all styles across your library share the same stylesheet instance, allowing for efficient style deduplication and management.
The StyleSheet is created once using the `createSheet` function and can be used throughout your package. All styles defined using this stylesheet will be automatically deduplicated, ensuring optimal performance and minimal CSS output.
1import { createSheet } from 'flairup';23// Create a stylesheet for your package4const stylesheet = createSheet('MyPackageName');56// Use the stylesheet to create styles7const styles = stylesheet.create({8 button: {9 color: 'red',10 ':hover': {11 color: 'blue',12 },13 },14});
FlairUp optimizes performance by generating a single class for each unique CSS property value. This means that if the same style is used in multiple places, it will only be added to the stylesheet once.
For example, if you use the color 'red' in multiple components, FlairUp will create a single class for it and reuse it across all instances, reducing the overall CSS bundle size.
FlairUp works by injecting a single <style> tag into the DOM. This tag contains all the styles for your package, and it is automatically managed by FlairUp.
During server-side rendering, FlairUp ensures that the styles are properly injected into the HTML, and on the client side, it manages the style tag to prevent duplicate injections.
Easy to use with a familiar CSS-like syntax
Full TypeScript support for better development experience
Automatic style scoping to prevent conflicts
Built-in support for server-side rendering
1npm install flairup2# or3yarn add flairup
The most basic usage of FlairUp, demonstrating how to create and apply styles to a component. This example shows a simple button with hover effects using the `create` function to define styles and the `cx` function to apply them.
1const styles = sheet.create({2 "button": {3 "color": "blue",4 "backgroundColor": "white",5 "padding": "10px 20px",6 "border": "1px solid blue",7 "borderRadius": "5px",8 "cursor": "pointer",9 ":hover": {10 "backgroundColor": "lightblue",11 "borderColor": "darkblue"12 }13 }14});
1function MyButton() {2 return (3 <button className={cx(styles.button)}>4 Hover me!5 </button>6 );7}
Demonstrates FlairUp's ability to manage multiple, scoped styles within a single stylesheet. This example showcases a button group with different visual variants (default, primary, and danger), all defined within the same `styles` object. The `create` function is used to define these styles, and the `cx` function facilitates their application, allowing for straightforward composition of different style scopes to a single element.
1const styles = sheet.create({2 "button": {3 "backgroundColor": "#3498db",4 "color": "white",5 "padding": "10px 20px",6 "borderRadius": "5px",7 "border": "none",8 "cursor": "pointer",9 "&:hover": {10 "backgroundColor": "#2980b9"11 }12 },13 "primary": {14 "backgroundColor": "#2ecc71",15 "&:hover": {16 "backgroundColor": "#27ae60"17 }18 },19 "danger": {20 "backgroundColor": "#e74c3c",21 "&:hover": {22 "backgroundColor": "#c0392b"23 }24 }25});
1function ButtonGroup() {2 return (3 <div className={cx(styles.buttonGroup)}>4 <button className={cx(styles.button)}>Default</button>5 <button className={cx(styles.button, styles.primary)}>Primary</button>6 <button className={cx(styles.button, styles.danger)}>Danger</button>7 </div>8 );9}
Shows how to use CSS variables in FlairUp. Unlike regular CSS properties, CSS variables are added as a single class per scope. This example demonstrates how to define and use CSS variables to create themeable components with different color variants.
1const styles = sheet.create({2 "box": {3 "--box-bg-color": "lightgreen",4 "backgroundColor": "var(--box-bg-color)",5 "padding": "15px",6 "borderRadius": "8px",7 "margin": "10px 0"8 },9 "box--primary": {10 "--box-bg-color": "lightblue"11 },12 "box--secondary": {13 "--box-bg-color": "lightcoral"14 },15 "button": {16 "--": {17 "--button-bg": "#3498db",18 "--button-hover-bg": "#2980b9",19 "--button-text": "white",20 "--button-padding": "10px 20px",21 "--button-radius": "4px"22 },23 "backgroundColor": "var(--button-bg)",24 "color": "var(--button-text)",25 "padding": "var(--button-padding)",26 "borderRadius": "var(--button-radius)",27 "border": "none",28 "cursor": "pointer",29 "transition": "background-color 0.3s ease",30 ":hover": {31 "backgroundColor": "var(--button-hover-bg)"32 }33 },34 "button--danger": {35 "--": {36 "--button-bg": "#e74c3c",37 "--button-hover-bg": "#c0392b"38 }39 },40 "button--success": {41 "--": {42 "--button-bg": "#2ecc71",43 "--button-hover-bg": "#27ae60"44 }45 }46});
1function Boxes() {2 return (3 <>4 <div className={cx(styles.box)}>Default Box</div>5 <div className={cx(styles.box, styles['box--primary'])}>Primary Box</div>6 <div className={cx(styles.box, styles['box--secondary'])}>Secondary Box</div>7 </>8 );9}
Demonstrates how to use media queries in FlairUp. This example shows a responsive box that changes its background color and padding based on the viewport width. The media query is defined directly in the style object using the `@media` syntax.
1const styles = sheet.create({2 "box": {3 "backgroundColor": "lightblue",4 "padding": "20px",5 "@media (max-width: 600px)": {6 "backgroundColor": "lightcoral",7 "padding": "10px"8 }9 }10});
1function ResponsiveBox() {2 return (3 <div className={cx(styles.box)}>4 Resize the window to see the effect5 </div>6 );7}
Demonstrates the use of pseudo-selectors and pseudo-elements in FlairUp. This example shows a button with hover, active, and focus states, as well as before and after pseudo-elements. The styles are defined using the standard CSS pseudo-selector syntax within the style object.
1const styles = sheet.create({2 "button": {3 "backgroundColor": "#f1c40f",4 "color": "white",5 "padding": "10px 20px",6 "borderRadius": "5px",7 "border": "none",8 "cursor": "pointer",9 "position": "relative",10 "transition": "all 0.3s ease",11 ":hover": {12 "backgroundColor": "#f39c12",13 "transform": "translateY(-2px)"14 },15 ":active": {16 "transform": "translateY(0)"17 },18 ":focus": {19 "outline": "none",20 "boxShadow": "0 0 0 3px rgba(241, 196, 15, 0.4)"21 },22 "::before": {23 "content": "🎩",24 "position": "absolute",25 "left": "10px",26 "top": "50%",27 "transform": "translateY(-50%)"28 },29 "::after": {30 "content": "→",31 "position": "absolute",32 "right": "10px",33 "top": "50%",34 "transform": "translateY(-50%)"35 }36 }37});
1function FancyButton() {2 return (3 <button className={cx(styles.button)}>4 Hover me!5 </button>6 );7}
1const styles = sheet.create({2 ".theme-dark": {3 "button": {4 "backgroundColor": "#3498db",5 "color": "white",6 "&:hover": {7 "backgroundColor": "#2980b9"8 }9 }10 },11 ".theme-light": {12 "button": {13 "backgroundColor": "#2ecc71",14 "color": "white",15 "&:hover": {16 "backgroundColor": "#27ae60"17 }18 }19 }20});
1function ThemeButtons() {2 return (3 <div className={cx(styles.themeContainer)}>4 <div className={cx(styles.themeBox, styles.themeDark, 'theme-dark')}>5 <button className={cx(styles.button)}>Dark Theme Button</button>6 </div>7 <div className={cx(styles.themeBox, styles.themeLight, 'theme-light')}>8 <button className={cx(styles.button)}>Light Theme Button</button>9 </div>10 </div>11 );12}
Demonstrates how to create and use keyframe animations in FlairUp. This example shows two animated boxes: one using a bounce animation and another using a pulse animation. The keyframes are defined using the `keyframes` function and applied to elements using the `animation` property.
1// First, define your keyframes2const keyframes = stylesheet.keyframes({3 bounce: {4 '0%': { transform: 'translateY(0)' },5 '50%': { transform: 'translateY(-20px)' },6 '100%': { transform: 'translateY(0)' },7 },8 pulse: {9 '0%': { transform: 'scale(1)' },10 '50%': { transform: 'scale(1.2)' },11 '100%': { transform: 'scale(1)' },12 },13});1415// Then use them in your styles with template strings16const styles = stylesheet.create({17 box: {18 width: '50px',19 height: '50px',20 backgroundColor: '#3498db',21 margin: '10px',22 display: 'flex',23 justifyContent: 'center',24 alignItems: 'center',25 fontSize: '30px',26 lineHeight: '1',27 borderRadius: '10px',28 },29 bouncingBox: {30 animation: `${keyframes.bounce} 1s infinite`,31 },32 pulsingBox: {33 animation: `${keyframes.pulse} 1s infinite`,34 },35});
1// Finally, apply the styles2function AnimatedBoxes() {3 return (4 <div className={cx(styles.container)}>5 <div className={cx(styles.box, styles.bouncingBox)}>🎩</div>6 <div className={cx(styles.box, styles.pulsingBox)}>🎩</div>7 </div>8 );9}