Categories
Articles EmberJS

ember-csz: A CSS-in-JS solution for styling in Ember

In this article we are going to take a look at an Ember.js addon called ember-csz which provides template helpers for csz, a Runtime CSS-Modules with SASS like pre-processing.

This is based on a talk I gave recently at ESNextConf. I just wanted to experiment the same with Ember.js and it came out well. I liked it very much the idea of keeping your styles as part of your component.js just like how the React community uses other awesome CSS-in-JS solutions like Styled Components and Emotion.

csz

csz is a super-tiny framework agnostic CSS-in-JS solution created by Luke Jackson. Luke is a front-end developer from London. He also created some other amazing tooks like perf-link and servor. csz uses stylis to parse styles from tagged template literals and append them to the head of the document at runtime. Loading in stylesheets dynamically – from .css files – is supported out of the box, so you can write your styles in .css files and import them via url without having to worry about flashes of unstyled content.

And csz supports a lot of other awesome features, please checkout the project’s README for more information.

Installing ember-csz

Just like installing any other Ember.js addon, you would install ember-csz with :

ember install ember-csz

Inline Styling

This addon exposes the default helper called csz which can be used directly in templates if you want to write your styles inline. But keep in mind that the styles are not applied inline with the style attribute but instead csz generates class names dynamically and append them to the head of the document at runtime and apply this class to the class attribute of your elements.

<h1 class={{ csz "text-align:center" }}>Hello World</h1>

And the dynamically generated class name will look like this:

.csz-wps2kyg7yn {
    text-align: center;
}

And the output in your html will be like:

<h1 class="csz-wps2kyg7yn">Hello World</h1>

Example of component styling with csz

Let’s begin with an example of styling a component with csz. This is the typical use case of using CSS-in-JS solution with Ember components. The first step is to import csz into your component.js file. Since the csz library is already part of the ember-csz addon, you don’t have to install it separately. Once you install the ember-csz addon, it will automatically available for you to use in your Javascript files in your Ember apps.

hello-world.js

import Component from "@glimmer/component";
import csz from "csz";

const styles = csz`
background: papayawhip;
text-align:center;
padding: 4em;
h1 {
color: palevioletred;
font-size: 2em;
}`;

export default class HelloWorldComponent extends Component {
  styles = styles;
}

hello-world.hbs

Now we will take a look at how to use the styles in our component template. It’s just as simple as applying or using any other property from your component.js. Just assign {{this.styles}} to your class property of the parent element.

<div class= {{this.styles}}>
  <h1>Hello Ember</h1>
</div>

Using the component

<HelloWorld />

The dynamically generated class names from the style tag will be:

.csz-pj0yxya1qes {
 background:papayawhip;
 text-align:center;
 padding:4em;
}
.csz-pj0yxya1qes h1 {
 color:palevioletred;
 font-size:2em;
}

Adapting based on props

Next we will see how to adapt the styles of your components based on the properties you pass to them. In this example we change the background and color property of the button based on the @primary property of the component.

my-button.js

import Component from "@glimmer/component";
import csz from "csz";

export default class MyButtonComponent extends Component {
  styles = csz`
    background: ${this.args.primary ? "palevioletred" : "white"};
        color: ${this.args.primary ? "white" : "palevioletred"};
        font-size: 1em;
        margin: 1em;
        padding: 0.25em 1em;
        border: 2px solid palevioletred;
        border-radius: 3px;
`;
}

my-button.hbs

<button class={{this.styles}} type="button">{{yield}}</button>

Using the button component

Next we will see how to use the `MyButton` component in our templates. If the `@primary` property is passed the button component will have `palevioletred` and `white` as the background and color attributes respectively. Otherwise it will take the default value of `white` and `palevioletred` for background and color.

<MyButton @primary={{true}}>Primary</MyButton>
<MyButton>Normal</MyButton>

Animation and Keyframe namespacing

Next we will take a look at how to create animations with csz. csz will automatically namespace your animations with the dynamically generated class names.

my-animation-component.js

import Component from "@glimmer/component";
import csz from "csz";

export default class MyAnimationComponent extends Component {
  styles = csz`
 display: inline-block;
      animation: rotate 2s linear infinite;
      padding: 2rem 1rem;
      font-size: 1.2rem;
      @keyframes rotate {
        from {
          transform: rotate(0deg);
        }
        to {
          transform: rotate(360deg);
        }
      }
`;
}

my-animation-component.html

<div class={{this.styles}}>
  <img src="tomster.png" alt="Ember Tomster" width="100" />
</div>

Using the animation component

<MyAnimation />

The dynamically generated class names with animation and keyframes namespacing will be like:

.csz-rzj7b7sdtyi {
 display:inline-block;
 -webkit-animation:rotate-csz-rzj7b7sdtyi 2s linear infinite;
 animation:rotate-csz-rzj7b7sdtyi 2s linear infinite;
 padding:2rem 1rem;
 font-size:1.2rem;
}
@-webkit-keyframes rotate-csz-rzj7b7sdtyi {
 from {
  -webkit-transform:rotate(0deg);
  -ms-transform:rotate(0deg);
  transform:rotate(0deg);
 }
 to {
  -webkit-transform:rotate(360deg);
  -ms-transform:rotate(360deg);
  transform:rotate(360deg);
 }
}
@keyframes rotate-csz-rzj7b7sdtyi {
 from {
  -webkit-transform:rotate(0deg);
  -ms-transform:rotate(0deg);
  transform:rotate(0deg);
 }
 to{
  -webkit-transform:rotate(360deg);
  -ms-transform:rotate(360deg);
  transform:rotate(360deg);
 }
}

As you can see from the above code, csz automatically do vendor-prefixing which is actually supported by Stylis.

Pseudo selectors

You can also use pseudo selectors, child selectors, sibling selectors and so on with csz. It’s primarily because of stylis which is the CSS preprocessor used by csz. Stylis is the CSS preprocessor used by Styled Components and Emotion. It’s a light weight CSS preprocessor with SASS like pre-processing. And there is no compilation or build step required to process the css because everything happens at runtime. This is way better than using plain SASS pre-processing with Ember becuase you need to install ember-cli-sass and node-sass in your projects to do the same.

my-pseudo component.js

import Component from "@glimmer/component";
import csz from "csz";

export default class MyPseudoComponent extends Component {
  styles = csz`
 color: blue;
        &:hover {
          color: red; // <Thing> when hovered
        }
        & ~ & {
          background: tomato; // <Thing> as a sibling of <Thing>, but maybe not directly next to it
        }
        & + & {
          background: lime; // <Thing> next to <Thing>
        }
        &.something {
          background: orange; // <Thing> tagged with an additional CSS class ".something"
        }
        .something-else & {
          border: 1px solid; // <Thing> inside another element labeled ".something-else"
        }
`;
}

my-pseudo.hbs

<div class={{this.styles}}>
{{yield}}
</div>

Using the MyPseudo Component

<MyPseudo>Hello World!</MyPseudo>
<MyPseudo>How ya doing?</MyPseudo>
<MyPseudo class="something">The sun is shining...</MyPseudo>
<div>Pretty nice day today.</div>
<MyPseudo>Don't you think?</MyPseudo>
<div class="something-else">
  <MyPseudo>Splendid.</MyPseudo>
</div>

Theming

Next we will see how we can implement themes using csz in Ember. You define your themes as simple objects with color values and then use the same in your template literals to use the values to evaluate based on the props or something. In this example we setting up a default theme with main color as palevioletred and if there is a theme property we will take the color from the passed-in theme property, otherwise we will use the default theme color.

theme-button.js

import Component from "@glimmer/component";
import csz from "csz";

const defaultTheme = {
  main: "palevioletred",
};

export default class ThemeButtonComponent extends Component {
  theme = this.args.theme || defaultTheme;
  styles = csz`
 font-size: 1em;
        margin: 1em;
        padding: 0.25em 1em;
        border-radius: 3px;
        background: white;
        color: ${this.theme.main};
        border: 2px solid ${this.theme.main};
`;
}

theme-button.hbs

<button class={{this.styles}} type="button">
{{yield}}
</button>

Using the ThemeButton component

When you pass the `@theme` property to the component, it takes the colors from the theme object otherwise it takes the colors from the default theme.

<ThemeButton>Normal</ThemeButton>
<ThemeButton @theme={{this.theme}}>Primary</ThemeButton>

The class names dynamically generated at runtime in the style tag will be:

.csz-czjrz46d7ko {
 font-size:1em;
 margin:1em;
 padding:0.25em 1em;
 border-radius:3px;
 background:white;
 color:palevioletred;
 border:2px solid palevioletred;
}
.csz-id9d43qutq {
 font-size:1em;
 margin:1em;
 padding:0.25em 1em;
 border-radius:3px;
 background:white;
 color:mediumseagreen;
 border:2px solid mediumseagreen;
}

And the html markup will look something like:

<button class="csz-czjrz46d7ko" type="button">
Normal
</button>
<button class="csz-id9d43qutq" type="button">
Primary
</button>

You can also check out all the above code example inside the addons dummy app folder.

References

Categories
Articles Spacemacs

Playing Music in Spacemacs with EMMS

I have been thinking about playing songs inside Spacemacs for quite a while, so that I don’t have to leave my favorite editor for just playing music. After a small exploration I found out that all I need is to enable EMMS inside Spacemacs.

Spacemacs

Spacemacs is a community-driven Emacs distribution. It is a new way to experience Emacs with a sophisticated and polished set-up focused on ergonomics, mnemonics and consistency.

EMMS

Emms is the Emacs Multi-Media System. It tries to be a clean and small application to play multimedia files from Emacs using external players.

Getting EMMS

Clone the spacemacs-emms repo

cd into your .emacs.d/private directory. clone the repository into the emms folder

Add emms layer

Add emms as a layer to your dotspacemacs-configuration-layers to enable emms within Spacemacs.

mplayer

EMMS is just like the back-end to setup a music system, and we need a front-end, a player to actually play the music. We are going to use mplayer as the player for EMMS. Add /usr/local/bin (where mplayer is located) to Emacs’s exec-path by adding the following line to your ~/.spacemacs file in the spacemacs/user-config section.

Setting up emms

The next thing you have to do is telling Spacemacs where Emms is located. Let’s say you have it in ~/.emacs.d/site-lisp/emms/lisp. So add this line to your .emacs:

You’ll then want to load Emms into Spacemacs. To achieve this you invoke the emms-standard setup function by adding the following lines.

Next you want to setup the file extensions you want to play with your player. And you specify mplayer as the emms simple player.

Final config (full)

This is the full configuration all you need to set up EMMS in Spacemacs.

That’s all, now you need to restart Spacemaces Spc-q-R

Bonus content (Some helpful key-bindings)

Adding emms to Spacemacs is not enough, you need some good key bindings to control your playback within Spacemacs. So I found this keybindings here and tweaked it a bit to my liking.

Playing music on Spacemacs startup

And if you are like me who wants to start playing music as soon as you open your text editor you can add this little function in your dotspacemacs/user-config

References:

Categories
Articles

Setting up Prettier on Spacemacs

What is Spacemacs?

Spacemacs is a community-driven Emacs distribution. It is a new way to experience Emacs with a sophisticated and polished set-up focused on ergonomics, mnemonics and consistency.

What is Prettier?

Prettier is an opinionated code formatter which supports many languages and integrates with most editors. It can be installed through npm. It is recommended to install prettier globally in your system so that your editor integrates it properly and can be even made to auto formatted while saving your files.

How to setup prettier?

Make sure you installed prettier globally on your system before following the below steps.

If you are using yarn package manager, you can install it with:

Get the develop branch of Spacemacs. Usually it will be located in your home directory under ~/.emacs.d

Add the prettier layer to dotspacemacs-configuration-layers

Add/update javascript layer with prettier as the javascript-fmt-tool. We are going to set prettier as the value for this. This variable tells which is the formatter to format a JavaScript file. Possible values are ‘web-beautify’ and ‘prettier’.

Create a js2-mode-hook to run prettier while saving your files. We are using the js2-mode-hook here because that is the major mode in which javascript files are opened.

If you want to know what is the list of modes you are currently working in like the major mode and the minor mode names, you can use the describe-mode command to know the same. You can either use the shortcuts C-h m or <Space> h d m to run the describe-mode command.

We are going to make use of the before-save-hook for the same. It is a normal hook that is run before a buffer is saved to its file.

References:

Categories
Articles Javascript

Snowpack – How it works?

Snowpack is a post-install tool. It runs after npm install, and it essentially exists to convert your npm packages (in your “nodemodules/” directory) into JS files that run in the browser without a bundler (written to a “webmodules/” directory).

Creating a sample app

In this tutorial we are going to create a simple demo app which is going to make use of a library called finance. We are going to create an app to calculate simple interest from principal, rate and time. This application has got no fancy user interface or something. It just calls the method from the library by giving parameters and print the result in the console.

First let’s setup the application by creating the necessary files.

index.html

This is how our index.html file will look like. As you can see, there’s nothing much in the page apart from the script tag at the bottom. This script tag includes a file called app.js from the src folder of the app and having the attribute type set as module. This means that app.js is an ES module that can be used directly in the page. We will see what goes inside the app.js file later.

snowpack-demo/package.json

This is how our package.json will look like for the demo app. The important thing to note here is the dependency of finance package.

Creating a dummy package inside nodemodules

And now for this example, we are not going to actually install any npm package. We are going to create custom packages on the fly within the nodemodules folder. That’s how nodemodules work, at the end of the day all the packages are just a folder of files with a package manifest, in other words a package.json. Hence in order to create a new package all you need is two things: one package.json and the source file.

For the finance package we were talking about earlier, we are going to create the package in the same way like below.

package: finance

And the package finance will contain two functions: one for calculating simple interest from principal, rate and time and the other one including the principal. We will be using only the simpleInterest function for our demo. The other one just exists for the sake of it.

finance/package.json

The package.json for the finance module is a normal package.json manifest with one exception. We are adding the module field to point out the ESM version of this module by telling where it resides. Since we have already written the package using ES import and export statements, the value of this field is the same as the main field which points to index.js

package: math

Now it’s time to take a look at the math package. It’s simple library exposing primitive operations like add, multiple, divide, etc., and it follows Common JS export system. The reason it is using CMD is to demonstrate the capabilities of Snowpack in handing Common JS modules. Snowpack can also bundle Common JS modules but which are actually the internal dependencies of your parent packages.

math / index.js

The below are the contents of the math library.

Now the dependency tree of our demo app looks like this.

Now install the dependencies using npm and then run snowpack.

Snowpack will read the dependencies from the package.json and start bundling them. Each individual dependency is built with all its dependent packages are flattened into a single file. As you can see below the finance and math packages is bundled into a single file in the new webmodules directory called finance.js. And this is the file which we will be consuming in our demo app.

Now if you inspect the finance.js file in your webmodules folder.

Now we can see how we can use the finance.js from the webmodules folder in our app.js

Peer Dependencies

Now what about peer dependencies? Snowpack is very well equipped for handling peer dependencies in your applications also. It will properly bundle your dependencies by putting the commonly used code such as peer dependencies inside a common folder so that the packages which are consuming these can easily access the same without redundancy.

The dependency tree of our app is very simple we have only two packages finance which is depending on math. Let’s introduce a new package called bmi which will expose methods for calculating bmi (body mass index). The bmi package is also depending on math package for its calculation. Hence our math package now becomes a peer dependency for finance and bmi.

We are going to follow the same steps for creating the bmi package just as we did for finance.

package/bmi

Now add the same package to the dependencies list for the demo app in package.json

The dependency tree of our demo will now look like this:

Now install the dependencies using npm and then run snowpack.

You can optionally add “snowpack” as a “prepare” script to your package.json and npm/yarn will automatically run it after every new dependency install. This is recommended so that new dependencies are automatically included in your webmodules/ directory immediately.

After the installing and running snowpack the bundled files inside webmodules directory will be three Javascript files. One for the bmi package, one for the finance package and we now have common directory which contains the common code in the file named index-093dfa0c.js used by both the packages, which is actually the math package code.

If you inspect the contents of the file in the webmodules folder you can see yourself that Snowpack changed both the bmi and finance package to import from the common math package bundled.

This is how the bundled bmi package will look like now.

And this is how the bundled finance package will look like.

And if you are curious what goes inside the common index file, as I mentioned previously it just contains the code of the math package.

Now we can import the bmi package into our demo application from the webmodules folder like below:

Production Builds

Snowpack is not only optimized for development environment but also for production builds. You can create compressed or minified version of your dependencies for using in production environments and deploying with Snowpack. It also generates source maps when you are bundling for production. All you need to do is to pass the –optimize flag while running Snowpack.

Tree Shaking

Snowpack helps you to remove any unused code from your dependencies (when “Automatic Mode” is enabled via the –include flag). In order for Tree shaking to work properly we need to have ESM compatible versions for all your packages. Since our math package is based on Common JS module system, we need to have a separate ESM version for the same like below.

It is actually quite easy all you have to do is to convert them using export syntax for each methods which are exported from the math package instead of using the module.exports

And you also need to make some changes with the package.json of the math package by exposing the ESM version using the module field.

Now if you run Snowpack again with the –include flag with the app.js file.

You will have your math package properly tree-shaked or the unused subtract methods from the package will be removed since it is not used by any of the dependencies.

That’s all from this tutorial. Hope you have a better idea now about how Snowpack bundles your dependencies in your applications from the above examples. Please let me know for any issues or feedback about the article in the comments.

Cover Image by Chris Biron on Unsplash