Tag: CSSinJS

Web Standards Meet User-Land: Using CSS-in-JS to Style Custom Elements

The popularity of CSS-in-JS has mostly come from the React community, and indeed many CSS-in-JS libraries are React-specific. However, Emotion, the most popular library in terms of npm downloads, is framework agnostic.

Using the shadow DOM is common when creating custom elements, but there’s no requirement to do so. Not all use cases require that level of encapsulation. While it’s also possible to style custom elements with CSS in a regular stylesheet, we’re going to look at using Emotion.

We start with an install:

npm i emotion

Emotion offers the css function:

import {css} from 'emotion';

css is a tagged template literal. It accepts standard CSS syntax but adds support for Sass-style nesting.

const buttonStyles = css`   color: white;   font-size: 16px;   background-color: blue;    &:hover {     background-color: purple;   } `

Once some styles have been defined, they need to be applied. Working with custom elements can be somewhat cumbersome. Libraries — like Stencil and LitElement — compile to web components, but offer a friendlier API than what we’d get right out of the box.

So, we’re going to define styles with Emotion and take advantage of both Stencil and LitElement to make working with web components a little easier.

Applying styles for Stencil

Stencil makes use of the bleeding-edge JavaScript decorators feature. An @Component decorator is used to provide metadata about the component. By default, Stencil won’t use shadow DOM, but I like to be explicit by setting shadow: false inside the @Component decorator:

@Component({   tag: 'fancy-button',   shadow: false })

Stencil uses JSX, so the styles are applied with a curly bracket ({}) syntax:

export class Button {   render() {     return <div><button class={buttonStyles}><slot/></button></div>   } }

Here’s how a simple example component would look in Stencil:

import { css, injectGlobal } from 'emotion'; import {Component} from '@stencil/core';  const buttonStyles = css`   color: white;   font-size: 16px;   background-color: blue;   &:hover {     background-color: purple;   } ` @Component({   tag: 'fancy-button',   shadow: false }) export class Button {   render() {     return <div><button class={buttonStyles}><slot/></button></div>   } }

Applying styles for LitElement

LitElement, on the other hand, use shadow DOM by default. When creating a custom element with LitElement, the LitElement class is extended. LitElement has a createRenderRoot() method, which creates and opens a shadow DOM:

createRenderRoot()  {   return this.attachShadow({mode: 'open'}); }

Don’t want to make use of shadow DOM? That requires re-implementing this method inside the component class:

class Button extends LitElement {   createRenderRoot() {       return this;   } }

Inside the render function, we can reference the styles we defined using a template literal:

render() {   return html`<button class=$ {buttonStyles}>hello world!</button>` }

It’s worth noting that when using LitElement, we can only use a slot element when also using shadow DOM (Stencil does not have this problem).

Put together, we end up with:

import {LitElement, html} from 'lit-element'; import {css, injectGlobal} from 'emotion'; const buttonStyles = css`   color: white;   font-size: 16px;   background-color: blue;   &:hover {     background-color: purple;   } `  class Button extends LitElement {   createRenderRoot() {     return this;   }   render() {     return html`<button class=$ {buttonStyles}>hello world!</button>`   } }  customElements.define('fancy-button', Button);

Understanding Emotion

We don’t have to stress over naming our button — a random class name will be generated by Emotion.

We could make use of CSS nesting and attach a class only to a parent element. Alternatively, we can define styles as separate tagged template literals:

const styles = {   heading: css`     font-size: 24px;   `,   para: css`     color: pink;   ` } 

And then apply them separately to different HTML elements (this example uses JSX):

render() {   return <div>     <h2 class={styles.heading}>lorem ipsum</h2>     <p class={styles.para}>lorem ipsum</p>   </div> }

Styling the container

So far, we’ve styled the inner contents of the custom element. To style the container itself, we need another import from Emotion.

import {css, injectGlobal} from 'emotion';

injectGlobal injects styles into the “global scope” (like writing regular CSS in a traditional stylesheet — rather than generating a random class name). Custom elements are display: inline by default (a somewhat odd decision from spec authors). In almost all cases, I change this default with a style applied to all instances of the component. Below are the buttonStyles which is how we can change that up, making use of injectGlobal:

injectGlobal` fancy-button {   display: block; } `

Why not just use shadow DOM?

If a component could end up in any codebase, then shadow DOM may well be a good option. It’s ideal for third party widgets — any CSS that’s applied to the page simply won’t break the the component, thanks to the isolated nature of shadow DOM. That’s why it’s used by Twitter embeds, to take one example. However, the vast majority of us make components for for a particular site or app and nowhere else. In that situation, shadow DOM can arguably add complexity with limited benefit.

The post Web Standards Meet User-Land: Using CSS-in-JS to Style Custom Elements appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , ,

The Fragmented, But Evolving State of CSS-in-JS

TLDR: The CSS-in-JS community has converged on a consistent API.

Not so long ago, a Facebook engineer compiled a list of the available CSS-in-JS methodologies. It wasn’t short:

aphrodite, babel-plugin-css-in-js, babel-plugin-pre-style, bloody-react-styled, classy, csjs, css-constructor, css-light, css-loader, css-ns, cssobj, cssx-loader, cxs, electron-css, emotion, es-css-modules, freestyler, glamor, glamorous, hiccup-css, hyperstyles, i-css, j2c, jsxstyle, linaria, nano-css, pre-style, radium, react-css-builder, react-css-components, react-css-modules, react-cssom, react-fela, react-free-style, react-inline-css, react-inline-style, react-inline, react-jss, react-look, react-native-web, react-statics-styles, react-styl, react-style, react-styleable, react-stylematic, react-theme, react-vstyle, reactcss, restyles, scope-styles, smart-css, stile-react-media-queries, stilr, stylable, style-it, styled-components, styled-jsx, styletron-react, styling, superstyle, typestyle, uranium

Such a fragmented ecosystem was far from appealing. Which one should you pick, (if any)?

Contributing to Javascript fatigue — you need at most one. Also feel free to not learn any.

GitHub stars are one useful metric:

However, GitHub stars say nothing about a project’s *trajectory* *—* perhaps they were accumulated long ago and the repo has since fallen out of favor or is no longer maintained. Glamor has plenty of open issues, and hasn’t seen a commit in over a year. Its author advises:

…it mostly works, I’m not going to do any major changes… if you need something more modern, I’d recommend emotion, it mostly matches glamor’s api, and is actively maintained.

The similarly named Glamorous was recently deprecated with its author also recommending users switch to Emotion:

At the time, Emotion had some features that Styled Components didn’t. Since then, Styled Components has made some big announcements.

Styled Components sells itself as the CSS-in-JS library for people that *like* CSS. Styled Components gained popularity by utilizing tagged template literals — allowing developers to *just write CSS* in the same syntax they already know, but inside JavaScript files. While this has proven popular, some developers prefer to [write styles as JavaScript objects. Emotion offered flexibility — developers could choose how to write their styles. Styled Components eventually followed suit.

Emotion also offers a css prop, which Styled Components didn’t have, until…

The rival CSS-in-JS libraries have stolen from each other until landing upon the same feature set and the same syntax — Emotion and Styled Components have an almost identical API. What once felt like a total mess of competing methodologies and libraries now feels somewhat stable. Even if CSS-in-JS hasn’t standardized on a dependency, it now has standardized a way of doing things — they’re just implemented differently:

Styled Components is by far the most popular CSS-in-JS library, but Emotion has seen a rapid increase in usage.

Both are used by some major companies. Styled Components are utilized by plenty of large companies, including Bloomberg, Atlassian, Reddit, Target, BBC News, The Huffington Post, Coinbase, Patreon, Vogue, Ticketmaster, Lego, InVision and Autodesk just to name a few.

Emotion boasts fewer recognizable names, but has been recently adopted by the New York Times.


While these libraries certainly do seem to be most popular amongst React users, they can be used with other frameworks. While they seem to have converged on the same features at last, it’s difficult to say whether this is the end point of CSS-in-JS, or whether we’ll see a continued evolution from here.

The post The Fragmented, But Evolving State of CSS-in-JS appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Bridging the Gap Between CSS and JavaScript: CSS-in-JS

In this article, we’re going to dig into the concept of CSS-in-JS. If you’re already acquainted with this concept, you might still enjoy a stroll through the philosophy of that approach, and you might be even more interested in the next article (coming tomorrow!).

Web development is very interdisciplinary. We’re used to working closely with multiple languages. And, as developing web applications becomes more commonplace and nuanced, we often look for creative ways to bridge the gaps between those languages to make our development environments and workflows easier and more efficient.

The most common examples are typically when using templating languages. For example, one language might be used to generate the code of a more verbose language (often HTML). This is one of the key aspects of front end frameworks — what does manipulating HTML look like? The most recent twist in this area was JSX because it’s not really a templating language; it’s a syntax extension to JavaScript, and it makes working with HTML really succinct.

Web applications go through many state combinations and it’s often challenging to manage content alone. This is why CSS sometimes falls by the wayside — even though managing styling through different states and media queries is equally important and just as challenging. In this two-part series, I would like to place CSS in the spotlight and explore bridging the gap between it and JavaScript. Throughout this series, I will assume that you’re using a module bundler like webpack. As such, I will use React in my examples, but the same or similar principles are applicable to other JavaScript frameworks, including Vue.

The CSS landscape is evolving in many directions because there are a lot of challenges to solve and there is no “correct” path. I’ve been spending considerable effort experimenting with various approaches, mostly on personal projects, so the intention behind this series is only to inform, not to prescribe.

Challenges of CSS

Before diving into code, it’s worth explaining the most notable challenges of styling web applications. The ones I’ll talk about in this series are scoping, conditional and dynamic styles, and reusability.

Scoping

Scoping is a well-known CSS challenge, it’s the idea of writing styles that don’t leak outside of the component, thus avoid unintended side effects. We would like to achieve it ideally without compromising authoring experience.

Conditional and dynamic styles

While the state in front-end applications started getting more and more advanced, CSS was still static. We were only able to apply sets of styles conditionally — if a button was primary, we would probably apply the class “primary” and define its styles in a separate CSS file to apply how it’s going to look like on the screen. Having a couple of predefined button variations was manageable, but what if we want to have a variety of buttons, like specific ones tailored for Twitter, Facebook, Pinterest and who knows what else? What we really want to do is simply pass a color and define states with CSS like hover, focus, disabled etc. This is called dynamic styling because we’re no longer switching between predefined styles — we don’t know what’s coming next. Inline styles might come to mind for tackling this problem, but they don’t support pseudo-classes, attribute selectors, media queries, or the like.

Reusability

Reusing rulesets, media queries etc. is a topic I rarely see mentioned lately because it’s been solved by preprocessors like Sass and Less. But I’d still like to revisit it in this series.

I will list some techniques for dealing with these challenges along with their limitations in both parts of this series. No technique is superior to the others and they aren’t even mutually exclusive; you can choose one or combine them, depending on what you decide will improve the quality of your project.

Setup

We’ll demonstrate different styling techniques using an example component called Photo. We’ll render a responsive image that may have rounded corners while displaying alternative text as a caption. It will be used like this:

<Photo publicId="balloons" alt="Hot air balloons!" rounded />

Before building the actual component, we’ll abstract away the srcSet attribute to keep the example code brief. So, let’s create a utils.js file with two utilities for generating images of different widths using Cloudinary:

import { Cloudinary } from 'cloudinary-core'  const cl = Cloudinary.new({ cloud_name: 'demo', secure: true })  export const getSrc = ({ publicId, width }) =>   cl.url(publicId, { crop: 'scale', width })  export const getSrcSet = ({ publicId, widths }) => widths   .map(width => `$  {getSrc({ publicId, width })} $  {width}w`)   .join(', ')

We set up our Cloudinary instance to use the name of Cloudinary’s demo cloud, as well as its url method to generate URLs for the image publicId according to the specified options. We’re only interested in modifying the width in this component.

We’ll use these utilities for the src and srcset attributes, respectively:

getSrc({ publicId: 'balloons', width: 200 }) // => 'https://res.cloudinary.com/demo/image/upload/c_scale,w_200/balloons'  getSrcSet({ publicId: 'balloons', widths: [200, 400] }) // => 'https://res.cloudinary.com/demo/image/upload/c_scale,w_200/balloons 200w,       https://res.cloudinary.com/demo/image/upload/c_scale,w_400/balloons 400w'

If you’re unfamiliar with srcset and sizes attributes, I suggest reading a bit about responsive images first. That way, you’ll have an easier time following the examples.

CSS-in-JS

CSS-in-JS is a styling approach that abstracts the CSS model to the component level, rather than the document level. This idea is that CSS can be scoped to a specific component — and only that component — to the extent that those specific styles aren’t shared with or leaked to other components, and further, called only when they’re needed. CSS-in-JS libraries create styles at runtime by inserting <style> tags in the <head>.

One of the first libraries to put this concept to use is JSS. Here is and example employing its syntax:

import React from 'react' import injectSheet from 'react-jss' import { getSrc, getSrcSet } from './utils'  const styles = {   photo: {     width: 200,     '@media (min-width: 30rem)': {       width: 400,     },     borderRadius: props => (props.rounded ? '1rem' : 0),   }, }  const Photo = ({ classes, publicId, alt }) => (   <figure>     <img       className={classes.photo}       src={getSrc({ publicId, width: 200 })}       srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}       sizes="(min-width: 30rem) 400px, 200px"     />     <figcaption>{alt}</figcaption>   </figure> ) Photo.defaultProps = {   rounded: false, }  export default injectSheet(styles)(Photo)

At first glance, the styles object looks like CSS written in object notation with additional features, like passing a function to set the value based on props. The generated classes are unique, so you never need to worry about them clashing with other styles. In other words, you get scoping for free! This is how most CSS-in-JS libraries work — of course, with some twists in features and syntax that we’ll cover as we go.

You can see by the attributes that the width of our rendered image starts at 200px, then when the viewport width becomes at least 30rem, the width increases to 400px wide. We generated an extra 800 source to cover even larger screen densities:

  • 1x screens will use 200 and 400
  • 2x screens will use 400 and 800

styled-components is another CSS-in-JS library, but with a much more familiar syntax that cleverly uses tagged template literals instead of objects to look more like CSS:

import React from 'react' import styled, { css } from 'styled-components' import { getSrc, getSrcSet } from './utils'  const mediaQuery = '(min-width: 30rem)'  const roundedStyle = css`   border-radius: 1rem; `  const Image = styled.img`   width: 200px;   @media $  {mediaQuery} {     width: 400px;   }   $  {props => props.rounded && roundedStyle};    const Photo = ({ publicId, alt, rounded }) => (   <figure>     <Image       src={getSrc({ publicId, width: 200 })}       srcSet={getSrcSet({ publicId, widths: [200, 400, 800] })}       sizes={`$  {mediaQuery} 400px, 200px`}       rounded={rounded}     />     <figcaption>{alt}</figcaption>   </figure> ) Photo.defaultProps = {   rounded: false, }  export default Photo

We often create semantically-neutral elements like <div> and <span> solely for styling purposes. This library, and many others, allow us to create and style them in a single motion.

My favorite benefit of this syntax is that it’s like regular CSS, minus interpolations. This means that we can migrate our CSS code more easily and we get to use our existing muscle memory instead of having to familiarize ourselves with writing CSS in the object syntax.

Notice that we can interpolate almost anything into our styles. This specific example demonstrates how we can save the media query in the variable and reuse it in multiple places. Responsive images are an excellent use case for this because the sizes attribute contains basically CSS, so we can use JavaScript to make the code more DRY.

Let’s say that we decided we want to visually hide the caption, but still make it accessible for screen readers. I know that a better way of achieving this would be to use an alt attribute instead, but let’s use a different way for the sake of this example. We can use a library of style mixins called polished — it works great with CSS-in-JS libraries making it great for our example. This library includes a mixin called hideVisually which does exactly what we want and we can use it by interpolating its return value:

import { hideVisually } from 'polished'  const Caption = styled.figcaption`   $  {hideVisually()}; `  <Caption>{alt}</Caption>

Even though hideVisually outputs an object, the styled-components library knows how to interpolate it as styles.

CSS-in-JS libraries have many advanced features like theming, vendor prefixing and even inlining critical CSS, which makes it easy to stop writing CSS files entirely. At this point, you can start to see why CSS-in-JS becomes an enticing concept.

Downsides and limitations

The obvious downside to CSS-in-JS is that it introduces a runtime: the styles need to be loaded, parsed and executed via JavaScript. Authors of CSS-in-JS libraries are adding all kinds of smart optimizations, like Babel plugins, but some runtime costs will nevertheless exist.

It’s also important to note that these libraries aren’t being parsed by PostCSS because PostCSS wasn’t designed to be brought into the runtime. Many use stylis instead as a result because it’s much faster. This means that we unfortunately can’t use PostCSS plugins.

The last downside I’ll mention is the tooling. CSS-in-JS is evolving at a really fast rate and text editor extension, linters, code-formatters etc. need to play catch-up with new features to stay on par. For example, people are using the VS Code extension styled-components for similar CSS-in-JS libraries like emotion, even though they don’t all have the same features. I’ve even seen API choices of proposed features being influenced by the goal of retaining syntax highlighting!

The future

There are two new CSS-in-JS libraries, Linaria and astroturf, that have managed zero runtime by extracting CSS into files. Their APIs are similar to styled-components, but they vary in features and goals.

The goal of Linaria is to mimic the API of CSS-in-JS libraries like styled-components by having built-in features like scoping, nesting and vendor prefixing. Conversely, astroturf is built upon CSS Modules, has limited interpolation capabilities, and encourages using a CSS ecosystem instead of deferring to JavaScript.

I built Gatsby plugins for both libraries if you want to play with them:

Two things to have in mind when using these libraries:

  1. having actual CSS files means that we can process them with familiar tools like PostCSS
  2. Linaria uses custom properties (a.k.a. CSS variables) under the hood, be sure to take their browser support into consideration before using this library

Conclusion

CSS-in-JS are all-in-one styling solutions for bridging the gap between CSS and JavaScript. They are easy to use and they contain useful built-in optimizations — but all of that comes at a cost. Most notably, by using CSS-in-JS, we’re essentially ejecting from the CSS ecosystem and deferring to JavaScript to solve our problems.

Zero-runtime solutions mitigate some of the downsides by bringing back the CSS tools, which ascends the CSS-in-JS discussion to a much more interesting level. What are the actual limitations of preprocessing tools compared to CSS-in-JS? This will be covered in the next part of this series.

Article Series:

  1. CSS-in-JS (This post)
  2. CSS Modules, PostCSS and the Future of CSS (Coming tomorrow!)

The post Bridging the Gap Between CSS and JavaScript: CSS-in-JS appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]