How I built (part 2) – Styling the frontend with CSS3 media queries, transitions, Flexbox and Dark Mode

Developers often wonder how to to get their side-project apps finished, and now that I’ve finished building I wanted to document my process before everything fades from memory. You can read the story of where the idea for the app came from if you’d like some context, as well as part 1 of the series.

Mobile-first responsive design

I decided to make a mobile-friendly interface early into the blueprinting phase, as I anticipated people would want to listen to the career advice podcasts while they were at their desktops as well as on their mobile devices. As mobile interfaces can work on desktops, but desktop interfaces usually do not work on mobile, focusing on a mobile-first design made sure I covered all possible devices. The consequence of this decision is that I would not be able to take full advantage of all of the available space at tablet/laptop/desktop widths, but while this would leave unused space on either side of the interface, at least users would only have to learn a single interface no matter what device they were on. on an iPhone SE, Pixel 2 XL, and iPad

In practice, having a mobile-friendly interface at tablet/laptop/desktop widths meant that beyond a certain width, the interface would lock itself to a fixed-width, emulating a mobile interface on a desktop. Achieving this effect required a very light touch with responsive CSS: Below a specified width, the interface would switch from fixed-width to 100% width. While this is a single line of width-overriding CSS conditionally applied to a root container using a media query, it had the far-reaching implication that every UI element in the interface had to be designed to look and work correctly no matter what the width.

The Scorecard podcast player hides the volume controls at mobile widths.

Applying CSS effects

I wanted to give a modern look loosely inspired by Material Design. I could have used the excellent Material UI React Component library but preferred to build custom components and styles so that I would have full control over both the look and the feel of the app.

Developers new to CSS effects tend to want to demonstrate what they know, and their enthusiasm often leads to app designs that have so many noticeable visual effects that the usability of the app suffers. The key to using CSS effects is subtly; it should not be apparent to the user that you are using CSS effects at all. A user should only be able to detect that you used visual effects if you take them away.

The interface with and without CSS effects.

Visual effects should be complementary and work in harmony with each other. The objective is to arrive at a single, cohesive design where everything is tied together seamlessly. Unfortunately, CSS syntax makes this difficult, as there are no higher-level concepts like “lighting,” “depth,” or “contrast.” Instead, you have to work backward from the design you want into the CSS syntax you need.

A surprising amount of CSS effects were used for such a seemingly simple, clean design.

Making circles connected to lines

I wanted to carry the theme of circles and lines throughout the app, to tie together the homepage, quiz, and Scorecard.

The general steps to creating a circle with a line attached in CSS are:

  1. Give an element equal height and width, a border, and a 50% border-radius.
  2. Create an element with a width and zero height (for horizontal lines) or a height with zero width (for vertical lines) and a border.
  3. Align the circle with the line to make them appear to be connected.
The structure of 1) The “1, 2, 3” on the homepage 2) The bars on the scorecard and 3) The quiz answer interface.

Specifically for, here is how I created each of the circles-with-lines effects:

  1. For the vertical “1, 2, 3” on the homepage, the challenge was making sure the circles were always in the vertical center of their associated paragraph of text no matter how much vertical height the text occupied. I used Flexbox to distribute the circle element and two pseudo elements within a parent container whose height was determined by the paragraph of text. Visually, this created the illusion of three circles connected by two lines, even though there were six lines in total. The very first and very last lines then had their visibility hidden so that they were invisible to the user but still rendered into the document so that Flexbox could still maintain proper alignment.
  2. For the horizontal score-bars on the scorecards, the zero height lines contain the ending circles. The ending circles use absolute positioning so that they do not increase the height of the line, and can be positioned precisely in the vertical and horizontal center of the end of the line.
  3. For the quiz answer selection, the circles and lines are thirteen sibling elements, and Flexbox is used to give them all the same vertical alignment despite them having different heights.

CSS transitions on scorecard bars

There were two situations where I wanted the bars on a scorecard to be animated:

  1. When switching between the twelve “Difficult Developer” profiles
  2. When loading a scorecard

Animating the scrollbars required only CSS transitions, not animations, as I was only animating between two states. The terminology can be a be confusing as you can create an animation using both a “CSS transition” and a “CSS animation”, with the difference between the techniques being the sophistication of the animation you wish to produce: simple animations need only CSS transitions; sophisticated animations require CSS animations.

To achieve the effect I wanted, I needed to transition the colors and width of the score-bar. The line of the bar only needed its border color and width transitioned; the end of a score bar had a circle effect that required the transition of the outer circle’s border as well as the inner circle’s background color. Achieving this effect was conceptually very simple, but I had two options of how I could code it:

  • I could create CSS classes and switch between them
  • I could change the style directly on the element

While CSS classes allowed for a clean separation of visual effect from the React component, the drawback of creating CSS classes is I would need a lot of them: 100 to be exact as the score-bar represented score as a percentage. The side effect would be an increase in the CSS download size, and it was technically not needed as the transition would work just as well if I manipulated the style directly on the element. The difference between triggering the animation between the two effects was only very slight:

  • Using CSS classes, I would change the class name when the data changed
  • Using CSS style, I would change the style attribute of the individual elements to change the bar color, bar width, end circle border color, and end circle border background-color.

I decided to use CSS classes for the following reasons:

  1. While there is quite a lot of debate around coupling style to components, I believe that style should be separate from content in order to retain the option for a wholesale redesign of the app many years into the future.
  2. By the very nature of the CSS class repetitiveness, when compressed by the server, gzip tokenization would dramatically reduce the download size of the 100 class definitions.

The 2nd point was only an assumption at the time I made the decision, but for the sake of this article, I ran a test to see if my assumption was correct. The result was that the SASS loop created 23KB of CSS, which gzipped down to 1KB. Is 1KB of download worth separating style from implementation? Considering that the entire CSS bundle is 8KB compressed it’s a significant increase proportionally, but the practical reality is that on a typical connection the extra KB would be transferred in a few milliseconds. If I were designing the interface for Amazon’s level of traffic, I would have elected to save the 1KB. As it stands, the extra network transfer cost for the anticipated peak traffic for would amount to the cost a cup of coffee a month.

The SASS code that generated the 100 classes used for the score-bar transitions.

Dark Mode

Early in the design process, the interface for had a dark color scheme. An early reviewer suggested a change to a more corporate color scheme, but I resisted as I preferred the look of an IDE/Editor more than that of a corporate training solution. Ultimately, near the end of the blueprinting process, I decided that the reviewer was correct and switched from a dark color scheme to a light color scheme.

About a week after I launched I learned that the new version of iOS was implementing a Dark Mode, and immediately regretted using a light color scheme. After some reflection, I realized that what I wanted was a color scheme that reflected the preference of the user, but I was not aware of any way to achieve this in a web browser. I did a bit of Googling, and soon found the new “prefers-color-scheme” CSS media query which was precisely what I needed. Unfortunately, the prefers-color-scheme media query was supported only in the very latest Safari for Mac OS, and an upcoming version of Chrome. Regardless, I applied the necessary CSS overrides to create support for Dark Mode using prefers-color-scheme, and now switches its color scheme based on the user’s preference.

If a user’s browser supports the new media query ” prefers-color-scheme” they will see the dark version of if their OS is set to Dark Mode.

Part 3 of the series will cover writing the backend in Node, Express, Knex, and Postgres.

Add your thoughts