Learn Svelte: Creating a Pomodoro Timer
Tags: svelte, javascript, webdev
Published: 2020-01-13 23:33:49

---
title: Learn Svelte: Creating a Pomodoro Timer
published: true
description: Sharing my initial experience in learning Svelte getting started by creating a sample project, a pomodoro technique app. In this part we add a list of tasks to be completed using the pomodoro technique
tags: [svelte,javascript,webdev]
canonical_url: https://www.barbarianmeetscoding.com/blog/2020/01/04/discovering-svelte-creating-a-pomodoro-timer
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/1ndayocmls8wg7iob21r.png
series: Learn Svelte
---

*This article was originally published on [Barbarian Meets Coding](https://www.barbarianmeetscoding.com/blog/2020/01/04/discovering-svelte-creating-a-pomodoro-timer).*

[Svelte](https://svelte.dev/) is a modern web framework that takes a novel approach to building web applications by moving the bulk of its work from runtime to compile-time. Being a compiler-first framework allows Svelte to do some very interesting stuff that is unavailable to other frameworks like disappearing from your application at runtime, or allowing for a component centered development with HTML, JavaScript and CSS coexisting within the same Svelte file in a very web standards friendly fashion.

In this series we'll follow along as I use Svelte for the first time to build an app. I'll use my go-to project[^1] to learn new frameworks: A [Pomodoro Technique](https://francescocirillo.com/pages/pomodoro-technique) app, which is a little bit more involved than a TODO list in that it has at least a couple of components (a timer and a list of tasks) that need to interact with each other.

**In this part four of the series we continue coding along as we create a pomodoro timer that will allow us to work on a given task with our complete focus and full attention. Let's get started!**

> Haven't read the other articles in this series? Then you may want to take a look at this <a href="https://www.barbarianmeetscoding.com/blog/2019/12/23/discovering-svelte">list of resources for getting started with Svelte</a>, and the <a href="https://www.barbarianmeetscoding.com/blog/2019/12/27/discovering-svelte-getting-started">first</a> and <a href="https://www.barbarianmeetscoding.com/blog/2019/12/29/discovering-svelte-adding-some-tasks-and-pomodoros">second</a> parts of building the Pomodoro Technique app.

## Working on a Task with Your Full Attention

In the last part of this series we learned how when using the Pomodoro Technique you'll typically start your day sitting down, deciding what you'll achieve during the day and breaking it in as many pomodoros as you think it'll take. **A pomodoro is a special unit of time used in [The Pomodoro Technique](https://francescocirillo.com/pages/pomodoro-technique) which represents 25 minutes of uninterrupted work focusing on a single task**.

The next step in The Pomodoro Technique consists in:

1. Picking the most important task, 
2. Starting the pomodoro timer, and...
3. Start kicking ass by focusing single-mindedly on that task for the next 25 minutes. 

After the 25 minutes have passed, you'll rest for 5 minutes, and then start a new pomodoro. After 4 pomodoros have been completed you'll rest for 20 minutes. It is important that both when the pomodoro starts and when it finishes, we get a auditory cue which will act as trigger to first get us into focus, and then to get us into a resting mindset.

So if we were to attemp to build a pomodoro timer to support this workflow, it would need to fullfill the following set of requirements:

* It should have three states: An *active state* where we are working on a task and a state where we're *resting* and an *idle state* where we're not doing anything at all.
* In the **active state** it should count from 25 minutes downwards
  * When a pomodoro starts we should hear a cue
  * When a pomodoro ends we should hear another cue
  * We should be able to cancel or stop a pomodoro any time
* In the **resting state** the timer should count from 5 or 20 minutes downwards
  * It should count from 20 minutes downwards when 4 pomodoros have been completed
  * It should count from 5 minutes downwards any other time
* In the **idle state** nothing happens

Once a pomodoro has been completed whe should increase the number of pomodoros invested in the task in progress, and whenever a pomodoro is cancelled we need to type down the reason why (how were we interrupted? Why couldn't we keep our focus?). In this part of the series we'll just focus on building the timer itself, and in future articles we'll continue improving the timer and finally putting everything together. **Let's get to it!**

## The Pomodoro Timer

Since a pomodoro timer seems like a completely separate responsibility from anything else in our app up to this point it deserves its own component. So I'll start by creating a new component called `PomodoroTimer.svelte`:

```html
<p>
  Hi, I'm a pomodoro timer. Yo!
</p>
```

And adding it to our `App.svelte` component:

```html
<script>
	let title = "il Pomodoro";
  import TaskList from './TaskList.svelte';
  import PomodoroTimer from './PomodoroTimer.svelte';
</script>

<main>
  <h1>{title}</h1>
  <PomodoroTimer />
  <TaskList />
</main>
```

I remember the rookie mistake I made in earlier parts of the series and I import the component before I use it in my template. Now my dev environment should display the new component... 

Although it doesn't... 

Weird... 

**Recheck, look at typos, refresh, rerun dev server**. After some troubleshooting I realize that I need to do a hard refresh in my browser, it seems like it is caching `localhost:5000`. So hard refresh it is and now I see the new component. **Sweet!**

## Starting a Pomodoro

Let's begin by implementing a way to start working on our first pomodoro. We're going to need:

1. A button to kick off the pomodoro
2. A way to represent the time left in a pomodoro

The button is quite simple. We update our svelte component template to include a new button that when clicked will start a new pomodoro:

```html
<section>
  <p>
    Hi, I'm a pomodoro timer. Yo!
  </p>
  <button on:click={startPomodoro}>start</button>
</section>
```

Since we don't have a pomodoro timer just yet, we'll start by creating an empty `startPomodoro` function for the time being:

```html
<script>
  function startPomodoro(){}
</script>
```

Now we need a way to represent the pomodoro timer. The initial state of the timer will be the length of a pomodoro (25 minutes). And since we'll often interact with the timer by decreasing a second at a time, we'll represent the length of a pomodoro in seconds (instead of minutes):

```html
<script>
  // length of a pomodoro in seconds
  const POMODORO_S = 25 * 60;

  // time left in the current pomodoro
  let pomodoroTime = POMODORO_S;

  function startPomodoro(){}
</script>
```

Since I don't like having magic numbers in my code I'll extract the time conversion between minutes and seconds inside a function:

```html
<script>
  const minutesToSeconds = (minutes) => minutes * 60;

  // length of a pomodoro in seconds
  const POMODORO_S = minutesToSeconds(25);

  // time left in the current pomodoro
  let pomodoroTime = POMODORO_S;

  function startPomodoro(){}
</script>
```

Now we need to represent that time in the template in the format `MM:SS`. We can use a function to transform the `pomodoroTime` into the desired format:

```javascript
  function formatTime(timeInSeconds) { 
    const minutes = secondsToMinutes(timeInSeconds);
    const remainingSeconds = timeInSeconds % 60;
    return `${padWithZeroes(minutes)}:${padWithZeroes(remainingSeconds)}`;
  }
```

Which uses a couple of helpers:

```javascript
  const secondsToMinutes = (seconds) => Math.floor(seconds / 60);
  const padWithZeroes = (number) => number.toString().padStart(2, '0');
```

Having defined `formatTime` we can use it in our template to transform the value of `pomodoroTime`:

```html
<section>
  <p>
    {formatTime(pomodoroTime)}
  </p>
  <footer>
    <button on:click={startPomodoro}>start</button>
  </footer>
</section>
```

The complete component now looks like this:

```html
<script>
  const minutesToSeconds = (minutes) => minutes * 60;
  const secondsToMinutes = (seconds) => Math.floor(seconds / 60);
  const padWithZeroes = (number) => number.toString().padStart(2, '0');

  // length of a pomodoro in seconds
  const POMODORO_S = minutesToSeconds(25);

  // time left in the current pomodoro
  let pomodoroTime = POMODORO_S;

  function formatTime(timeInSeconds) { 
    const minutes = secondsToMinutes(timeInSeconds);
    const remainingSeconds = timeInSeconds % 60;
    return `${padWithZeroes(minutes)}:${padWithZeroes(remainingSeconds)}`;
  }

  function startPomodoro(){}
</script>

<section>
  <p>
    {formatTime(pomodoroTime)}
  </p>
  <footer>
    <button on:click={startPomodoro}>start</button>
  </footer>
</section>
```

And looks like this:

<img class="center shadow-small" src="https://www.barbarianmeetscoding.com/images/svelte-pomodoro-timer.jpg" title="A pomodoro timer!" alt="A pomodoro app with a timer and a series of tasks">

But if we click on the button `start` nothing happens. We still need to implement the `startPomodro` function. Now that we have an initial implementation for the timer we can fill in its implementation:

```javascript
function startPomodoro() { 
  setInterval(() => {
    pomodoroTime -= 1;
  },1000);
}
```

And **TaDa!** we have a working timer:

<img class="center shadow-small" src="https://www.barbarianmeetscoding.com/images/svelte-pomodoro-timer-starts.gif" title="A pomodoro timer starts its countdown" alt="A pomodoro app with a timer and a series of tasks. The user clicks on start and the pomodoro timer starts its countdown.">

## Completing a Pomodoro and Taking a Break

Now there's two options, we can either focus on working on the task at hand and complete a pomodoro (Yihoo! Great job!) or we can cancel the pomodoro because we've been interrupted by something or someone.

When we complete a pomodoro, two things should happen:

1. The pomodoro count of the current task should increase by one
2. The timer goes into a resting state and starts counting down

Since we aren't going to integrate the timer with the rest of the app yet, let's focus on item number #2 by creating a new function `completePomodoro`. Whenever the `pomodoroTime` count down arrives to `0` we complete the pomodoro calling this new function:

```javascript
function startPomodoro() { 
  setInterval(() => {
    if (pomodoroTime === 0) {
      completePomodoro();
    }
    pomodoroTime -= 1;
  },1000);
}
```

Whenever we complete a pomodoro we're going to slide into a resting state counting down from `20` minutes or `5` minutes depending on whether we have completed 4 pomodoros up to this point. So:

* We define a couple of constants to store the lenghts of the breaks `LONG_BREAK_S` and `SHORT_BREAK_S`
* We define a `completedPomodoros` variable we'll use to keep track of how many pomodoros we have completed up to this point. This variable will determine whether we take the short or long break.
* We implement the `completePomodoro` to complete a pomodoro and jump into the resting state:

```javascript
  const LONG_BREAK_S = minutesToSeconds(20);
  const SHORT_BREAK_S = minutesToSeconds(5);
  let completedPomodoros = 0;

  function completePomodoro(){
    completedPomodoros++;
    if (completedPomodoros === 4) {
      rest(LONG_BREAK_S);
      completedPomodoros = 0;
    } else {
      rest(SHORT_BREAK_S);
    }
  }
```

We still have an interval running our counting down function so we need to make sure to stop that interval before we proceed. We update the `startPomodoro` function to store a reference to the interval:

```javascript
let interval;
function startPomodoro() { 
  interval = setInterval(() => {
    if (pomodoroTime === 0) {
      completePomodoro();
    }
    pomodoroTime -= 1;
  },1000);
}
```

And clear it whenever we complete a pomodoro:

```javascript
function completePomodoro(){
  clearInterval(interval):
  completedPomodoros++;
  // TODO: update the current task with a completed pomodoro
  if (completedPomodoros === 4) {
    rest(LONG_BREAK_S);
    completedPomodoros = 0;
  } else {
    rest(SHORT_BREAK_S);
  }
}
```

The `rest` function sets the timer into the resting state:

```javascript
function rest(time){
  pomodoroTime = time;
  interval = setInterval(() => {
    if (pomodoroTime === 0) {
      idle();
    }
    pomodoroTime -= 1;
  },1000);
}
```

It's very similar to an in-progress pomodoro but it sets the pomodoro into an `idle` state when the count down finishes. The `idle` state can be modelled with this other function:

```javascript
  function idle(){
    clearInterval(interval);
    pomodoroTime = POMODORO_S;
  }
```

The whole component looks like this right now:

```html
<script>
  const minutesToSeconds = (minutes) => minutes * 60;
  const secondsToMinutes = (seconds) => Math.floor(seconds / 60);
  const padWithZeroes = (number) => number.toString().padStart(2, '0');

  const POMODORO_S = minutesToSeconds(25);
  const LONG_BREAK_S = minutesToSeconds(20);
  const SHORT_BREAK_S = minutesToSeconds(5);

  let pomodoroTime = POMODORO_S;
  let completedPomodoros = 0;
  let interval;

  function formatTime(timeInSeconds) { 
    const minutes = secondsToMinutes(timeInSeconds);
    const remainingSeconds = timeInSeconds % 60;
    return `${padWithZeroes(minutes)}:${padWithZeroes(remainingSeconds)}`;
  }

  function startPomodoro() { 
    interval = setInterval(() => {
      if (pomodoroTime === 0) {
        completePomodoro();
      }
      pomodoroTime -= 1;
    },1000);
  }

  function completePomodoro(){
    clearInterval(interval);
    completedPomodoros++;
    // TODO: update the current task with a completed pomodoro
    if (completedPomodoros === 4) {
      rest(LONG_BREAK_S);
      completedPomodoros = 0;
    } else {
      rest(SHORT_BREAK_S);
    }
  }

  function rest(time){
    pomodoroTime = time;
    interval = setInterval(() => {
      if (pomodoroTime === 0) {
        idle();
      }
      pomodoroTime -= 1;
    },1000);
  }

  function idle(){
    clearInterval(interval);
    pomodoroTime = POMODORO_S;
  }
</script>

<section>
  <p>
    {formatTime(pomodoroTime)}
  </p>
  <footer>
    <button on:click={startPomodoro}>start</button>
  </footer>
</section>
```

Now, when things go wrong and we get distracted we must cancel the pomodoro, write down the cause of our distraction (so we can reflect and learn from it) and start over. Let's update our timer to support this use case.

## Cancelling a Pomodoro

In order to be able to cancel a pomodoro we'll add a new button to our template:

```html
<section>
  <p>
    {formatTime(pomodoroTime)}
  </p>
  <footer>
    <button on:click={startPomodoro}>start</button>
    <!-- New button HERE -->
    <button on:click={cancelPomodoro}>cancel</button>
    <!-- END new stuff-->
  </footer>
</section>
```

Whenever the user clicks on this button we'll cancel the current pomodoro using the `cancelPomodoro` function:

```javascript
function cancelPomodoro(){
  // TODO: Add some logic to prompt the user to write down
  // the cause of the interruption.
  idle();
}
```

And now we can start and cancel pomodoros:

<img class="center shadow-small" src="https://www.barbarianmeetscoding.com/images/svelte-cancel-pomodoro.gif" title="Starting and cancelling a pomodoro" alt="A pomodoro app with a timer and a series of tasks. The user clicks on start and the pomodoro timer starts its countdown. Then they click on cancel and the pomodor stops.">

## Improving The User Experience Slightly

With our current implementation a user can start a pomodoro when a pomodoro has already started, and likewise cancel a pomodoro which hasn't started yet which makes no sense. Instead the user should get some visual cues as to what actions make sense under the different conditions. So we're going to improve the user experience of our timer by:

* Enabling the *start pomodoro* button only when we're in an idle state
* Enabling the *cancel pomodoro* button only when we're in a pomodoro-in-progress state

In order to do that we need to keep track of the state of the timer so we start by modeling the different states available with an object:

```javascript
const State = {idle: 'idle', inProgress: 'in progress', resting: 'resting'};
```

And we'll store the current state of the pomodoro timer in a `currentState` variable:

```javascript
let currentState = State.idle;
```

We then update the different lifecycle methods to update this state as needed:

```javascript
function startPomodoro() { 
  currentState = State.inProgress;
  interval = setInterval(() => {
    if (pomodoroTime === 0) {
      completePomodoro();
    }
    pomodoroTime -= 1;
  },1000);
}

function rest(time){
  currentState = State.resting;
  pomodoroTime = time;
  interval = setInterval(() => {
    if (pomodoroTime === 0) {
      idle();
    }
    pomodoroTime -= 1;
  },1000);
}

function idle(){
  currentState = State.idle;
  clearInterval(interval);
  pomodoroTime = POMODORO_S;
}
```

And now we update our templates to take advantage of this new knowledge to enable/disable the buttons that control the timer:

```html
<section>
  <p>
    {formatTime(pomodoroTime)}
  </p>
  <footer>
    <button on:click={startPomodoro} disabled={currentState !== State.idle}>start</button>
    <button on:click={cancelPomodoro} disabled={currentState !== State.inProgress}>cancel</button>
  </footer>
</section>
```

Awesome!

<img class="center shadow-small" src="https://www.barbarianmeetscoding.com/images/svelte-disable-buttons.gif" title="Buttons are only enabled when it makes sense! Yippi!" alt="A pomodoro app with a timer and a series of tasks. The user clicks on start and the pomodoro timer starts its countdown. Then they click on cancel and the pomodor stops. The buttons are only enabled when it makes sense.">

The full component at this point looks like this:

```html
<script>
  const minutesToSeconds = (minutes) => minutes * 60;
  const secondsToMinutes = (seconds) => Math.floor(seconds / 60);
  const padWithZeroes = (number) => number.toString().padStart(2, '0');
  const State = {idle: 'idle', inProgress: 'in progress', resting: 'resting'};

  const POMODORO_S = minutesToSeconds(25);
  const LONG_BREAK_S = minutesToSeconds(20);
  const SHORT_BREAK_S = minutesToSeconds(5);

  let currentState = State.idle;
  let pomodoroTime = POMODORO_S;
  let completedPomodoros = 0;
  let interval;

  function formatTime(timeInSeconds) { 
    const minutes = secondsToMinutes(timeInSeconds);
    const remainingSeconds = timeInSeconds % 60;
    return `${padWithZeroes(minutes)}:${padWithZeroes(remainingSeconds)}`;
  }

  function startPomodoro() { 
    currentState = State.inProgress;
    interval = setInterval(() => {
      if (pomodoroTime === 0) {
        completePomodoro();
      }
      pomodoroTime -= 1;
    },1000);
  }

  function completePomodoro(){
    clearInterval(interval);
    completedPomodoros++;
    if (completedPomodoros === 4) {
      rest(LONG_BREAK_S);
      completedPomodoros = 0;
    } else {
      rest(SHORT_BREAK_S);
    }
  }

  function rest(time){
    currentState = State.resting;
    pomodoroTime = time;
    interval = setInterval(() => {
      if (pomodoroTime === 0) {
        idle();
      }
      pomodoroTime -= 1;
    },1000);
  }

  function cancelPomodoro() {
    // TODO: Add some logic to prompt the user to write down
    // the cause of the interruption.
    idle();
  }

  function idle(){
    currentState = State.idle;
    clearInterval(interval);
    pomodoroTime = POMODORO_S;
  }
</script>

<section>
  <p>
    {formatTime(pomodoroTime)}
  </p>
  <footer>
    <button on:click={startPomodoro} disabled={currentState !== State.idle}>start</button>
    <button on:click={cancelPomodoro} disabled={currentState !== State.inProgress}>cancel</button>
    <!--button on:click={completePomodoro}>complete</button-->

  </footer>
</section>
```

## Adding Some Styling

Now let's apply some styling to our timer. The timer consists on some text with the timer itself and a couple of buttons. The styles of the timer feel like something that should belong to this component and this component only, but the styles of the buttons sound like something that should be consistent across the whole application.

Styling the timer text is quite straighforward. We just update the styles within `PomodoroTimer.svelte`. While I'm doing this, I remember HTML has a `time` element that is a more semantic way to represent time in a web application and I switch my puny `p` element for `time`:

```html
<style>
  time {
    display: block;
    font-size: 5em;
    font-weight: 300;
    margin-bottom: 0.2em;
  }
</style>

<section>
  <time>
    {formatTime(pomodoroTime)}
  </time>
  <footer>
    <button on:click={startPomodoro} disabled={currentState !== State.idle}>start</button>
    <button on:click={cancelPomodoro} disabled={currentState !== State.inProgress}>cancel</button>
    <!--button on:click={completePomodoro}>complete</button-->

  </footer>
</section>
```

And now, for the buttons, how does one do application-wide styles in Svelte? There's [different options](https://css-tricks.com/what-i-like-about-writing-styles-with-svelte/) but for this particular use case we can take advantage of the `global.css` file that is already available in our starter project. In fact, it already has some styles for buttons:

```css
button {
  color: #333;
  background-color: #f4f4f4;
  outline: none;
}

button:disabled {
  color: #999;
}

button:not(:disabled):active {
  background-color: #ddd;
}

button:focus {
  border-color: #666;
}
```

Let's tweak this a little. We're going to have a primary and secondary action buttons, where the primary action is going to be the start pomodoro, and the rest will be treated as secondary action (we really want to get our pomodoros started). The primary action will use a set of *accent* colors while the secondary action will use a set of *base* colors which we'll define as a color scheme using CSS variables:

```css
:root{
 --black: #333;
 --base: white;
 --base-light: #f4f4f4;
 --base-dark: #ddd;

 --white: white;
 --accent: orangered;
 --accent-light: #ff4500d6;
 --accent-dark: #e83f00;
}
```

Now we redefine the styles for the **secondary action button** which we'll just act as the default look and feel of a button:

```css
button {
  background-color: var(--base);
  border-color: var(--black);
  color: var(--black);
  font-size: 1.5em;
  font-weight: inherit;
  outline: none;
  text-transform: uppercase;
  transition: background-color .2s, color .2s, border-color .2s, opacity .2s;
}

button:disabled {
  opacity: 0.5;
}

button:focus,
button:not(:disabled):hover {
  background-color: var(--base-light);
}

button:not(:disabled):active {
  background-color: var(--base-dark);
}
```

And we define new styles for the **primary action** button which will build on top of the styles above:

```css
button.primary {
  background-color: var(--accent);
  border-color: var(--accent);
  color: var(--white);
}

button.primary:not(:disabled):hover {
  background-color: var(--accent-light);
  border-color: var(--accent-light);
}

button.primary:not(:disabled):active {
  background-color: var(--accent-dark);
  border-color: var(--accent-dark);
}
```

Now to make the inputs fit it with the buttons we'll tweak their font-size:

```css
input, button, select, textarea {
  font-family: inherit;
  font-size: 1.5em;
  font-weight: inherit;
  padding: 0.4em;
  margin: 0 0 0.5em 0;
  box-sizing: border-box;
  border: 1px solid #ccc;
  border-radius: 2px;
}
```

We also update the `font-weight` of our app to be lighter and more minismalistic because why not:

```css
body {
  color: var(--black);
  margin: 0;
  padding: 8px;
  box-sizing: border-box;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
  font-weight: 300;
}
```

We make the add task button in the `TaskList.svelte` component also be a primary button:

```html
<button class="primary" on:click={addTask}>Add a new task</button>
```

And why not? Let's make the title a little bit bigger (I'm getting carried away here). Inside `App.svelte`:

```css
  h1 {
    color: var(--accent);
    text-transform: uppercase;
    font-size: 6em;
    margin: 0;
    font-weight: 100;
  }
```

And that's it! We may need to revisit the styles to make sure the contrast is enough to support great accessibility but this is a start:

<img class="center shadow-small" src="https://www.barbarianmeetscoding.com/images/svelte-new-styles.gif" title="New Styles! Wiho!" alt="A pomodoro app with a timer and a series of tasks. The user clicks on start and the pomodoro timer starts its countdown. Then they click on cancel and the pomodor stops. The buttons are only enabled when it makes sense. The styles have been improved from previous iterations.">

**Sweet!** And that's all for today. In the next part in the series we'll continue with:

* Refactoring our timer with the help of automated tests (because I'm not super happy with the current implementation).
* Adding auditory feedback when the pomodoro starts and ends.
* Integrating the timer with the tasks so we have a full pomodoro technique flow.

> ### Looking for the source code for the pomodoro app?</h3>
> Look no more! You can find it on <a href="https://github.com/Vintharas/il-pomodoro-in-svelte">GitHub</a> ready to be cloned and enjoyed, or on <a href="https://svelte.dev/repl/0ab6485feb3540cc9326716b0e900b19?version=3.16.7">the Svelte REPL</a> where you can tinker with it right away.

## More Reflections

Working with Svelte continues to be very pleasant. In addition to my previous reflections ([1](https://www.barbarianmeetscoding.com/blog/2019/12/27/discovering-svelte-getting-started#some-reflections-so-far), [2](https://www.barbarianmeetscoding.com/blog/2019/12/29/discovering-svelte-adding-some-tasks-and-pomodoros#some-more-reflections-so-far)), I've found that:

* **Formatting functions are very straightforward**. When I needed to format the time in a specific format in my template, I just went with my gut, wrapped the formatting within a vanilla JavaScript function, used it on the template `formatTime(pomodoroTime)}` and it worked.
* **Assigning and binding properties to a DOM element is also straightforward**. Once more, I just went with my gut, typed `disabled={currentState !== State.idle}` and it worked as I expected it to. Principle of least surprise! Yey!
* **Having the styles within a component feels very natural and useful**: There's no need to switch context as the styles are in close proximity to where they're used. If you ever need to update the styles of a component you know where to go, and likewise if you remove a component its styles disappear with it (You don't need to search around your application in a deadly csshunt).