GithubHelp home page GithubHelp logo

callstack / linaria Goto Github PK

View Code? Open in Web Editor NEW
11.5K 11.5K 414.0 31.98 MB

Zero-runtime CSS in JS library

Home Page: https://linaria.dev

License: MIT License

JavaScript 12.36% HTML 0.14% TypeScript 87.17% Shell 0.33%
css css-in-js css-in-react react

linaria's Introduction

Linaria

Zero-runtime CSS in JS library.


Build Status Code Coverage Version MIT License

All Contributors PRs Welcome Chat Code of Conduct Greenkeeper Sponsored by Callstack

tweet

Features

  • Write CSS in JS, but with zero runtime, CSS is extracted to CSS files during build
  • Familiar CSS syntax with Sass like nesting
  • Use dynamic prop based styles with the React bindings, uses CSS variables behind the scenes
  • Easily find where the style was defined with CSS sourcemaps
  • Lint your CSS in JS with stylelint
  • Use JavaScript for logic, no CSS preprocessor needed
  • Optionally use any CSS preprocessor such as Sass or PostCSS
  • Supports atomic styles with @linaria/atomic

Why use Linaria

Learn how Airbnb improved both developer experience and web performance with Linaria

Installation

npm install @linaria/core @linaria/react @wyw-in-js/babel-preset

or

yarn add @linaria/core @linaria/react @wyw-in-js/babel-preset

Setup

Linaria is now built on top of wyw-in-js.dev. It supports various bundlers to extract the CSS at build time. To configure your bundler, check the following guides on the wyw-in-js.dev site:

See Configuration to customize how Linaria processes your files.

Syntax

Linaria can be used with any framework, with additional helpers for React. The basic syntax looks like this:

import { css } from '@linaria/core';
import { modularScale, hiDPI } from 'polished';
import fonts from './fonts';

// Write your styles in `css` tag
const header = css`
  text-transform: uppercase;
  font-family: ${fonts.heading};
  font-size: ${modularScale(2)};

  ${hiDPI(1.5)} {
    font-size: ${modularScale(2.5)};
  }
`;

// Then use it as a class name
<h1 className={header}>Hello world</h1>;

You can use imported variables and functions for logic inside the CSS code. They will be evaluated at build time.

If you're using React, you can use the styled helper, which makes it easy to write React components with dynamic styles with a styled-component like syntax:

import { styled } from '@linaria/react';
import { families, sizes } from './fonts';

// Write your styles in `styled` tag
const Title = styled.h1`
  font-family: ${families.serif};
`;

const Container = styled.div`
  font-size: ${sizes.medium}px;
  color: ${props => props.color};
  border: 1px solid red;

  &:hover {
    border-color: blue;
  }

  ${Title} {
    margin-bottom: 24px;
  }
`;

// Then use the resulting component
<Container color="#333">
  <Title>Hello world</Title>
</Container>;

Dynamic styles will be applied using CSS custom properties (aka CSS variables) and don't require any runtime.

See Basics for a detailed information about the syntax.

Demo

Edit Linaria Demo

Documentation

Contributing

We appreciate any support in library development!

Take a look on Contributing docs to check how you can run Linaria in development mode.

Trade-offs

  • No IE11 support when using dynamic styles in components with styled, since it uses CSS custom properties

  • Dynamic styles are not supported with css tag. See Dynamic styles with css tag for alternative approaches.

  • Modules used in the CSS rules cannot have side-effects. For example:

    import { css } from '@linaria/core';
    import colors from './colors';
    
    const title = css`
      color: ${colors.text};
    `;

    Here, there should be no side-effects in the colors.js file, or any file it imports. We recommend to move helpers and shared configuration to files without any side-effects.

Interoperability with other CSS-in-JS libraries

Linaria can work together with other CSS-in-JS libraries out-of-the-box. However, if you want to use styled components from Linaria as selectors in styled-components/emotion, you need to use @linaria/interop

Editor Plugins

VSCode

Atom

Webstorm

Sublime Text

Recommended Libraries

Inspiration

Acknowledgements

This project wouldn't have been possible without the following libraries or the people behind them.

Special thanks to @kentcdodds for his babel plugin and @threepointone for his suggestions and encouragement.

Made with ❤️ at Callstack

Linaria is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. Callstack is a group of React and React Native geeks, contact us at [email protected] if you need any help with these or just want to say hi!

Like the project? ⚛️ Join the team who does amazing stuff for clients and drives React Native Open Source! 🔥

Sponsors

{callstack}

Servers.com

Contributors

Thanks goes to these wonderful people (emoji key):

Paweł Trysła
Paweł Trysła

💻 📖 🤔
Satyajit Sahoo
Satyajit Sahoo

💻 📖 🤔
Michał Pierzchała
Michał Pierzchała

💻 📖 🤔
Lucas
Lucas

📖
Alexey Pronevich
Alexey Pronevich

📖
Wojtek Szafraniec
Wojtek Szafraniec

💻
Anton Evzhakov
Anton Evzhakov

💻 🤔 📖
Tushar Sonawane
Tushar Sonawane

📖 💡
Ferran Negre
Ferran Negre

📖
Jakub Beneš
Jakub Beneš

💻 📖
Oscar Busk
Oscar Busk

🐛 💻
Dawid
Dawid

💻 📖
Kacper Wiszczuk
Kacper Wiszczuk

💻 📖
Denis Rul
Denis Rul

💻
Johan Holmerin
Johan Holmerin

💻 📖
Gilad Peleg
Gilad Peleg

📖
Giuseppe
Giuseppe

💻
Matija Marohnić
Matija Marohnić

💻 📖
Stefan Schult
Stefan Schult

💻
Ward Peeters
Ward Peeters

💻
radoslaw-medryk
radoslaw-medryk

💻
杨兴洲
杨兴洲

💻
Dawid Karabin
Dawid Karabin

📖
Chris Abrams
Chris Abrams

💻 📖 🤔
Jayphen
Jayphen

💻
c4605
c4605

💻
Toru Kobayashi
Toru Kobayashi

💻
Jakub Mazurek
Jakub Mazurek

💻
Joshua Nelson
Joshua Nelson

💻 🤔 📖
Tomasz Krzyżowski
Tomasz Krzyżowski

💻
Martin Schulze
Martin Schulze

💻
wmzy
wmzy

💻
Hyeseong Kim
Hyeseong Kim

💻
Martin Hochel
Martin Hochel

💻
Daniel Lo Nigro
Daniel Lo Nigro

💻
0xflotus
0xflotus

💻
Afzal Sayed
Afzal Sayed

💻
AijiUejima
AijiUejima

💻
Oleksii Vasyliev
Oleksii Vasyliev

💻
Alican Erdoğan
Alican Erdoğan

💻
Aman Kubanychbek
Aman Kubanychbek

💻
Andrew Gerard
Andrew Gerard

💻
Andrey Frolov
Andrey Frolov

💻
Benjamin Solum
Benjamin Solum

💻
Billy Kwok
Billy Kwok

💻
Christian Todd
Christian Todd

💻
David Peek
David Peek

💻
Denis Skiba
Denis Skiba

💻
Dima Kharitonov
Dima Kharitonov

💻
Gabriel Valfridsson
Gabriel Valfridsson

💻
Gitai
Gitai

💻
Hampus Kraft
Hampus Kraft

💻
Igor Sukharev
Igor Sukharev

💻
Ikko Ashimine
Ikko Ashimine

💻
Iman Mohamadi
Iman Mohamadi

💻
JB <codecorsair>
JB

💻
Jack Works
Jack Works

💻
James George
James George

💻
Jed Mao
Jed Mao

💻
Joe Lencioni
Joe Lencioni

💻
Joey Cozza
Joey Cozza

💻
Juan Ferreras
Juan Ferreras

💻
Kazuma Ebina
Kazuma Ebina

💻
Lars Kappert
Lars Kappert

💻
Luciano Mammino
Luciano Mammino

💻
Madhav Varshney
Madhav Varshney

💻
Malash
Malash

💻
Martijn Swaagman
Martijn Swaagman

💻
Matias Lahti
Matias Lahti

💻
Michael James
Michael James

💻
Michael Strobel
Michael Strobel

💻
Michał Chudziak
Michał Chudziak

💻
Mike
Mike

💻
Mike Stop Continues
Mike Stop Continues

💻
Mokshit Jain
Mokshit Jain

💻
Oleksandr Fediashov
Oleksandr Fediashov

💻
Paddy O'Brien
Paddy O'Brien

💻
Patrik Smělý
Patrik Smělý

💻
Pavel Udaloff
Pavel Udaloff

💻
Przemysław Bitkowski
Przemysław Bitkowski

💻
RiN
RiN

💻
Roman Sokhan
Roman Sokhan

💻
Seokmin Hong (Ray)
Seokmin Hong (Ray)

💻
Serge K Lebedev
Serge K Lebedev

💻
Sergey Korovin
Sergey Korovin

💻
Shreyas Sreenivas
Shreyas Sreenivas

💻
Sky Wickenden
Sky Wickenden

💻
Stanislav Panferov
Stanislav Panferov

💻
Ted Jenkins
Ted Jenkins

💻
Thanh Tran
Thanh Tran

💻
Thor Amorim
Thor Amorim

💻
tobenna
tobenna

💻
Tomas Carnecky
Tomas Carnecky

💻
Tsubasa1218
Tsubasa1218

💻
Turadg Aleahmad
Turadg Aleahmad

💻
Vitor Buzinaro
Vitor Buzinaro

💻
Mistereo
Mistereo

💻
Vladislav Kozulya
Vladislav Kozulya

💻
Yuhei Yasuda
Yuhei Yasuda

💻
Danil Kamyshov
Danil Kamyshov

💻
Sebastian Landwehr
Sebastian Landwehr

💻
everdimension
everdimension

💻
ptol
ptol

💻
roottool
roottool

💻
ryamaguchi0220
ryamaguchi0220

💻
simka
simka

💻
soso
soso

💻
Nikita Skovoroda
Nikita Skovoroda

💻
黄小健
黄小健

💻
iMoses
iMoses

💻
Jeremy Neander
Jeremy Neander

💻
Andy Parsons
Andy Parsons

💻
Platane
Platane

📖
Tim Kutnick
Tim Kutnick

📖
Dmitrii Pikulin
Dmitrii Pikulin

💻

This project follows the all-contributors specification. Contributions of any kind welcome!

linaria's People

Contributors

agriffis avatar agtlucas avatar allcontributors[bot] avatar anber avatar anc95 avatar chentsulin avatar chrisabrams avatar cometkim avatar daniel15 avatar dependabot[bot] avatar dfrkp avatar esemesek avatar gabbev avatar github-actions[bot] avatar hotell avatar jayphen avatar jayu avatar johanholmerin avatar jpnelson avatar kcover avatar kutnickclose avatar layershifter avatar mrfoxpro avatar nstepien avatar satya164 avatar thymikee avatar tmaszko avatar wmzy avatar yepitschunked avatar zamotany 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  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

linaria's Issues

Allow to disable auto prefixng for extracted CSS

Currently the CSS will be autoprefixed to support as far as IE8. We need to be able to customize the browser target to reduce any extra code we don't need.

Perhaps we should switch to PostCSS instead of stylis as the parser so we have finer control over the extraction.

Add a `include` helper

What do you think about a include helper (kinda like the @include in sass)

const a = css`
  color: blue;
`;

const b = css`
  ${include(a)}
  font-family: sans-serif;
`

Will produce:

.a_hg654gv {
  color: blue;
}

.b_56rwtyf {
  color: blue;
  font-family: sans-serif;
}

The implementation will be something like:

function include(...classNames: string[]): string {
  return classNames.reduce((acc, curr) => {  
    const style = stylesCache.find(item => item.selector === curr);
    if (typeof style !== 'undefined') {
      return acc + '\n' + style.css;
    }
    throw new Error(`Unable to find CSS for the class name: ${curr}`);
  }, '');
}

Action required: Greenkeeper could not be activated 🚨

🚨 You need to enable Continuous Integration on all branches of this repository. 🚨

To enable Greenkeeper, you need to make sure that a commit status is reported on all branches. This is required by Greenkeeper because we are using your CI build statuses to figure out when to notify you about breaking changes.

Since we did not receive a CI status on the greenkeeper/initial branch, we assume that you still need to configure it.

If you have already set up a CI for this repository, you might need to check your configuration. Make sure it will run on all new branches. If you don’t want it to run on every branch, you can whitelist branches starting with greenkeeper/.

We recommend using Travis CI, but Greenkeeper will work with every other CI service as well.

Improve class name composition

Composing classnames should ensure correct specificity so that class name towards end has higher specificity. We can increase the specificity manually to achieve this.

e.g. - compose('header__gyt654', 'title__gf63rt') might produce .title__gf63rt, .title__gf63rt.header__gyt654 and .header__gyt654 instead of .title__gf63rt and .header__gyt654

Babel plugin to Hoist CSS

What if we could write a CSS rule inline without having to give it a name? A Babel plugin can automatically hoist this to the program scope and give it a name.

This JavaScript:

function MyComponent() {
  return <article {...styles(css`color: blue;`)} />;
}

Gets transpiled into:

function MyComponent() {
  return <article {...styles(_article)} />;
}
var _article = css`color: blue;`;

Context: https://twitter.com/ryanflorence/status/907316693084418048

Implementation:

export default function(babel) {
  const { types: t } = babel;

  return {
    visitor: {
      Program: {
        enter(path, state) {
          state.cssRules = {};
        },
        exit(path, state) {
          Object.keys(state.cssRules).forEach(name => {
            path.node.body.push(
              t.variableDeclaration("var", [t.variableDeclarator(t.identifier(name), state.cssRules[name])])
            );
          });
        }
      },
      JSXOpeningElement(path, state) {
        path.node.attributes.forEach(attr => {
          if (t.isJSXSpreadAttribute(attr) && t.isCallExpression(attr.argument) && attr.argument.callee.name === "styles") {
            attr.argument.arguments = attr.argument.arguments.map(arg => {
              if (t.isTaggedTemplateExpression(arg) && arg.tag.name === "css") {
                const className = path.scope.generateUidIdentifier(path.node.name.name);
                state.cssRules[className.name] = arg;
                return className;
              }
              return arg;
            });
          }
        });
      }
    }
  };
}

PR and issue templates

GitHub allows for creating nice templates for PRs and Issues, which is helpful both for maintainers and contributors. Would be nice to set this up.

Deduplicate CSS rules

When the content of two rules are the same, use the same rule instead of adding duplicates:

e.g.: instead of

.header { font-size: 2rem; }

.title { font-size: 2rem; }

We should produce:

.header,
.title { font-size: 2rem; }

Better class names

We should include the variable names in the class name. We could write a babel plugin to replace const header = css with const header = css.named('header__ghg54t') which will achieve that

Add a note in generated CSS

Just to avoid confusion for new devs coming to the project, it's useful if we append a small note to the generated file, just like Yarn does:

/* THIS IS AN AUTOGENERATED FILE BY LINARIA. DO NOT EDIT THIS FILE DIRECTLY. */

.hero_37644b71{background:#fffaf6;padding:150px 0;}
.heading_37644b71{color:#DE2D68;font-weight:700;font-size:4em;text-transform:uppercase;text-shadow:0 0 #FFEBCE,1px 1px #FFEBCE,2px 2px #FFEBCE,3px 3px #FFEBCE,4px 4px #FFEBCE,5px 5px #FFEBCE;}

Polyfill CSS Variables Support

API

The API for writing styles remains the same. For example:

const title = css`
  color: var(--title-color);
`;

When using this class name in a component:

function MyComponent({ color }) {
  return <h1 {...styles(title, { '--title-color': color })} />;
}

How to implement

  1. Write a Babel plugin which looks for styles calls with variables and assigns an unique ID for each. Something like:
    styles(title, { __id: 'xgfs65d', '--title-color': color });
    This ID is necessary so that we can keep track of dynamic styles and avoid inserting same styles multiple times. The ID should be unique, but shouldn't change between builds. The easiest way is to hash the current file path and the sequential number in the file.
  2. We will need to generate an AST from the CSS string. We can probably use the ast plugin for stylis to generate the ast, but I had a broken ast when I used nested media queries. We will probably need to fix the plugin. We will also need to write a simple serializer which takes the AST and serializes it to plain CSS string.
  3. The css tagged template literal should parse the CSS string to an AST to determine if there are CSS variables in the string. It should generate 2 different CSS strings, one with properties using variables, and one without. The one without is inserted normally to the sheet API, but the ones with variables is returned as a templates. It's an array because there can be multiple rules. The returned object from css can look like following:
    { toString() { return 'xgsf64f'; }, templates: ['.xgsf64f { color: var(--title-color); }'] }
    Something to keep in mind is that if a property is redeclared after a variable use, we should discard the one with variable, i.e. color: var(--title-color); color: blue should be considered as color: blue.
  4. The names function should be updated to look for these objects and use toString when necessary
  5. The styles function should look for templates and do a search replace in them with the value of the variables, then insert it to the CSSOM with the insertRule API. If CSS custom properties are supported in the browser, it should avoid the search replace and use inline styles to set the variable values.
  6. The babel presets should be separated to client and server presets, where the server preset excludes the preval-extract extract plugin and includes a plugin which just converts css to css.named calls.
  7. Server rendering APIs and instructions should be updated to read the stylesheet from the string in memory instead of from a file.
  8. This should be configurable in the Babel preset options. If the polyfill behaviour is disabled, templates shouldn't be generated and inline styles should be used to set the variables instead.

Advantages

  1. A consistent prop-based way to use dynamic styles with components
  2. Variables don't cascade anymore, and results are more predictable
  3. No overhead for browsers which already support CSS variables

Disadvantages

  1. Some overhead for browsers which don't support CSS variables

Export babel plugins

We need to export 2 different babel plugins:

linaria/classnames - should transform css to css.named with the variable name. this is useful for development where we would like to have better class names, this is also useful for server rendering
linaria/extract-css - should inline the constants etc. with preval and extract the CSS files

Webpack integration

We should be able to use Webpack's HTML plugin to inject generated stylesheet into the HTML file, probably using css-loader and style-loader. This probably works already, but needs to be tested and documented.

Ensure we throw nice errors when users do something unsupported

Using undefined variables

Handled in #81

Errors thrown during preval

Handled in #111

If a function throws an error during preval, we should catch it and throw nice error with proper stacktrace.

const test = () => {
  throw new Error('Some weird error');
}

const test = css`
  color: ${test()};
`;

Typos in CSS

If the user mistyped a variable name, we could print an error instead of generating invalid CSS. For example, say we get undefined during interpolation:

const colors = {
  blue: 'blue'
}

cont title = css`
  color: ${colors.bleu};
`

Verify hashes are unique

We need to ensure that the short hash module never produces same hash for different CSS text. Probably better to vendor this module (it's tiny) or look at other CSS in JS libs' implementation and copy them.

Redundant CSS in webpack HMR

I started to work on website (using Next.js) and this is what I'm hitting in HMR:

screen shot 2017-08-27 at 09 33 31

There's a bunch duplicated css in every hot update. Wonder if we can do something about it? Not a HMR ninja unfortunately :<

cc @zamotany

Spread syntax for styles

This is a proposal for a possible syntax for styles

image

advantages of spread syntax

  • less typing
  • less noise
  • same syntax can be used on a different platform like RN

disadvantages

  • not obvious how to pass styles from a parent component

Don't include parser in the bundle

Since we pre-evaluate all css code during build time, we don't need to include the css tag and the CSS parser in the bundle. To reduce the bundle size, we can:

  • Rewrite the import for css with a function that throws an error
  • Rewrite the import for include with a function that throws an error
  • Rewrite imports like import { names } from 'linaria' to import names from 'linaria/build/names'

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.