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 EmberJS

Creating runtime assisted Codemods using Telemetry helpers

In this article, we are going to take a glimpse at the ember-codemods-telemetry-helpers package and how it helps to create more advanced codemods for Ember.js.

If you want a more in-depth introduction to codemods, you can checkout this post detailing the why’s and how’s of codemods.

Telemetry for layman

First we will take a look at what Telemetry is all about. According to Wikipedia,

Telemetry is the collection of measurements or other data at remote or inaccessible points and their automatic transmission to receiving equipment for monitoring.

Software Telemetry

In software, telemetry is used to gather data on the use and performance of applications and its components, e.g. how often certain features are used, measurements of start-up time and processing time, hardware, application crashes and general usage statistics and/or user behavior. In some cases, very detailed data is reported like individual window metrics, counts of used features and individual function timings.

This kind of telemetry can be essential to software developers to receive data from a wide variety of endpoints that can’t possibly all be tested in-house.

Privacy concerns

Due to concerns about privacy, since software telemetry can easily be used to profile users, telemetry in user software is often an user choice, commonly presented as an opt-in feature (requiring explicit user action to enable it) or during the software installation process.

We had enough look about what really Telemetry is about. Now it’s time to look at the more specific use-case of creating codemods using telemetry helpers.

Telemetry helpers for Codemods

Telemetry helpers runs the app, grabs basic info about all of the modules at runtime. This allows the codemod to know the names of every helper, component, route, controller, etc. in the app without guessing / relying on static analysis. They basically help you to create “runtime assisted codemods”.

Below you can find the package composition of the ember-codemods-telemetry-helpers package.

The goal of this project is to enable each codemod to manage its own type of data gathering and to provide the harness to run that custom gathering function.

This package exports six functions for gathering telemetry information which can be used in the codemods.

Using the telemetry helpers for codemods

Assuming you are authoring a codemod with codemod-cliember-codemods-telemetry-helpers allows you the freedom to assign your own “telemetry gathering” function while provide one of its own out of the box.

All invocations of gatherTelemetryForUrl internally returns an object enumerated with properties named after all possible entries within window.require.entries. The values of each property is the value returned from within the gathering function. Using the example above, the output might be:

This package provides one gathering function: analyzeEmberObject. This function does a “best effort” analysis of the app runtime, returning such things as Components, Helpers, Routes, etc. and their particular properties.

It parses Ember meta data object, collects the runtime information and returns the following list of properties :

Gathering runtime data

Let’s see how the codemods use the telemetry helpers to gather data at runtime. For that, let’s take an example to see how the ember-native-class-codemod is invoked.

The first argument that you must pass to the codemod is the URL of a running instance of your application. The codemod opens up your application using the URL passed as the argument and analyzes the classes directly in order to transform them. Any classes that were not analyzed will not be transformed. This includes classes that are private to a module and never exported.

If you have any lazily loaded modules, such as modules from Ember Engines, you need to ensure that the URL you provide loads these modules as well. Otherwise, the codemod will not be able to detect or analyze them.

Origins of the Telemetry helpers

This project was extracted from ember-native-class-codemod. That codemod uses Puppeteer through this package to visit the Ember app and gather telemetry necessary to convert to native classes.

The idea for the extraction was to put the harness in this package (extracted from the native class codemod), but have the actual “telemetry gathering” live in each individual codemod project because the things that they need are quite different, for example, for implicit this codemod and angle brackets codemod all we need to know is an array of the helpers and components in the app, but for native class codemod it needs much more information such as names and types of methods, properties, etc on each default export.

Codemods already using the helpers

Currently there are two codemods : ember-native-class-codemod and ember-no-implicit-this-codemod, which are already making use of the telemetry helpers to gather information from the app during runtime. And there is an open pull request by Ryan Mark for the ember-angle-brackets-codemod to make use of the telemetry helpers.

References

Categories
Articles EmberJS

Ember into Futurity

Nobody can predict the future, but everybody can wish for something in it. It could be for the benefit and interest of one person or a community as a whole. I am putting down my thoughts and wishes for Ember.js as both a consumer of the framework itself and producer of the supporting tools and addons for the same.

The best way to predict the future is to invent it.

This post is my answer to the call for blog posts for #EmberJS2019. I haven’t had the slightest idea to write a post for the blog post call, but my colleagues and friends insisted me to write one. Because honestly I really don’t know where to start and how to give a conclusive ending to my post. But my creative Muse never ceases to amaze me. So here it goes.

The Framework

As a framework, we need to fully achieve conceptual integrity on what we want to accomplish and how we want to move forward with it. Because instead of trying to become one “All Purpose Tool Kit”, we should focus on one thing and do it well.

Ember has the right ideas, but the wrong atomic structure.

Component Paradigm

While Ember was ruling the MVC realms, there came a knight known as “React“, armed with a simple yet profound idea, who taught us how we can build web applications using components. The Components Model may be a new one for Web development, but it wasn’t for programmers and system application developers like me who have been dealing with objects and classes for a very long time. Yet this new Knight conquered the world with his deep and extensive conceptual integrity. Ember failed to realize this threat for a very long time, even though Ember had components in it’s arsenal, but it was no match for React.

Octane

Octane is the one thing I eagerly waited for in Ember a year ago when Tom Dale published his Ember 2018 Roadmap RFC. I even remember that I explored the RFC in detail in my blog here. But still we are yet to see a full-blown Octane Edition landing in Ember. And it would be a disgrace if I fail to appreciate the effort put by the Team to make the necessary provisions for the Octane release by making the guides ready with great care and great tact.

More Flexible CLI

One of the best things I love about Ember is the CLI. No other Javascript Framework has got it right till date. While all of the other frameworks use CLI as just an application scaffolding tool to bootstrap a project, Ember-CLI goes beyond them with generators and blueprints. And it gets even better by giving us the flexibility to define and generate a custom blueprint.

But the CLI is not very flexible in-terms of choosing tools and libraries for various tasks other than the standard packages shipped as part of the CLI. The Convention over Configuration is holding us back in some aspects on this front. The Web is evolving with more new tools and libraries day by day, and I think we should open up more and make provisions to allow developers to choose their own tools and libraries best suited for their projects.

We should adopt the ideas from projects like ember-cli-create which will allow the developers to choose their own tools and technologies for their projects based on the requirements, platform and other external factors.

Unified Documentation

This is one thing I really wanted to emphasize here. Because we have so many ideas being implemented in Ember such as Server-Side Rendering (Fastboot), Ember Engines and Glimmer.js. But all of them are scattered and fragmented. I wish all the above things should branch out from our main guides for Ember instead of having separate websites.

I am not concerned more on the technical implementation rather on the conceptual mapping to give developers an overall idea of different technologies that can be used along with the framework. It could be a simple page for each one of the above within guides itself and guide the users to take them to the main website if they want to explore more on this or a more prominent place where we can have links to these projects within the guides itself.

Functional Components (Hooks)

When hooks landed in React, it made a real fuzz. More of my fellow React developers were singing praises for the same, such as how it changed the way they write code and mutated their minds on how you could bring about solutions for complex problems using components. And again underneath all the fuzz, it’s just a conceptual modification that the React framework brought in for writing components, which can be adapted to any framework that supports the component model for creating user interfaces for the web.

I wish Ember could have adopted it much earlier, but it’s better late than never. I am very much relieved to see this has seen the light of the day, when I saw ember-functional-component  created by our beloved rwjblue.

Fail Fast

As a community we value “Stability without Stagnation”, but that proved expensive for the framework in the long run. I personally urge that we should fail fast, because by failing fast we come to value the lessons and insights we learnt from our failures and move forward with much greater pace. We need to get out there, try out new things and find out what works and what doesn’t.

Why do we fall? So we can learn to pick ourselves up.

– Alfred, Batman

Embroider

Using broccoli as a build tool, have been a huge impediment for Ember, in adopting the best and the latest conventions from other frameworks in terms of build pipeline and tooling. Since other frameworks like React, Angular and Vue are heavily betting on cutting edge packaging technologies like Webpack, Rollup and Parcel, while Ember still fixating on broccoli proved to be a costly mistake for the community. I think Embroider will not suffer from the same fate, by adopting Webpack as a tool in the build pipeline.

Conclusion

So instead of asking “Why didn’t Ember succeed?”, actually the right question to ask will be Why is there a perception that Ember didn’t “succeed”? And I would like to conclude this post with words from my favorite author, Henry David Thoreau.

Read your fate, see what is before you and walk on into futurity.

Categories
Articles EmberJS

Creating Connection-aware Ember Media Components

Creating Connection-aware Ember Media Components

In this article, we will take a look at creating Ember Components serving media content such as images and videos based on the network bandwidth of the users. For this, we will make use of the Network Information API provided by the browsers. Currently, the only browser that supports this api is Chrome, soon we are expecting that all the browsers will start supporting the network information api.

This tutorial is purely based on an existing tutorial by Max Böck, a frontend web developer based in Vienna, Austria. Max has done the components in React, this is an Ember port of the same component.

Let’s get started. We will create a new Ember application for this by firing the ember-cli to scaffold a new app.

Next we will create the actual component :

We will start working with the component code now. First we will start with the component template, since we are going to render different types of components based on the connection type, we will be using the {{component}} helper in Ember to render the appropriate component. Hence the template for the component will look something like:

First to make sure the browser supports the Network Information API. We are going to have a private function in the component to tell us the same.

Using the above helper function, we are going to set the effective connection type in the component.js file with the init() component life-cycle hook:

Next we are going to determine the type of component to be rendered based on the connectionType using a computed property called componentType which will be used in the component template.

As you can see, we need three more components , one for the image, one for video and one placeholder component for other stuff like offline and unknown connection types.

Hence we will generate the three components accordingly:

We are making use the PODS structure for components here to ensure the templates and component logic are in the same folder. To know more about the PODS structure please refer to the standard Ember Guides.

Now the respective component templates for each and every component will be like the following.

For the place-holder component, the template will be something like:

For the ember-video component, the template code contains the following markup:

And, finally the ember-image component markup will look like this:

Now we are determining the image url to be loaded based on the connection type, and again using one more computed property for the same task.

And for the ember-video component, we need to provide the source urls for the video files, and for this we are using two computed properties just reading the parent component properties like this.

Now we are pretty much ready to use the component, so we will be providing the component in our sample application generated above in the first place and in the application.hbs file of the app, we will render our component like:

The respective urls for the image and videos are hard-coded as controller properties for the application route. For that first we need to generate a controller for our application route using the command

And then we will provide the urls in the form of four distinct properties for low resolution image, high resolution image, ogg video url and webm video url.

Now our component looks great and render properly the right content effectively based on the user’s connection bandwidth. But it will take the connection information only at the initial rendering of the page or component, so if the connection quality improves in the middle of the user session, still we will be getting the same effective content rendered.

To address this, we will be passing one more property to our component called autoUpdatebased on which the component renders the right media content as soon the connection quality is either improved or degraded in the browser.

To accommodate the autoUpdate functionality for our component, we need to bind event handlers for network api change events. We will do this in two places, one in didInsertElement for adding the event listener and to clean up the stuff in willDestoryElement life-cycle hooks of the component to remove the binded event listeners.

And we will be defining the event handler for the change event of network information api into something like this:

And in the helper function getConnectionType will be doing this

Demo & Source Code

References

Image credits

  • Photo by Clint Adair on Unsplash