Electron

Packed Chromium + Node.js

Web-site https://electronjs.org
Platforms Linux, macOS, Windows
Renderer Skia + WebGL
Download Electron Chat Coming soon

Tech stack used for building a showcase app:

Build system Webpack
Languages TypeScript
Libraries React, styled components
Editor Visual Studio Code

Internals

The browser is an incredibly complicated piece of software comparable with the Operating System itself. There is a lot happening to render these simple HTML + CSS into pixels that we see on the screen. I should put here a link to Chrome internals. Chromium uses cross-platform library Skia (skia.org) to paint these pixels.

Let’s look at a simple button. In order to place it on the screen you need HTML:

<button type="button">Save</button>

some optional CSS to style this button:

button {
  margin: 2em;
}

and optionally JavaScript to handle changes:

document.querySelector("input").onchange = e => console.log(e);

This is the result you get on macOS in Chrome and Electron:

A default button in Chrome

At first glance, it looks like a native button on macOS, but when we press it, we can spot a little difference comparing to the same button in Safari:

a pressed button in Chrome

a pressed button in Safari

Why is it happening? If we dump our rendered chat client from HTML+CSS to Skia’s internal representation format and use a debugger, we can watch a step-by-step demonstration of how the whole document was painted by using lower-level graphical instructions:

Skia debugger step-by-step painting process of a chat

And the button here is just an image of button’s background + set of glyphs drawn over it + some transformation logic applied to get the correct size.

So the button in Chrome doesn’t match the native look&feel for every operating system out there. But who cares? In terms of styling, usually for a web site it does not mean a lot, controls are rarely used as it is, their design is customized and it probably won’t look anything like a default native button anyway:

A styled button

This is not always true for the desktop. For a certain group of desktop apps, though, you may want to “mimic” the look to make it closer to system controls. That is still possible by carefully crafting the right CSS, but it’ll be harder to maintain over time. As an example, look at the library that provided a set of macOS UI controls called Maverix, it became outdated the next second Apple released the next macOS version.

Good news, we don’t need anything like for the most parts of our chat app, we can use custom styles.

Developer experience

Since Electron is based on a web browser, Chromium, in this case, the developer experience for anyone who is already familiar with the web is just amazing. For everything that is related to the UI inside a window, you feel like you just write a web application. You have all sort of hot reloadings, DevTools that allows you to change styles on the fly and profile on every machine your app installed, and an infinite amount of integrations with design tools.

Simplicity of UI development in Electron — it's basically the same as in the browser

The level of googlability of any problem is really high, either that is an npm package, a stackoverflow question or a GitHub issue similar to yours.

Network

What about writing network code, HTTP and WebSocket in particular? We can guess just by looking at the names of these protocols, we have Hypertext and WebSocket here, both invented for the web browser, which exactly Electron is. Surprisingly, though, HTTP was much more suited initially for interlinked documents, not apps, so all we have is XmlHttpRequest and Fetch and abstractions over them. Now, things are changing and browsers are getting better. You have WebRTC now and can try to build Skype on top of it.

And again we’re writing a web application, so to start using Slack’s API we’ll go to npm:

yarn add @slack/client

and use it like this for HTTP API:

const web = new WebClient(token);
return web.users.list({}).then(results => {
  return results.members as Array<SlackUser>;
});

and we can also use WebSocket-based, real-time client, without even thinking about details:

const rtm = new RTMClient(token, params);
rtm.on("message", message => {
  // Log the message
  console.log(
    `(channel:${message.channel}) ${message.user} says: ${message.text}`
  );
});

Here comes a small rant about how hard to write a desktop client for Slack, even though it’s understandable from the business’ standpoint and I should have probably picked something open sourced. But thanks to the support, turns out it’s possible to use undocumented way to use their API. And again, which is one might love web platform, it’s so easy to do live debugging

Paused debugger for Slack developer section in order to change a scope parameter

OS Integration

To be able to write an app

oAuth in a Slack app that does not re-use the browser

OAuth leveraging existing Slack session in the browser.

Accessibility (a11y)

Let’s enable acessibility on macOS and check simple things:

macOS accessibility shortcuts

Voice Over

At first I was getting “link, #webrender, we” on each menu:

Voice over demonstration for menu, it says link, #webrender, we

Why is that?

Because React components inside ChannelList produced HTML that looked like this:

<div>
  <!-- ... -->
  <a href="">
    <span>#</span>

    <span>webrender</span>

    <span>we</span>
  </a>
  <!-- ... -->
</div>

After reading https://inclusive-components.design/menus-menu-buttons/, I added some semantics and aria attributes:

<nav>
  <ul aria-labelledby="sections-heading">
    <h3 id="sections-heading">Channel List</h3>
    <!-- ... -->
    <li>
      <a href="" aria-current="page">
        <span aria-hidden="true">#</span>

        <span aria-label="channel webrender">webrender</span>

        <span aria-hidden="true">we</span>
      </a>
    </li>
    <!-- ... -->
  </ul>
</nav>

It’s all done in styled-components and React fully supports aria- attributes.

So we hide # and we (needed for the compact mode), and now it looks like a menu, and users know what to expect:

The demonstration of Voice over after a small refactoring

And what’s nice, we can use LightHouse to inspect accessibility since our UI is basically an HTML opened in the web browser and we can use Devtron — a tool to debug Electron apps.

Animations

To be continued…

Theme support