{"id":15297183,"url":"https://github.com/ricokahler/flair","last_synced_at":"2025-07-16T03:41:21.967Z","repository":{"id":37029389,"uuid":"241587426","full_name":"ricokahler/flair","owner":"ricokahler","description":"a lean, component-centric style system for React components","archived":false,"fork":false,"pushed_at":"2025-06-20T23:37:31.000Z","size":3750,"stargazers_count":19,"open_issues_count":24,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-21T00:27:43.898Z","etag":null,"topics":["babel-plugin","composable-styles","css","css-in-js","react","reactjs","theming","typescript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ricokahler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-02-19T09:57:33.000Z","updated_at":"2023-08-26T16:39:36.000Z","dependencies_parsed_at":"2023-10-14T23:13:23.230Z","dependency_job_id":"d45675c0-62fd-4712-83ca-badc67f283cc","html_url":"https://github.com/ricokahler/flair","commit_stats":{"total_commits":203,"total_committers":4,"mean_commits":50.75,"dds":"0.40886699507389157","last_synced_commit":"54701d2de653d08d42614649fd015693f81da188"},"previous_names":["ricokahler/react-style-system"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/ricokahler/flair","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Fflair","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Fflair/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Fflair/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Fflair/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ricokahler","download_url":"https://codeload.github.com/ricokahler/flair/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ricokahler%2Fflair/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261044034,"owners_count":23101822,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["babel-plugin","composable-styles","css","css-in-js","react","reactjs","theming","typescript"],"created_at":"2024-09-30T19:15:32.673Z","updated_at":"2025-07-16T03:41:21.906Z","avatar_url":"https://github.com/ricokahler.png","language":"JavaScript","funding_links":[],"categories":["🌐 Web Development - Frontend"],"sub_categories":[],"readme":"# Flair\n\n\u003e a lean, component-centric style system for React components\n\n## ⚠️ This library is still in heavy development with the best features coming soon (development on hiatus)\n\nWatch releases to be notified for new features.\n\n## Features\n\n- 🎣 hooks API\n- 👩‍🎨 theming\n- 🎨 advanced color context features including **dark mode**\n- 🧩 composable styles by default\n- 📦 small size, [7.3kB](https://bundlephobia.com/result?p=flair)\n- 👩‍🎨 full color manipulation library included ([colork2k](https://github.com/ricokahler/color2k)) (no need for chroma-js or polished)\n- ⛓ full TypeScript support and enhanced DX\n\n**Experimental features**\n\nThe best features of this library are still in development:\n\n- static and extracted CSS similar to [Linaria](https://github.com/callstack/linaria) via a [Babel Plugin](https://github.com/ricokahler/flair/tree/master/packages/babel-plugin-plugin) (this will become the preferred way to use the library when stable)\n- SSR support\n- much smaller bundle [2.5kB](https://bundlephobia.com/result?p=@flair/ssr)\n- performance improvements\n\n**Requirements**\n\n- React `\u003e16.8.0` (requires hooks)\n- No IE 11 support\n\n**Table of contents**\n\n- [Flair](#flair)\n  - [Features](#features)\n  - [Why another CSS-in-JS lib?](#why-another-css-in-js-lib)\n  - [Installation](#installation)\n    - [Install](#install)\n    - [Create your theme](#create-your-theme)\n    - [Provider installation](#provider-installation)\n    - [Add type augments](#add-type-augments)\n    - [VS Code extension](#vs-code-extension)\n  - [Usage](#usage)\n    - [Basic usage](#basic-usage)\n    - [Composability](#composability)\n    - [Dynamic coloring](#dynamic-coloring)\n    - [Color system usage](#color-system-usage)\n    - [Theming usage](#theming-usage)\n  - [Implementations](#implementations)\n    - [How does this all work?](#how-does-this-all-work)\n    - [Enabling the experiemental SSR mode (`@flair/ssr`)](#enabling-the-experiemental-ssr-mode-flairssr)\n    - [Configure babel](#configure-babel)\n    - [Configure Webpack](#configure-webpack)\n\n## Why another CSS-in-JS lib?\n\n[Glad you asked! See here for more info.](./why-another-css-in-js-lib.md)\n\n## Installation\n\n### Install\n\n```\nnpm i --save flair\n```\n\n### Create your theme\n\n`flair`'s theming works by providing an object to all your components. This theme object should contain values to keep your app's styles consistent.\n\n[See theming usage for more info](#theming-usage)\n\n```ts\n// /src/theme.ts (or /src/theme.js)\n\nconst theme = {\n  // see theming usage for more info\n  colors: {\n    brand: 'palevioletred',\n    accent: 'peachpuff',\n    surface: 'white',\n  },\n};\n\nexport default theme;\n```\n\n### Provider installation\n\n```tsx\n// index.ts (or index.js)\nimport React from 'react';\nimport { ThemeProvider, ColorContextProvider } from 'flair';\nimport { render } from 'react-dom';\nimport theme from './theme';\nimport App from './App';\n\nconst container = document.querySelector('#root');\n\nrender(\n  \u003cThemeProvider theme={theme}\u003e\n    \u003cColorContextProvider\n      color={theme.colors.accent}\n      surface={theme.colors.surface}\n    \u003e\n      \u003cApp /\u003e\n    \u003c/ColorContextProvider\u003e\n  \u003c/ThemeProvider\u003e,\n  container,\n);\n```\n\n### Add type augments\n\nIf you're using typescript or an editor that supports the typescript language service (VS Code), you'll need to add one more file to configure the types and intellisense.\n\nPlace this file at the root of your project.\n\n```tsx\n// /arguments.d.ts\nimport {\n  StyleFnArgs,\n  ReactComponent,\n  StyleProps,\n  GetComponentProps,\n} from 'flair';\n\ndeclare module 'flair' {\n  // this should import your theme\n  type Theme = typeof import('./src/theme').default;\n\n  // provides an override type that includes the type for your theme\n  export function useTheme(): Theme;\n\n  // provides an override type that includes the type for your theme\n  export function createStyles\u003cStyles, ComponentType extends ReactComponent\u003e(\n    stylesFn: (args: StyleFnArgs\u003cTheme\u003e) =\u003e Styles,\n  ): \u003cProps extends StyleProps\u003cStyles\u003e\u003e(\n    props: Props,\n    component?: ComponentType,\n  ) =\u003e {\n    Root: React.ComponentType\u003cGetComponentProps\u003cComponentType\u003e\u003e;\n    styles: { [P in keyof Styles]: string } \u0026 {\n      cssVariableObject: { [key: string]: string };\n    };\n  } \u0026 Omit\u003cProps, keyof StyleProps\u003cany\u003e\u003e;\n}\n```\n\n### VS Code extension\n\nIf you're using VSCode, we recommend installing the `vscode-styled-components` by [the styled-components team](https://github.com/styled-components/vscode-styled-components). This will add syntax highlighting for our style of CSS-in-JS.\n\n## Usage\n\n### Basic usage\n\n```tsx\n// Card.tsx\nimport React from 'react';\nimport { createStyles, PropsFromStyles } from 'flair';\n\n// `flair` works by creating a hook that intercepts your props\nconst useStyles = createStyles(({ css, theme }) =\u003e ({\n  // here you return an object of styles\n  root: css`\n    padding: 1rem;\n    background-color: peachpuff;\n    /* you can pull in your theme like so */\n    border-right: 5px solid ${theme.colors.brand};\n  `,\n  title: css`\n    font-weight: bold;\n    font-weight: 3rem;\n    margin-bottom: 1rem;\n  `,\n  description: css`\n    line-height: 1.5;\n  `,\n}));\n\n// write your props like normal, just add the `extends…` like so:\ninterface Props extends PropsFromStyles\u003ctypeof useStyles\u003e {\n  title: React.ReactNode;\n  description: React.ReactNode;\n}\n\nfunction Card(props: Props) {\n  // `useStyles` intercepts your props\n  const {\n    // `Root` and `styles` are props added via `useStyles`\n    Root,\n    styles,\n    // `title` and `description` are the props you defined\n    title,\n    description,\n  } = useStyles(props, 'div' /* 👈 `div` is the default if you omit this */);\n\n  return (\n    // the `root` class is automatically applied to the `Root` component\n    \u003cRoot\n      onClick={() =\u003e {\n        // you can supply any props you would send to the root component\n        // (which is a `div` in this case)\n      }}\n    \u003e\n      {/* the styles that come back are class names */}\n      \u003ch2 className={styles.title}\u003e{title}\u003c/h2\u003e\n      \u003cp className={styles.description}\u003e{description}\u003c/p\u003e\n    \u003c/Root\u003e\n  );\n}\n\nexport default Card;\n```\n\n### Composability\n\n`flair`'s styles are composable by default. This means that every style you write can be augmented because the style props `className`, `style`, and `styles` are automatically propagated to the subject `Root` component.\n\nBuilding from the example above:\n\n```tsx\n// Grid.tsx\nimport React from 'react';\nimport { createStyles, PropsFromStyles } from 'flair';\nimport Cart from './Card';\n\nconst useStyles = createStyles(({ css }) =\u003e ({\n  root: css`\n    display: grid;\n    gap: 1rem;\n    grid-template-columns: repeat(3, 1fr);\n  `,\n  card: css`\n    box-shadow: 0 0 45px 0 rgba(0, 0, 0, 0.2);\n  `,\n  titleUnderlined: css`\n    text-decoration: underlined;\n  `,\n}));\n\ninterface Props extends PropsFromStyles\u003ctypeof useStyles\u003e {}\n\nfunction Grid(props: Props) {\n  const { Root, styles } = useStyles(props);\n\n  return (\n    \u003cRoot\u003e\n      \u003cCard\n        // augments the `root` class in the Card\n        className={styles.card}\n        styles={{\n          // augments the `title` class in `Card`\n          title: styles.titleUnderlined,\n        }}\n        title=\"flair\"\n        description={\n          \u003c\u003ea lean, component-centric style system for React components\u003c/\u003e\n        }\n      /\u003e\n\n      \u003cCard\n        className={styles.card}\n        title=\"emotion\"\n        description={\n          \u003c\u003eCSS-in-JS library designed for high performance style composition\u003c/\u003e\n        }\n      /\u003e\n\n      \u003cCard\n        className={styles.card}\n        title=\"styled-components\"\n        description={\n          \u003c\u003e\n            Visual primitives for the component age. Use the best bits of ES6\n            and CSS to style your apps without stress\n          \u003c/\u003e\n        }\n      /\u003e\n    \u003c/Root\u003e\n  );\n}\n```\n\n### Dynamic coloring\n\nEvery component styled with `flair` supports dynamic coloring. This means you can pass the prop `color` to it and use that color when defining styles.\n\n```tsx\n// passing the color prop\n\u003cButton color=\"red\"\u003eMy Red Button\u003c/Button\u003e\n```\n\n```tsx\n// using the color prop to define styles\nimport React from 'react';\nimport { createStyles, PropsFromStyles } from 'flair';\n\n// the `color` prop comes through here  👇\nconst useStyles = createStyles(({ css, color, surface }) =\u003e ({\n  //                                           👆\n  // additionally, there is another prop `surface` that hold the color of the\n  // surface this component is on currently. this is usually black for dark mode\n  // and white for non-dark modes\n  root: css`\n    border: 1px solid ${color.decorative};\n    background-color: ${surface};\n    color: ${color.readable};\n  `,\n}));\n\ninterface Props extends PropsFromStyles\u003ctypeof useStyles\u003e {\n  children: React.ReactNode;\n  onClick: () =\u003e void;\n}\n\nfunction Button(props: Props) {\n  const { Root, children, onClick } = useStyles(props, 'children');\n  return \u003cRoot onClick={onClick}\u003e{children}\u003c/Root\u003e;\n}\n\nexport default Button;\n```\n\n[See this demo in CodeSandbox](https://codesandbox.io/s/dynamic-coloring-7dr3n)\n\n### Color system usage\n\n`flair` ships with a simple yet robust color system. You can wrap your components in `ColorContextProvider`s to give your components context for what color they should expect to be on top of. This works well when supporting dark mode.\n\n[See here for a full demo of color context.](https://codesandbox.io/s/nested-color-system-demo-qphro)\n\n### Theming usage\n\nTheming in `flair` is implemented as one object that will be available to all your components in the app. You can use this object to store values to make your app's styles consistent. We recommend referring to [`material-ui`'s theme object](https://material-ui.com/customization/default-theme/#default-theme) for idea on how to define your own theme's shape.\n\nWrap your App in a `ThemeProvider` and give that `ThemeProvider` a theme object.\n\nAfter your wrap in a theme provider, you can access the theme via the args in `createStyles`:\n\n```tsx\n//                                     👇👇👇\nconst useStyles = createStyles(({ css, theme }) =\u003e ({\n  root: css`\n    color: ${theme.colors.brand};\n  `,\n}));\n```\n\nAnd inside your component. You can access the theme via `useTheme()`\n\n```tsx\nfunction Component(props: Props) {\n  const theme = useTheme();\n\n  // ...\n}\n```\n\n## Implementations\n\nThis repo has two implementations that are better suited for different environments/setups.\n\nBoth implementations share the exact same API and even use the same import (the SSR version rewrites the imports via the babel plugin).\n\nIn general, the standalone implementation is easier to get started with, works in more environments, and is currently much more stable than the SSR counterpart.\n\nWith the existence of both versions, you can get started using the standalone version and optimize later with the SSR version.\n\n\u003c!-- prettier-ignore-start --\u003e\n| Feature | `@flair/standalone` | `@flair/ssr` |\n|--|--|--|\n| Works standalone without any babel plugins or webpack loaders (for `create-react-app` support) | ✅ | 🔴 |\n| Zero config | ✅ | 🔴 |\n| Faster, static CSS 🚀 | 🔴 | ✅ |\n| Extracts CSS from JS bundle | 🔴 | ✅ |\n| Stability | 👍 beta | 🤔 experimental |\n| Bundle size | [7.3kB](https://bundlephobia.com/result?p=@flair/standalone) 🤷‍♀️ | [2.5kB](https://bundlephobia.com/result?p=@flair/ssr) 😎 |\n| [Theming](#theming-usage) | ✅ | ✅ |\n| [Dynamic coloring](#dynamic-coloring) | ✅ | ✅ |\n| Same lean API | 😎 | 😎 |\n\u003c!-- prettier-ignore-end --\u003e\n\n### How does this all work?\n\n[See the architecture docs for more info.](./architecture.md)\n\n### Enabling the experimental SSR mode (`@flair/ssr`)\n\n\u003e ⚠️ In order to get this to work, you need to be able to freely configure babel and webpack. This is currently _not_ possible with `create-react-app`.\n\n### Configure babel\n\nCreate or modify your `.babelrc` configuration file at the root of your folder.\n\n```js\n{\n  \"presets\": [\n    // ...rest of your presets\n  ],\n  \"plugins\": [\n    // ...rest of your plugins\n    [\n      \"@flair/plugin\",\n      {\n        // this is the theme file. refer to here:\n        // https://github.com/ricokahler/flair#create-your-theme\n        \"themePath\": \"./src/styles/theme.js\"\n      }\n    ]\n  ]\n}\n```\n\n\u003e **Note:** You do _not_ need to change your imports. The babel plugin `@flair/plugin` will re-write your imports to use the `@flair/ssr` package\n\n### Configure Webpack\n\nIn your webpack config, create a new rule for `.rss-css` files and include the `@flair/loader` in the chain.\n\n```js\nmodule.exports = {\n  // ...\n  module: {\n    // ...\n    rules: [\n      // ...\n      {\n        test: /\\.rss-css$/,\n        use: [\n          'style-loader', // you can use the mini-css-extract-plugin instead too\n          {\n            loader: 'css-loader',\n            options: { importLoaders: 2 },\n          },\n          // flair loader must be last\n          '@flair/loader',\n        ],\n        include: [\n          require.resolve('@flair/loader/load.rss-css'),\n          // ...\n        ],\n      },\n    ],\n  },\n};\n```\n\n### Credits\n\nBig thanks to [`@lepture`](https://twitter.com/lepture) for the name `flair` ❤️\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricokahler%2Fflair","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fricokahler%2Fflair","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fricokahler%2Fflair/lists"}