free-style

Make CSS easier and more maintainable by using JavaScript

Subscribe to updates I use free-style


Statistics on free-style

Number of watchers on Github 643
Number of open issues 4
Average time to close an issue 5 days
Main language TypeScript
Average time to merge a PR 8 days
Open pull requests 4+
Closed pull requests 5+
Last commit about 2 months ago
Repo Created over 3 years ago
Repo Last Updated about 2 months ago
Size 179 KB
Organization / Authorblakeembrey
Latest Releasev2.5.1
Contributors2
Page Updated
Do you use free-style? Leave a review!
View open issues (4)
View free-style activity
View on github
Latest Open Source Launches
Trendy new open source projects in your inbox! View examples

Subscribe to our mailing list

Evaluating free-style for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)

Free-Style

NPM version NPM downloads Build status Test coverage Greenkeeper badge

Free-Style is designed to make CSS easier and more maintainable by using JavaScript.

Installation

npm install free-style --save

Why?

There's a great presentation by Christopher Chedeau you should check out.

Solved by using CSS in JS

  • No global variables (What and where is .button? Why is it conflicting?)
  • Defined dependency systems (CommonJS, Require.js, <script />)
  • Dead code elimination automatically removes unused styles
  • Minification through JavaScript tooling
  • Shared constants and reusable styles
  • Every style is isolated, tested and namespaced to the JS component
  • Extensible - everything from Math to color manipulation already exists!

Also solved with Free-Style

  • Works with third-party DOM components (You can nest regular .class-name in your styles)
  • Consistently generates styles and class names, and will automatically merge duplicate styles
  • Develop components alongside the style (No more hunting CSS files for estranged ul > li > a)
  • Create universal applications and serve styles for only the components rendered (see React Free-Style)
  • Use the CSS you already know ({ '&:hover': { ... } })
  • Automatically namespace @-rule styles ({ '@media (min-width: 500px)': { ... } })
  • Overload CSS properties using arrays ({ backgroundColor: ['red', 'linear-gradient(to right, red 0%, blue 100%)'] })
  • Small and powerful API that works with any ecosystem (~360 SLOC)

But How?

Free-Style generates a consistent hash from the style, after alphabetical property ordering and formatting, to use as the class name. This allows duplicate styles to automatically be merged on duplicate hashes. Every style is registered and assigned to a variable, which gets the most out of linters that will warn on unused variables and features like dead code minification. Using register returns the class name used for the Style instance and style instances (returned by create()) can be merged together at runtime to output only the styles on page (see React Free-Style). Styles should usually be created outside of the application run loop (e.g. render()) so the CSS string and hashes are only generated once.

Ways to Use

  • react-free-style - React implementation that renders styles on the current page (for universal apps)
  • typestyle - Popular type-safe interface for working with CSS
  • stylin - Simplest abstraction for creating styles, rules, and keyframes, and keeps <style /> in sync
  • easy-style - Light-weight singleton API for browsers and node
  • i-css - Library for writing CSS with literal objects
  • This module! - Manually create, compose and manipulate style instances

Usage

var FreeStyle = require('free-style')

// Create a stylesheet instance.
var Style = FreeStyle.create()

// Register a new style, returning a class name to use.
var backgroundStyle = Style.registerStyle({
  backgroundColor: 'red'
}) //=> "f14svl5e"

// Inject `<style>` into the `<head>`.
var styleElement = document.createElement('style')
styleElement.textContent = Style.getStyles()
document.head.appendChild(styleElement)

// Render the style by using the class name.
React.render(
  <div className={backgroundStyle}>Hello world!</div>,
  document.body
)

Style

var buttonStyle = Style.registerStyle({
  backgroundColor: 'red',
  padding: 10
})

console.log(buttonStyle) //=> "f65pi0b"

Tip: The string returned by registerStyle is a unique hash of the content and is used as the class in HTML.

Overload CSS Properties

Style.registerStyle({
  background: [
    'red',
    '-moz-linear-gradient(left, red 0%, blue 100%)',
    '-webkit-linear-gradient(left, red 0%, blue 100%)',
    '-o-linear-gradient(left, red 0%, blue 100%)',
    '-ms-linear-gradient(left, red 0%, blue 100%)',
    'linear-gradient(to right, red 0%, blue 100%)'
  ]
}) //=> "f1n85iiq"

Nest Rules

Style.registerStyle({
  color: 'red',
  '@media (min-width: 500px)': {
    color: 'blue'
  }
}) //=> "fk9tfor"

Nest Selectors

Style.registerStyle({
  color: 'red',
  '.classname': {
    color: 'blue'
  }
}) //=> "fc1zv17"

Parent Selector Reference

Style.registerStyle({
  color: 'red',
  '&:hover': {
    color: 'blue'
  }
}) //=> "f1h42yg6"

Tip: The ampersand (&) will be replaced by the parent selector at runtime. In this example, the result is .f1h42yg6:hover.

Tip: The second argument to registerStyle, registerKeyframes and registerHashRule is a display name. The display name will be used as the class name prefix in development (process.env.NODE_ENV !== 'production').

Use JavaScript

var extend = require('xtend')

var ellipsisStyle = {
  whiteSpace: 'nowrap',
  overflow: 'hidden',
  textOverflow: 'ellipsis'
}

var redEllipsisStyle = Style.registerStyle(extend(
  {
    color: 'red'
  },
  ellipsisStyle
)) //=> "fvxl8qs"

Tip: This is a shallow extend example. There are modules on NPM for deep extending objects. You can also take advantage of new JavaScript features, such as const and computed properties:

const mediaQuery = '@media (min-width: 400px)'

const style = Style.registerStyle({
  backgroundColor: 'red',
  [mediaQuery]: {
    backgroundColor: 'pink'
  }
})

Unique Style Ouput

Sometimes you need to skip the de-duping behaviour of free-style. Use the IS_UNIQUE export and enforce every style to be output separately:

Style.registerStyle({
  color: 'blue',
  '&::-webkit-input-placeholder': {
    color: `rgba(0, 0, 0, 0)`,
    [FreeStyle.IS_UNIQUE]: true
  },
  '&::-moz-placeholder': {
    color: `rgba(0, 0, 0, 0)`,
    [FreeStyle.IS_UNIQUE]: true
  },
  '&::-ms-input-placeholder': {
    color: `rgba(0, 0, 0, 0)`,
    [FreeStyle.IS_UNIQUE]: true
  }
}) //=> "f13byakl"

Style.getStyles() //=> ".f13byakl{color:blue}.f13byakl::-webkit-input-placeholder{color:rgba(0, 0, 0, 0)}.f13byakl::-moz-placeholder{color:rgba(0, 0, 0, 0)}.f13byakl::-ms-input-placeholder{color:rgba(0, 0, 0, 0)}"

Keyframes

var colorAnimation = Style.registerKeyframes({
  from: { color: 'red' },
  to: { color: 'blue' }
}) //=> "h1j3ughx"

var style = Style.registerStyle({
  animationName: colorAnimation,
  animationDuration: '1s'
}) //=> "fibanyf"

Tip: The string returned by registerKeyframes the name of the animation, which is a hash of the rule (you can also add a display name in development).

Hash Rule

Hashed rules are what registerKeyframes uses internally. It accepts a prefix and the styles object, which will create a rule using prefix + hash. Conveniently, the same contents will generate the same hash so you can register vendor-specific rules using the same hash.

var keyframes = {
  from: {
    color: 'blue'
  },
  to: {
    color: 'red'
  }
}

var animation1 = Style.registerHashRule('@keyframes', keyframes) //=> "f1dz2mpx"
var animation2 = Style.registerHashRule('@-webkit-keyframes', keyframes) //=> "f1dz2mpx"

Rules

Style.registerRule('@font-face', {
  fontFamily: '"Bitstream Vera Serif Bold"',
  src: 'url("https://mdn.mozillademos.org/files/2468/VeraSeBd.ttf")'
})

Style.registerRule('@media print', {
  body: {
    color: 'red'
  }
})

Style.registerRule('body', {
  margin: 0,
  padding: 0
})

CSS Object

Style.registerCss({
  body: {
    margin: 0,
    padding: 0,
    '@print': {
      color: '#000'
    }
  },
  h1: {
    fontSize: '2em'
  }
})

CSS String

Style.getStyles() //=> ".f65pi0b{background-color:red;padding:10px}"

Useful Libraries

Implementor Details

Custom Hash Algorithm

Initialize Free-Style with a custom CSS class generator.

create(hashFunction) //=> `FreeStyle.FreeStyle`

Debug

Debug mode can changed programmatically using the second argument to create([hash [, debug]). It defaults to the value of process.env.NODE_ENV !== 'production'.

Changes

Free-Style provides two methods for detecting style changes. First is a changeId property, incremented every time a change occurs.

const Style = create()
const prevChangeId = Style.changeId

Style.registerStyle({ color: 'red' })

if (Style.changeId !== prevChangeId) {}

Second, the third argument to create() is a map of change function handlers. All functions are required:

  • add (style: Container<any>, index: number): void
  • change (style: Container<any>, oldIndex: number, newIndex: number): void
  • remove (style: Container<any>, index: number): void

All classes implement Container, so you can getStyles(), getIdentifier() or use id.

Classes

FreeStyle.FreeStyle // Similar to writing a CSS file, holds styles and rules - returned from `create()`.
FreeStyle.Style // Styles hold the CSS string and a generate a consistent hash of their contents.
FreeStyle.Rule // Rules are lighter-weight containers that can be nested inside `FreeStyle` instances.
FreeStyle.Selector // Selectors hold the CSS selector and can be nested inside `Style` instances.
FreeStyle.Cache // `FreeStyle`, `Style` and `Rule` all extend the cache which maintains reference counts.

Other Properties and Methods

var ChildStyle = Style.create()

Style.merge(ChildStyle) // Merge the child styles into the current instance.
Style.unmerge(ChildStyle) // Unmerge the child styles from the current instance.

Legacy Browsers

To support legacy browsers (<= IE8) you'll need to polyfill some ES5 features, such as Object.keys, Object.create(null) and Array.isArray.

License

MIT

free-style open issues Ask a question     (View All Issues)
  • over 1 year How to enforce "uniqueness" / separate outputs?
  • over 1 year Idea: Static version of `free-style`
  • over 1 year Suggestion: Post-processing that doesn't alter the style hash
  • almost 3 years Performance question
free-style open pull requests (View All Pulls)
  • Refactor implementation
  • Componentize into files (wip as of now.../reviews & opinions plz)
  • Update @types/node to the latest version 🚀
  • Update ts-node to the latest version 🚀
free-style questions on Stackoverflow (View All Questions)
  • How would I rewrite this palindrome verifier in point-free style?
  • Changing function definition to point-free style
  • What is point free style in Functional Programming?
  • Performance Implications of Point-Free style
  • How to compose functions that return Applicative in point-free style?
  • Point-free style and using $
  • Transforming a function to point-free style changes its type
  • How do achieve the below function composition in point free style?
  • How to schedule a child Free Style job programmatically from a parent Job in Jenkins Plugin Development?
  • Trying to enable "perform maven release" to free style project
  • F# point free style division
  • Documenting functions defined using point-free style
  • Is it possible to convert a Jenkins free-style job to a multi-configuration job?
  • Declaration issue with using curry in point-free style in Haskell
  • Got confused with point free style code
  • Point free style in haskell
  • Why is the point-free style called point free in Haskell when it is full with points? Where does the term "point-free" originates from?
  • simple Haskell functions in point-free style
  • wxPython : Free Style Graphics
  • Partial function application for a non-symmetric operator using point-free style in F#?
  • The case for point free style in Scala
  • What are advantages and disadvantages of "point free" style in functional programming?
  • What is the difference between a Maven and a free-style software project in Jenkins / Hudson?
  • Can this be expressed in point free style?
  • Build a free-style software project - code description
  • Applying multiple functions to the same value point-free style in Haskell
  • Jenkins - How to pass values from pom.xml to downstream job (free style)
  • Partially apply mplus to rewrite function in point-free style
  • Jenkins does not filter Maven resources with environment variables on free-style projects
  • How do I re-write a Haskell function of two argument to point-free style
free-style list of languages used
free-style latest release notes
v2.5.1 Fix Overloaded Property Ordering

Fixed

  • When a property has > 10 overloads, Chrome has an undefined sorting order for matching elements (good breakdown in https://github.com/blakeembrey/free-style/pull/66)
v2.5.0 Escape Display Name

Changed

  • Escape the display name in output CSS for special character support in class names
v2.4.1 Minor Bump

Improved

  • Custom string concatenation is faster than Array#join for the perf tests
  • Update hyphenate regexp matching to do less work
Other projects in TypeScript