Giter VIP home page Giter VIP logo

fela's Introduction

Fela

Fela is a small, high-performant and framework-agnostic toolbelt to handle state-driven styling in JavaScript.
It is dynamic by design and renders your styles depending on your application state.

It generates atomic CSS and supports all common CSS features such as media queries, pseudo classes, keyframes and font-faces. Fela ships with a powerful plugin API adding e.g. vendor prefixing or fallback value support.

Fela can be used with React or with any other view library. It even supports React Native.

Bundlephobia npm downloads Spectrum

Support Us

Support Robin Weser's work on Fela and its ecosystem directly via GitHub Sponsors.

Benefits

  • Predictable Styling
  • Scoped Atomic CSS
  • Minimal Bundle Size
  • No Specificity Issues
  • No Naming Conflicts
  • Framework-Agnostic
  • Huge Ecosystem
  • RTL Support

Read more about the benefits!

The Gist

Fela's core principle is to consider style as a function of state.
The whole API and all plugins and bindings are built on that idea.
It is reactive and auto-updates once registered to the DOM.

The following example illustrates the key parts of Fela though it only shows the very basics.

import { createRenderer } from 'fela'

// a simple style rule is a pure function of state
// that returns an object of style declarations
const rule = (state) => ({
  textAlign: 'center',
  padding: '5px 10px',
  // directly use the state to compute style values
  fontSize: state.fontSize + 'pt',
  borderRadius: 5,
  // deeply nest media queries and pseudo classes
  ':hover': {
    fontSize: state.fontSize + 2 + 'pt',
    boxShadow: '0 0 2px rgb(70, 70, 70)',
  },
})

const renderer = createRenderer()

// fela generates atomic CSS classes in order to achieve
// maximal style reuse and minimal CSS output
const className = renderer.renderRule(rule, {
  fontSize: 14,
}) // =>  a b c d e f

The generated CSS output would look like this:

.a { text-align: center }
.b { padding: 5px 10px }
.c { font-size: 14pt }
.d { border-radius: 5px }
.e:hover { font-size: 16pt }
.f:hover { box-shadow: 0 0 2px rgb(70, 70, 70) }

Primitive Components

If you're using Fela, you're most likely also using React.
Using the React bindings, you get powerful APIs to create primitive components.

Read: Usage with React for a full guide.

import * as React from 'react'
import { useFela } from 'react-fela'

const rule = ({ fontSize }) => ({
  textAlign: 'center',
  padding: '5px 10px',
  // directly use the props to compute style values
  fontSize: fontSize + 'pt',
  borderRadius: 5,
  ':hover': {
    fontSize: fontSize + 2 + 'pt',
    boxShadow: '0 0 2px rgb(70, 70, 70)',
  },
})

function Button({ fontSize, children }) {
  const { css } = useFela({ fontSize })

  return <button className={css(rule)}>{children}</button>
}

Check this example on CodeSandbox

Examples

Documentation

Workshop

If you are coming from CSS and want to learn JavaScript Styling with Fela, there is a full-feature fela-workshop which demonstrates typical Fela use cases. It teaches all important parts, step by step with simple examples. If you already know other CSS in JS solutions and are just switching to Fela, you might not need to do the whole workshop, but it still provides useful information to get started quickly.

Talks

Posts

Ecosystem

There are tons of useful packages maintained within this repository including plugins, enhancers, bindings and tools that can be used together with Fela. Check the Ecosystem documentation for a quick overview.

Community

Apart from all the packages managed within this repository, there are many community third-party projects that are worth mentioning:

Support

Got a question? Come and join us on Github Discussion!
We'd love to help out. We also highly appreciate any feedback.
Don't want to miss any update? Follow us on Twitter.

Who's using Fela?

Check all the logos on the website.

Want to add yours? Please create a new issue with your logo attached and we will add it!

Contributing

This project exists thanks to all the people who contribute.

We highly appreciate any contribution.
For more information follow the contribution guide.
Also, please read our code of conduct.

License

Fela is licensed under the MIT License.
Documentation is licensed under Creative Commons License.
Created with ♥ by @robinweser and all the great contributors.

fela's People

Contributors

adderpositive avatar aga5tya avatar alizeait avatar asadm avatar danielstocks avatar dependabot[bot] avatar derek-duncan avatar dhurlburtusa avatar dustin-h avatar fongandrew avatar johanneslumpe avatar kittygiraudel avatar kokjinsam avatar layershifter avatar levithomason avatar milesj avatar msmith9393 avatar rafalfilipek avatar reinhard avatar robinweser avatar sgrishchenko avatar steida avatar tajo avatar theultdev avatar tiagojsalmeida avatar troch avatar txhawks avatar wagerfield avatar wcastand avatar xdamman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fela's Issues

Reuse markup from server rendering in client

Actually when rendering fela styles on the server, the new created fela renderer overwrites the styles which are already in DOM.

This causes the browser to rerender all styles which leads to a slight flash of the hole window, because the styles disappear and appear again.
This is quiet annoying.

So in my opinion a solution would be to create two methods in renderer which can export/import the renderer state.

Cheers
Dustin

Remove invalid properties

We should remove invalid properties such as dynamic values that do not get a value passed and therefore remain undefined. This would help to only produce valid CSS markup and to reduce the markup size.

Fonts are reloading with each styles update

When using custom fonts, Chrome tries to reload them each time the mountNode gets updated.

This happens for example when using a custom font in a React Component A. When another component gets mounted with new styles, the renderer triggers an update of the mountNode. This replaces all css and forces the browser to refetch the fonts and rerender all text on the website. The user can see this as slight flash of the hole screen (when some Text is on the page).

Maybe fonts should get rendered in a different way.

Remove style clustering and dynamic style extraction

Right now styles get clustered into a new object to be able to render them more easily. In order to improve performance we should remove that step completely and directly iterate and render the styles recursively.
Within this progress we should also get rid of the extra style extraction and do that on the fly.

Rendering global styles


Description

Most likely every application uses some global styles e.g. CSS resets. Also sometimes you need to render legacy or third-party CSS.

Proposal

Allow strings and objects passed to a Renderer.

Code Example:

renderer.render('*{margin:0;padding:0}')
// or with ECMAScript 2015 template string
renderer.render(`
* {
  margin: 0;
  padding: 0
}`)

// object notation for multiple selectors and more readability
renderer.render({
  '*': {
    margin: 0,
    padding: 0
  },
  '#someid': {
    fontSize: '12px'
  },
  'div': {
    display: 'flex'
  }
})

Plugins

Plugins should be allowed (and used) too, but the styles should not be validated so that it comes with the users risk actually.

Functional Selector

Using the Selector class for complex selectors including media styles is quite comfortable, but often one would just need simple style selectors without media settings at all.

Actually just a function of props that returns an object of styles would be enough - making the composer a valid functional Selector itself.
This method would also completely remove the need for the basic Fela package itself as long as one is only using functional Selectors.

API proposal

const selector = props => ({
   color: props.color,
   font-size: '12px'
})

const renderer = new FelaDOM.Renderer(node)
renderer.render(selector, { color: 'blue' })

Changes

  • DOMRenderer must accept functional Selectors
  • enhanceWithPlugins should also enhance functional Selectors

[Benchmark] DOM rendering performance

In order to get the best (DOM) rendering performance possible, we should generate some basic benchmark to test different methods of adding a new ruleset to a CSSStyleSheet.

Methods

  • node.sheet.insertRule(rule)
  • node.textContent = updatedCSS
  • node.textContent += rule

Is there a JS notation for a global style renderer?

Sorry this is just a question! I'm looking forward to working with Fela, but was wondering how to handle the addCSS situation I with React-Look (or Radium) where I have an object that looks like:

import theme from 'theme';

const background = {
  width: '100%',
  height: '100%',
  margin: 0,
  backgroundColor: theme.greyScale5,
};

const global = {
  html: {
    ...background,
    fontSize: '10px',
  },
  '#root': background,
  '#app': background,
  body: {
    ...background,
    fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
    fontSize: '14px',
  },
  '*': {
    boxSizing: 'border-box !important',
    outline: 'none !important',
  },
  a: {
    color: theme.primary,
    cursor: 'pointer',
  },
  h2: {
    textTransform: 'capitalize',
    fontWeight: 300,
    margin: '0.5rem 0',
  },
};

export default global;

And I want to render that into a global stylesheet. Is this no longer supported, or do I need to write this as a string of css (where I would like the theme values?

Thanks!

I promise to help on this project as soon as this Angular -> React migration is done!

[Plugin] Adding units to values

A plugin that automatically adds units to values if needed. It must consider unitless properties and should also add units to strings like 34.

Improve documentation

Version: 1.0.0
Environment: DOM / Server
Type: Docs


Description

Using Gitbook we were able to launch some initial documentation pages, but there are still a lot missing. Also there are some more topics we'd like to include in the near future.

ToDo

Introduction
  • 1.1. Motivation
Bascics
  • 2.4. Renderer
Advanced
  • 3.2. Enhancers
Recipes
  • 4.1. Configuring your Renderer
  • 4.2. Server Rendering
API Reference
  • 5.6. enhance

Umbrella 1.0

This is a list of things that need to be done before releasing version 1.0 (first stable).
Fela actually is quite stable and produces correct, minimal and compressed markup. It is just very young (2 weeks~) and therefore not very matured.

(The list might be updated next days)

  • just test Fela in a real world application (maybe by replacing react-look)
  • complete docs ( #35 )
  • better/more examples ( #28 )

Rehydration

Environment: Both
Type: Feature

Proposal

Right now there is no way to recalculate all the rule-props pairs as they get resolved and compressed once. After compression there is no way to conclude previously used props at all.

Code Example:

const renderer = createRenderer()

let someVar = 'red'

const rule = props => ({
  color: props.color,
  backgroundColor: someVar
})

renderer.renderRule(rule, { fontSize: '15px' }) // => 'c0 c0-foo'
// .c0 { background-color: red }

someVar = 'blue'

renderer.rehydrate()
// .c0 { background-color: blue }

Plugin Meta-Data

To create advanced plugins it would be useful to have additional meta data available such as used props, type (keyframe, font, rule, static), rule/keyframe function, etc.

fela-native

To be able to use Fela with React Native we need to improve the infrastructure.

  • Native Renderer
    • Leverage React Native's StyleSheet.create for caching to reduce JS object bridging which is inperformant ( rofrischmann/react-look#273 )
  • Utilize shared Renderer methods
    • generatePropsReference(props)
    • processStyle(style, meta, plugins)
    • diffStyle(style, base)
  • React Native example
  • Documentation

Server-side rendering

Next to the DOMRenderer, Fela should also ship a ServerRender which allows to render into a string directly. While this can actually be achieve using a small HTMLElement mock, we should have a more convenient way to handle this.

API proposal

const selector = new Selector(props => {
  color: props.color,
  fontSize: '12px'
})

const renderer = new FelaServer.Renderer()

// do some render calls
renderer.render(selector, { color: 'blue' })
renderer.render(selecotr, { color: 'red' })

// get the final CSS string
renderer.renderToString()

The best way to do both would be to pass a renderer into a application which does several .render() calls. As the API is exactly the same there should not be any problem doing this. Afterwards one could call .renderToString() to get the CSS output.

Document useful tools & utilites

To even provide a better ecosystem, next to plugins we should also add some information on third-party tools and utilites useful for Fela.

[Plugin] Extending styles

Environment: Both
Type: Feature


Description

Would be great to have a plugin which allows (conditional) style object extension.

Proposal

We could use the extend key to specify styles used to extend.

Code Example:

// basic
const baseRules = { backgroundColor: 'red' }
const rule = props => ({
  color: 'blue',
  // might support multiple objects using arrays
  extend: baseRules
})

// conditional
const rule = props => ({
  color: 'blue',
  // could also support multiple using arrays
  extend: {
    condition: props.bg === true,
    style: {
     backgroundColor: 'red' 
   }
  }
})

[Plugin] Style Validation

As we removed the forced style validation from Fela's core, it would be great to have a plugin that does this job. It should also be able to log and fix (delete) invalid styles.

Roadmap - 2016

The following list shows some ideas that we will investigate later this year. It is not said that these get implemented at all, but they for sure at least get considered.

  • fela-native: Fela for React Native ( #68 )
  • Linting/Validation: Evolving elodin to be able to fully use style object linting

Note: It's not about linting indentation and stuff, but rather about coding style and enforced best practices.

  • Type Validation

Clean-up & Extract utilities from StyleSheet

A lot of methods within StyleSheet are not actually directly bound to it and should be extracted into single utility files. Would also help fo clean up the tests and to improve readability.

[Plugin] Fallback value resolver

In order to get compatible CSS markup, we need to resolve arrays with fallback values into a single string. e.g. display: ['-webkit-flex', 'flex'] should become display:-webkit-flex;display:flex.

Does not render multiple static styles for a single selector

Version: 1.0.1
Environment: Both
Type: Bug


Description

Please try to add as much detail as possible.

Steps to reproduce

const renderer = createRenderer()

renderer.renderStatic({ color: 'red' }, 'div')
renderer.renderStatic({ fontSize: '12px' }, 'div')

Expected Behavior

Expected CSS output:

div {
  color: red
}

div {
  font-size: 12px
}

Actual Behavior

Actual CSS output:

div {
  color: red
}

Proposal

Not only use the selector as a cache reference, but the whole stringified style.

Theming

There should be an easy way to do theming.

We could use an enhancer to add a theme as second parameter of each rendering method.
Yet one could also theme using the props or a simple Theme singleton as well.

Proposal

Theming Enhancer

const theme = { 
  PRIMARY: 'red', 
  SECONDARY: 'blue' 
}

const renderer = createRenderer({
  enhancers: [ theming(theme) ]
})

const rule = (props, theme) => ({
  fontSize: props.fontSize,
  color: props.color || theme.PRIMARY,
  backgroundColor: theme.SECONDARY
})

renderer.renderRule(rule, { fontSize: '15px' }) // => 'c0 c0-foo'
renderer.renderRule(rule, { fontSize: '20px', color: 'green' }) // => 'c0 c0-bar'
.c0 {
  color: red;
  background-color: blue
}

.c0-foo {
  font-size: 15px
}

.c0-bar {
  font-size: 20px;
  color: green;
}

Base Styles

const theme = { 
  PRIMARY: 'red', 
  SECONDARY: 'blue' ,
  styles: {
    button: { 
      color: 'blue', 
      lineHeight: '20px', 
      padding: '10px' 
    }
  }
}

const rule = (props, theme) => ({
  ...theme.styles.button,
  fontSize: props.fontSize,
  backgroundColor: theme.SECONDARY
})

Consideration: How to style child elements?

As https://medium.com/@taion/the-problem-with-css-in-js-circa-mid-2016-14060e69bf68#.wv57un3dw describes there are several problems with not using selectors sometimes.
Actually we could solve this using Fela's props or using sth. like :nth-child, but we might want to consider adding a special plugin or some default functionality to style child elements e.g. .btn > .child as well as .btn .child.

Perhaps its enough to allow the > selector or use an explicit & before adding selectors.

const rule = props => ({
  color: 'blue',
  ':hover': {
    color: 'red'
  }
  '& > button': {
    color: 'red',
    ':last-child': {
      paddingRight: 10
    }
  }
})

[Plugin] Vendor prefixer

A common use case for a plugin is to add vendor prefixes. We should use inline-style-prefix-all for now and perhaps later add support for inline-style-prefixer as well.

[Plugin] Conditional style extension

A custom property that merges some styles based on a condition. It would be defined with an expression string such as:

const selector = props => {
  color: 'red',
  fontSize: '14px',
  'isActive=true': {
    color: 'blue',
    backgroundColor: 'red'
  }
}

render(selector, { isActive: true })  // => {font-size:14px;color:blue;background-color:red}
render(selector) // => {color:red;font-size:14px}

See https://github.com/rofrischmann/react-look/blob/develop/packages/react-look-core/modules/mixins/condition.js for an example implementation

Consider using objects in favor of Map/Set

Type: General


Description

We should consider replacing all ECMAScript 2015 Set/Map with plain objects. Map and Set are both great new features, but yet not supported by every major browser (especially older versions which will logically never get native support).

Pro
  • Less overhead as you would need to add polyfills for both
  • Less pain setting up the environment
  • Browser compatibility by default
Contra
  • Might negatively affect performance? (Research)
  • Reduces code semantics / (readiblity)

Angular 2 - Docs + Examples

Description

I really like your library and would like to contribute Angular 2 usage Docs and source-code examples.

So my questions:

  • Do you want me to wait until Angular 2 is fully released or is it fine doing it with a release candidate?
  • Is it sufficient to provide examples for JavaScript or do you also want examples for DART and Typescript? (Angular2 comes in three flavors)

I would provide you a pull request as soon as I am done. I would try to do a similar app like the current react example and provide a similar doc.

Do you have any other hints or wishes?

cheers,
Bernhard

Simplify media style definition


Description

We should somehow simplify how media styles are defined. The MediaSelector is kind of verbose and unhandy to read at all. We should include them into into the selector itself. This would also remove the need for MediaSelectors at all.

Proposal

Nesting media queries inside selectors.

Code Example:

const selector = props => ({
  color: 'red',
  '@media (min-height: 300px)': {
    color: 'blue'
  }
})

Downsides

It would introduce additional complexity. We would need to split the styles into media parts and afterwards split them into pseudo parts as well.

Prevent ever-growing stylesheet

At the moment our stylesheet will keep growing the more selectors we render. We should investigate if this becomes an issue and if so, how we can prevent this and maybe update selectors in-place instead of just adding new ones.

More examples

We should provide more examples. Especially at least one non-React example.

  • React
  • Vanilla JS
  • Routing

[Plugin] Custom property

A plugin that allows the use of custom properties (often refered to as mixins) e.g.

const plugin = customProperty({
  size: size => ({
    width: size,
    height: size
  })
})

const styles = {
  size: '25px'
}

plugin(styles) // => { width: 25px, height: 25px }

Use insertRule for production

Version: 1.0.0
Environment: DOM / Server
Type: General


Description

As pointed out in #3 we should prefer using CSSOM's insertRule instead of mutating textContent. The performance difference can be huge.

Why only for production?

@nathanmarks found out that using insertRule will kill the Chrome DevTools which are very important for debugging though. Using textContent on the other hand does not interrupt the DevTools.

[Plugin] Friendly pseudo class

In order to improve DX we should have a plugin to transform javascript-friendly pseudo class syntax into valid syntax. e.g. { onHover: { /* styles */ }} would become { ':hover' : { /* styles */ }}.

Use a proper warning module

We shoud use a proper warning module that removes all warnings on production build. e.g. Facebook's warning module.

Optimize rendering output

Sharing static style declarations with all Selector variations could drastically reduce the markup size and improve performance. It would remove redundant styles in favor of reusing them.
e.g.

const selector = props => ({
  color: props.color,
  fontSize: '12px',
  width: '20px',
  justifyContent: 'center',
  flexWrap: 'nowrap'
})

renderer.render(selector, { props: 'red' }) // => class1
renderer.render(selector, { props: 'blue' }) // => class2

produces the following CSS markup

.class1 {
  color: red;
  font-size: 12px;
  width: 20px;
  justify-content: center;
  flex-wrap: nowrap
}

.class2 {
  color: blue;
  font-size: 12px;
  width: 20px;
  justify-content: center;
  flex-wrap: nowrap
}

we could optimize the rendering process to have the following markup:

renderer.render(selector, { props: 'red' }) // => class1 class1-dyn1
renderer.render(selector, { props: 'blue' }) // => class1 class1-dyn2
.class1 {
  font-size: 12px;
  width: 20px;
  justify-content: center;
  flex-wrap: nowrap
}

.class1-dyn1 {
  color: 'red'
}

.class1-dyn2 {
  color: 'blue'
}

Ability to dangerously set an animation name for Keyframe

Proposal

Using Fela with React a lot one will usually pass the renderer via context and then use from within the Components itself. Though rendering keyframes always follows the following pattern (as you can't call the renderer from out of the component)

const keyframe = new Keyframe(props => ({
  from: { color: 'red' },
  to: { color: 'blue' }
}))

const selector = props => ({
  animation: props.name +  ' 2s infinite'
})

const className = renderer.render(selector, {
  name: renderer.render(keyframe)
})

This will create unnecessary selectors as the static selector styles will output: animation: undefined 2s infinite. We should therefore be able to to dangerously set a custom animation name.

Why dangerously?

Because with a fix animation name you can only use the last rendered keyframe variation. Also using this you're not safed from naming collisions.

Code Example:

const keyframe = new Keyframe(props => ({
  from: { color: 'red' },
  to: { color: 'blue' }
}), 'myanimation')

const selector = props => ({
  animation: 'myanimation 2s infinite'
})

const className = renderer.render(selector)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.