<![CDATA[Tinacious Design Blog]]>https://blog.tinaciousdesign.comhttps://cdn.hashnode.com/res/hashnode/image/upload/v1596603351597/vcR5psTkx.pngTinacious Design Bloghttps://blog.tinaciousdesign.comRSS for NodeThu, 27 Jan 2022 17:09:37 GMT60<![CDATA[Simple Event Bus in Android using coroutines and flows]]>In this post I will describe a simple event bus pattern in Android that is implemented using coroutines and flows. Thanks to Kotlin, coroutines, and flows, this is a simple approach that requires very little code.

What is an Event Bus?

Event bus is a pattern that can be implemented in any programming language. An Event Bus is simply a class that implements this pattern.

In Android, there are libraries that have named themselves "EventBus," but it is not a library by default, it is a pattern that can be implemented quite easily without the use of any libraries.

The event bus approach and pattern is a tool that can be leveraged in an event-driven application. Event-driven applications are reactive and react to events as they occur.

One of the benefits of using the event bus pattern is to simplify your application architecture and decouple components that may need to know about each others' data. So, rather than injecting components into each other—which is not very scalable as your app grows—components can emit events and other components can subscribe to these events. This means that your components can instead depend on a shared EventBus class, to which they can publish and subscribe events, rather than on the components themselves that would be required to get this information.

When should I use an Event Bus?

You can use an Event Bus when you need to notify the rest of the app that something has happened, without worrying about all of the dependencies, the hierarchy of your app, or the lifecycles.

Some examples of events that may be appropriate to use the event bus pattern for:

  • internet connection is lost or regained
  • the user logs out of the app
  • a long-running task has completed in the background and other components may want to be notified of this task completion
  • data is updated in the background and other components may want to know when this data is updated so that they may re-fetch it

You can use events for pretty much anything but they may not make sense for everything.

When should I NOT use an Event Bus?

An Event Bus is not a one-stop solution—it is a tool that is useful in some cases but may not make sense in other cases.

If you have a view that, on mount, fetches data, and you want the view (fragment or activity) to be aware of this data once it's fetched, an event bus in Android is not the best approach for this. For this, I would instead recommend using MVVM (Model - View - View Model) architecture, and implementing a LiveData observable. With LiveData, you can observe changes to this data, so that this data (once changed) can be updated in the view.

The event bus pattern is not meant to replace Live Data and observables, but rather complement it. You can use both at the same time, if necessary. It is very valid to have both an event bus and live data observables in an application as they have different use cases and value.

A simple example EventBus implementation

EventBus class

The following example EventBus class uses Kotlin coroutine flows to provide events to subscribers. You can learn more about coroutines in Android here. Here is the example EventBus class:

import android.util.Log
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow

class EventBus {
    private val _events = MutableSharedFlow<AppEvent>()
    val events = _events.asSharedFlow()

    suspend fun emitEvent(event: AppEvent) {
        Log.d(TAG, "Emitting event = $event")

    companion object {
        private const val TAG = "EventBus"

In the above code sample, AppEvent is a simple enum. This approach would keep your event bus simple. You may want to instead use a sealed class so you can pass data alongside this event, though I would probably limit the complexity of the event bus by just emitting simple events and having subscribers do what they want with that data. This may or may not be the simplest or most practical approach in your application, so consider your app's needs.

It is important to have a single instance of the event bus so that consumers are publishing and subscribing to the same event bus. In most cases, this EventBus would be provided as a singleton using your dependency injection framework of choice, like Koin or Dagger Hilt, both of which are out of scope for this article.

Publishing events

Publishers who want to publish events would call the emitEvent method with the desired event on the EventBus instance:


Subscribing to events

Subscribers who want to listen to events would collect the emitted events on the flow. This implementation could look something like this:

    .filter { it == AppEvent.CONNECTION_RECONNECTED }
    .collectLatest { handleReconnection() }

Caveats with the Event Bus approach

While an event bus is a useful tool, like anything, it can be abused. An application's state can quickly get out of hand if every component is using the event bus for everything. It could get to a point where events are being fired unexpectedly, causing unexpected side effects in your app.

Due to this, it may make sense to only use such an approach for events that need to be global in nature (e.g. network connection is dropped or connected, the user logs out, etc.), and stick to conventional MVVM architecture for most of your app's data.


In summary, the event bus pattern is a useful approach to reactive programming but is not a solution for everything. In most cases, LiveData observables may solve your problem, but in some cases you may want to leverage an event bus to react to events that are more global in nature as they occur.

https://blog.tinaciousdesign.com/simple-event-bus-android-coroutines-flowshttps://blog.tinaciousdesign.com/simple-event-bus-android-coroutines-flowsTue, 24 Aug 2021 22:51:52 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1629845318429/VPkBokOVJ.png
<![CDATA[Covid Today Android app]]>Close to the beginning of the pandemic, I made a React app that displayed COVID-19 data per country.

This is the Android version. Built in native Android with Kotlin, I developed it as an opportunity to build something with some newer Android API's including LiveData, ViewPager2, and coroutines.

You can see the source code on Github or download the app on AppCenter.

Demo time

Demo screencast of the Covid Today Android app

Covid Today Android app screenshot

Covid Today Android app screenshot

Covid Today Android app screenshot

https://blog.tinaciousdesign.com/covid-today-android-apphttps://blog.tinaciousdesign.com/covid-today-android-appSun, 03 Jan 2021 06:35:23 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1609655683693/3CJs8ulF1.jpeg
<![CDATA[How I made my keyboard flash red when the build breaks on CI 🚨]]>I recently got a Steelseries keyboard. Other than that this keyboard was one of the only RGB keyboards I could find that was supported on Mac, it had an extra draw, which was that it was programmable.

Steelseries offers their GameSense SDK which has a REST API you can use. This sounded simple enough, so I decided to create a dream for myself:

As a developer, when I push up code that breaks the build on CI, I want my keyboard to flash red

So now I had a new side project to waste my time on!

Here's a picture of the keyboard display showing a message for the build status. This one is for successful builds.

Build success on the Steelseries keyboard display

Here's a picture of the display when builds fail.

Build fail on the Steelseries keyboard display

The function keys also flash either red or green, which you can sort of see in the demo video below.

This app leverages Codeship as the continuous integration service. I like Codeship for this purpose because it allows you to configure a web hook for the project and doesn't require you to modify any Yaml files in your source code to get this to work, you can just set it up in the dashboard without making a scene in your team's code. I initially looked at Github Actions and then Circle CI but neither of those had a setup as quick and easy as Codeship's.

I also used ngrok to create a local tunnel so that Codeship had a public URL to hit.

How the Steelseries GameSense SDK works

When you install the Engine software, a server is created on your machine. Steelseries keyboards listen for events on this port, e.g. localhost:<port>.

Steelseries also offers official SDK's for game engines like Unity. There was an unofficial Node.js client, which I tried first, but unfortunately it didn't run and threw a bunch of errors when implementing the example code, so I needed to build my own solution.

Demo time

If this sounds interesting, watch this demo to see it in action.

Get the code

If you like what you see, get the code on Github. The readme includes a tutorial on how you can set this up for yourself. Feel free to fork it. If you add any cool features, I'd love to see them!

https://blog.tinaciousdesign.com/how-i-made-my-keyboard-flash-red-when-the-build-breaks-on-cihttps://blog.tinaciousdesign.com/how-i-made-my-keyboard-flash-red-when-the-build-breaks-on-ciTue, 29 Dec 2020 08:31:21 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1609229338415/uR6aVa8mC.jpeg
<![CDATA[Saving to photos in Android Q+ (Android 10+)]]>As of Android 10, file access permissions have changed. In a previous blog post Migrating from getExternalStoragePublicDirectory in Android 10 & 11, I moved from the legacy storage to a simpler intent-based approach that opened the native share sheet. While this solved the problem in a different way than the original implementation, it changed the functionality—instead of writing directly to disk, I created an intent with the file data and the user chose where to put it. While this was a better solution for that app that dealt with JSON files, it's not a great solution for apps that deal with images.

I came across this problem again in a different app, and it was time to solve it the right way.

Saving to photos before Android 10

Before Android 10, you could save to photos by writing to the following directory:


As of Android 10, this approach has been deprecated. You no longer have access to folders directly in this way. You could still use it for a little while though with the requestLegacyExternalStorage flag in your manifest, but this is a bandaid solution, and as soon as you upgrade your app to target Android 11, this will no longer work, which means you'll need to eventually overhaul your storage implementation. Targeting Android 11 will become mandatory in August 2021, giving developers about 1 year to migrate from the legacy storage implementation.

Share sheet for images

Sharing via the share sheet on Android is the easiest approach, and it's the one I outline in this blog post, but it's a cumbersome experience for some users.

On Pixel devices, Google Photos is the default photos program. When you want to see the photos on your device, you open Google Photos. There is no difference between Google Photos and your device photos anymore.

On other (non-Google) Android devices, this isn't the case, and many other device manufacturers have their own version of a gallery app. Unfortunately, some of these apps are not available as an option in the native share sheet when creating an image share intent. The OnePlus Gallery app, for example—which ships with One Plus devices—doesn't show up as an option to share to. This creates a cumbersome experience for these Android users who just want to save an image to their phone.

Problem: the go-to React Native plugin doesn't work on Android 10+

The React Native CameraRoll plugin, which works well enough for iOS, unfortunately doesn't work for recent Android versions. There are a few issues and/or pull requests open for this topic: #244, #219, #268, #248, #271 to name a few.

The problem I was solving was quite unique and had multiple layers: React Native app with an embedded web view, and the image is an export of an HTML5 canvas, which provides the image data as a base64 encoded string. To solve this problem on Android, I needed 2 separate plugins: the above-mentioned react-native-cameraroll plugin for iOS, as well as the React Native Share plugin to share with the native share sheet on Android since saving to photos didn't work. While this allowed me to get the image to the user to some degree, it wasn't the ideal solution for my needs.

Solution: Implement scoped storage for Android Q+ with legacy storage fallback

Because the React Native community didn't have a solution for my needs, I decided to develop one. While the react-native-cameraroll plugin sort of worked on iOS, it wasn't perfect—it would crash the app if the user had declined to share access to their photos.

I ended up developing the plugin I called React Native Save Base Sixty Four. The naming is unfortunately quite verbose but numbers in package names get marked as spam by NPM and prevent publishing.

The plugin I developed solves the following problems, which are limited in scope to an image that is in the format of an encoded base64 string.

On iOS:

  • Enable iOS users to save a base64 image to their photos
  • Allow the React Native app to handle the case that the iOS users decline to grant permissions to photos

On Android:

  • Enable users on Android 10 and higher to save a base64 image to their photos on their device
  • Enable users on Android 9 and older to save a base64 image to their photos on their device
  • Allow the React Native app to handle the case that the user declines to grant permissions to photos

The plugin also allows user to share a base64 image via the native share sheet on both platforms, but if that's all you need to do, I would recommend you use the React Native Share plugin as it's more mature and is supported by the community and handles base64 strings perfectly. I added this functionality to my plugin because it was simple enough, and I could use the same plugin for both platforms and uninstall the other two from my project.

How to use this plugin

The full instructions can be found in the project readme, including instructions for how to modify your Android manifest and iOS Info.plist file, but here's a snippet. It uses the promises API, which can be implemented with a set of .then().catch() blocks or async await.

import SaveBase64Image from 'react-native-save-base-sixty-four';

// Save to device
//    with async await
try {
  const success = await SaveBase64Image.save(base64ImageString, options);
  if (!success) {
    // 😭 user did not grant permission
} catch (error) {
  // 💥 there was a crash

//    with promises
  .save(base64ImageString, options)
  .then((success) => {
    if (!success) {
      // 😭 user did not grant permission
  .catch((error) => {
    // 💥 there was a crash

The linked readme also includes further instructions on how to implement permission checking on Android. Legacy versions of Android require you to prompt the user when you run the app, while Android 10+ does not because access is scoped to files you create. Older versions of Android will crash if you try to access something you don't have permission to access. This snippet is a handy utility function for React Native apps to check Android permissions. It depends on the react-native-device-info plugin. The example app in the plugin I developed has an example of how to implement the plugin with permission checking.

Implementing scoped storage for Android 10+

I have provided some links below in the Further reading section to specifics on implementing scoped storage. The TL;DR is as follows:

  • Convert your base64 encoded string into a Bitmap. For this, you can use Android SDK classes Base64 and BitmapFactory to accomplish this task. Here is the source.
  • Implement the ContentResolver API to create an output stream and pass the bitmap to that stream.

The full source code can be viewed on the Github page but most of it is in this file. The proof of concept app is available for download on AppCenter.

Gratitude 🙏

I must give a huge thank you to the team that built the bob tool for scaffolding React Native modules as it was a pleasure to work with and saved me a huge amount of time.

Further reading

https://blog.tinaciousdesign.com/save-to-photos-gallery-android-10-android-qhttps://blog.tinaciousdesign.com/save-to-photos-gallery-android-10-android-qMon, 28 Dec 2020 00:42:55 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1609116157292/GoVsI0c2p.png
<![CDATA[Migrating from get External Storage Public Directory in Android 10 & 11]]>Android 11 introduces breaking changes to working with files on Android. One of these breaking changes includes the deprecation of the following method:


The Canned Replies Android app used to use this deprecated API. As of Android 11, both importing and exporting features broke. 😬 This article will go through the solution I used to fix these bugs.

Why did Google deprecate get External Storage Public Directory?

In the Android documentation, Google mentions that access to the user's shared and external storage directories is deprecated as of API level 29 for privacy reasons.

Screenshot of deprecation notice

What should I do instead?

The documentation makes a few suggestions as to which alternative API's to use instead. Some suggestions include MediaStore and getExternalFilesDir, which may not always be available.

You may, however, be able to get away with solving this problem with a much simpler approach by using the intent system.

Do users access these files?

Does your app read and write private files to disk, e.g. for caching reasons? Or will the user specifically be involved in the process?

If you are creating features like allowing the user to import or export their data using files, then you can do this with using the intent system. You don't actually need permissions to write to any storage directories and can do so via intents.

Using Intents to read and write files

You can uses the Intent API to read and write files in Android. This API implementation has 2 steps:

  1. Creating your intent to perform an action, e.g. get a file
  2. Handling the system's response to your intent, i.e. success and failure cases

This blog post will go through the example of reading and writing JSON files. This blog post code is written in Kotlin.

Reading files using Intents

To read files using Intents, we can prompt the user to pick files and then we can perform actions with those files.

This example will read a file of any type.

Creating our intent to read a file

First, we need to create an intent to read a file. We can attach this to a user action, for example, the tapping of a button or menu item. Once the user interacts with our view, we can execute requestImportIntent() which is implemented as follows:

private fun requestImportIntent() {
  val pickIntent = Intent(Intent.ACTION_GET_CONTENT)
  pickIntent.type = "*/*"
  startActivityForResult(pickIntent, INTENT_IMPORT_REQUEST)

The above code is creating an intent for ACTION_GET_CONTENT which allows the user to pick a file of any type.

Then, we start an activity for request with the request code stored in the INTENT_IMPORT_REQUEST, which is an Int value.

Once this intent activity is created, the user will be prompted to provide a file. This will allow the user to browse their file system and choose the file they want to provide.

Responding to our intent to read the file

To respond to this intent, we need to implement the override method onActivityResult. We can use the following code, which only cares about successful results, i.e. Activity.RESULT_OK:

  super.onActivityResult(requestCode, resultCode, data)

  if (resultCode != Activity.RESULT_OK) {

  when (requestCode) {
    INTENT_IMPORT_REQUEST -> handleIntentImport(data)

When the requestCode is for the intent we assigned to INTENT_IMPORT_REQUEST, then we should handle it. I've created a method handleIntentImport(data) which takes the nullable Intent data:

private fun handleIntentImport(intent: Intent?) {
  val importedData: Uri = intent?.data ?: return

  try {
    // Do things with imported data
  } catch (e: IOException) {

The above code allows us to read the data from the intent, which may be null. If it's null, we make an early return out of the function, otherwise we try to perform our actions on the file data.

Writing files using Intents

Similar to reading files with Intents, we can also write files with Intents. The following example will write a JSON file to the user's desired destination. It will allow the user to name the file.

Creating our intent to write a JSON file

We need to create an intent to write to a file. The following code will allow us to prompt the user to choose where they'd like the document we create to be written:

private fun requestExportIntent() {
  val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
  intent.type = "application/json"
  intent.putExtra(Intent.EXTRA_TITLE, generatedFileNameWithTimestamp)

  startActivityForResult(intent, INTENT_EXPORT_REQUEST)

In the above code, we first create an intent for ACTION_CREATE_DOCUMENT and specify that the category is openable. We need to add the category CATEGORY_OPENABLE in order to be able to have full control over the file via the ContentResolver API's.

Specifying the right type is required to ensure that the file has the right file extension. For example, to ensure we have the .json file extension, we need to set the type to application/json.

We can also provide a default file name. In this case, I'm using a generated file name with a timestamp. The user will be able to change this.

Next, the user will choose the location and the file name. Once they successfully create the file, we will be able to respond to the intent.

Responding to our intent to write the file

Similar to our read intent above, we will need to implement the override method onActivityResult. The following code has both the existing read intent code and the new code added:

  super.onActivityResult(requestCode, resultCode, data)

  if (resultCode != Activity.RESULT_OK) {

  when (requestCode) {
    INTENT_IMPORT_REQUEST -> handleIntentImport(data) // old

    INTENT_EXPORT_REQUEST -> handleExportIntent(data) // new ✨

Given that we get a successful export intent request (i.e. the user has chosen a directory and filename), we can handle that in our handleExportIntent method:

private fun handleExportIntent(intent: Intent?) {
  val uri = intent?.data ?: return

  val outputStream = contentResolver.openOutputStream(uri)
  val exportText = myStringifiedJsonData

  if (exportText == null || outputStream == null) {

  try {
  } catch (e: Exception) {

We can now use the Content Resolver API's to write to this data URI using an output stream.

The example above of myStringifiedJsonData is just a serialized list of POJO's (Plain Old Java Objects). So I serialize the data as JSON, write it to the file, and close the output stream.

Benefits of using intents

  • No permissions required
  • User has full control over the files

No permissions required

What's great about using the intent system is that no extra permissions are required. You do not need access to read and write to storage if you are using intents.

If Google starts cracking down on permissions for Android like they did with the Chrome Web Store, we may find ourselves with more Play Store rejections for requesting permissions we may not need.

User has full control over the files

Unlike when working with getExternalStoragePublicDirectory, your users can choose where the file goes instead of you choosing a default directory. This is more user-friendly as it would allow the user to put it somewhere specific so they can retrieve it later.

Your users will also be able to name the file something sensible, while still allowing your app to provide a default file name.

Be intentional

Be intentional, spiritually and in an Android way. 🙏 In my opinion, the Intent experience is one of the best features of Android. Nowadays, Apple has caught up and offers something similar with iOS.

When using intents, you can use patterns that Android users are already familiar with instead of coming up with creative solutions to read and write to disk.

That said, intents will not work for all use cases, but if users are interacting with these files, it may be a better user experience to use intents rather than to write to specific directories that you choose.

https://blog.tinaciousdesign.com/migrating-from-get-external-storage-public-directory-in-android-10-and-11https://blog.tinaciousdesign.com/migrating-from-get-external-storage-public-directory-in-android-10-and-11Sun, 04 Oct 2020 02:02:18 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1601776107675/35OFGejg2.png
<![CDATA[Generate a Vue component using Plop]]>Plop is a handy tool for code generation. It uses Handlebars under the hood alongside some provided Handlebars helper functions.

By using a code generation tool like plop, you can avoid writing tedious boilerplate code and be productive right away. It's also great for maintaining consistency across the project and company.

Updated 4 Nov 2020: Added template for Vue 3 syntax.


Create a Vue project

To make sure you're set up to generate components using Plop, you'll need to have a Vue.js project. You can generate one with Vue CLI or with Nuxt.

Add Plop as a dependency

Run the following to add Plop as a dependency:

npm install --save-dev plop

Add an NPM script

Add a command to call plop from an NPM script, e.g.

  "name": "example-project",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "start": "nuxt-ts start",
    "plop": "plop"

Create a Plopfile

Create a file named plopfile.js in the root of your project. Put the following code in it:

// plopfile.js
module.exports = function (plop) {
  plop.setGenerator('component', {
    description: 'Generates a Vue component',
    prompts: [
        type: 'input',
        name: 'component',
        message: 'Component name, e.g. NatureBanner'
    actions: [
        type: 'add',
        path: 'components/{{pascalCase component}}/{{pascalCase component}}.vue',
        templateFile: 'plop-templates/component.hbs'

Create a plop template

You'll notice the above plopfile references a template file plop-templates/component.hbs. You will need to create this file.

Vue 2

  <div class="{{kebabCase component}}">{{pascalCase component}}</div>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: '{{pascalCase component}}',
  props: {}

<style lang="scss">
.{{kebabCase component}} {}

Vue 3

For Vue 3, we don't need to import or extend Vue:


<script lang="ts">
export default {
  name: '{{pascalCase component}}',
  props: {}

<style lang="scss">
.{{kebabCase component}} {}

These are the differences:

Diff patch of changes between Vue 2 and Vue 3

Diff word changes between Vue 2 and Vue 3

Above diffs generated with TextDiffer.

Note: If you're using Nuxt (or any project set up to use Prettier), you will likely need to disable the "helpful" Prettier JS formatting to avoid destroying your Handlebars file.


Once you've added all your files, your project should look something like this:

├── package.json
├── plop-templates
│   └── component.hbs
└── plopfile.js

1 directory, 3 files

Run your script

Now all you need to do is run your script to generate a component:

npm run plop

You will be prompted for the component name. Enter it.

You should now have generated a component in the project's component directory:

├── Logo.vue
├── NatureBanner
│   └── NatureBanner.vue
└── README.md

1 directory, 3 files

If you open the file, you should see a Vue component that uses Sass and TypeScript was generated.

  <div class="nature-banner">NatureBanner</div>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'NatureBanner',
  props: {}

<style lang="scss">
.nature-banner {}

You can see an example without Sass or TypeScript here.

https://blog.tinaciousdesign.com/generate-a-vue-component-using-plophttps://blog.tinaciousdesign.com/generate-a-vue-component-using-plopSun, 20 Sep 2020 22:33:50 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1600640696764/StDI9cxZA.png
<![CDATA[Use BEM for scalable and maintainable CSS]]>BEM CSS stands for Block Element Modifier, which is a CSS methodology to help you write maintainable CSS.

Here's BEM's official website but keep reading for a brief overview.

BEM Basics

BEM takes a component-based approach to CSS development. It has some strict rules about inheritance and how to write your CSS selectors.

Rules for Inheritance

To follow BEM properly, you should not use inherited CSS styles in order to style your components.

Some CSS properties are inheritable like font- and table-related properties, but most are not (more info).

In order to follow this rule, you should set inherited properties explicitly, where applicable. So, for example, if you are building a card component but the whole card has a smaller font size, all text elements should have their font sizes specified explicitly rather than inheriting the smaller font size from the parent.

This rule exists in order to prevent bugs that can occur due to the cascading nature of CSS, i.e. when inheritable styles are added or removed.

Rules for Selectors

The most important rule is that you should not nest your selectors. The reason for not nesting your selectors is so that you do not have specificity wars.

What's a specificity war, you ask? Remember when you tried to apply a CSS style and it didn't work? And then you had to go digging in the dev console to find out why? Sometimes it was because you specified your style before the style that was applied, but more often than not it was because the style was declared with a combination of classes and HTML element selectors, which resulted in a higher level of specificity.

Some of the most popular CSS frameworks (like Twitter Bootstrap, Material UI, Semantic UI) have this problem, which is why it can be cumbersome to use them when working with an actual UI designer.

The following selector does not obey BEM for multiple reasons:

  • because it nests selectors
  • because it uses an element selector h2

Here's the bad stuff you should not copy:

/* bad */
.page h1 {
  font-size: 2.5rem;

.page h2 {
  font-size: 2rem;

The following is how you can do the same thing using BEM.

/* good */
.page__title {
  font-size: 2.5rem;

.page__subtitle {
  font-size: 2rem;

You'll notice some underscores there, more on that later. For this, see the Element section.

Following the selector rule in BEM means you can end up with more verbose HTML because it would require you to add a corresponding CSS class to everything you want to style. This can make tables more verbose, for example, but it means that in the event you need to have a table that's styled differently, you won't have to have any CSS specificity wars.

Anatomy of BEM

Essentially, the BEM syntax looks like this:


B – Block

Blocks are the main component. If you've worked with React or Vue, you can break up your components the same way in your CSS.

You can also nest blocks inside of other blocks, if needed, but this is generally avoided.

A block can be as simple as a single HTML element, like a button, or it can be as complex as a set of them, like a card with an image, subtitle, excerpt, and a button.

Here is a simple block:

<button class="btn">
  Click me

Here is a more complex block, which could be a card view for a blog where a single card describes a single blog post:

<div class="card">
  <h3 class="card__title">
    How do I use BEM CSS?
  <div class="card__excerpt">
    BEM CSS stands for Block Element Modifier, which is a 
    CSS methodology to help you write maintainable CSS.

  <a class="btn btn--secondary" href="/path/to/blog/post">
    Read More

In the above example, the following are blocks:

  • card
  • btn

E – Element

A block can contain elements, but doesn't have to.

Though you can nest blocks inside of other blocks, you can only nest elements inside of blocks and you cannot nest elements inside of other elements. This is to maintain a flat CSS structure.

In the above card HTML, the following are elements:

  • card__excerpt
  • card__title

M – Modifiers

Modifiers are used to modify either blocks or elements. They are used to demonstrate state or variants.

They are used in addition to block and element classes, not in place of them.

Possible states:

  • active
  • disabled

Possible variants, e.g. for button variants:

  • primary
  • secondary
  • small

In the above card HTML, the following are modifiers:

  • btn--secondary

Here is how you would style buttons using BEM:

<button class="btn btn--primary">
  Click me!

<button class="btn btn--primary btn--disabled">
  Nope, can't touch this!

The modifier modifies only the relevant part that needs to be modified. If you had a button that was blue by default and grey when it was disabled, then you would only change the background colour:

.btn {
  padding: 10px 20px;
  font-size: 1rem;

.btn--primary {
  background: blue;
  color: white;

.btn--secondary {
  background: green;
  color: white;

.btn--disabled {
  background: grey;
  color: white;

Generally, I list my state modifiers after my theme modifiers. To avoid ambiguity or any specificity or inheritance issues, if a modifier needs to override another modifier, I will explicitly set all properties, e.g. background and color in this case.

Combining modifiers

Combining modifiers can be a bit tricky, and it seems like everyone on the internet has their own opinion for how to do this. Many of the posts I've found have produced barely-legible CSS.

Given that our primary button is blue, and our secondary button is green, let's assume that the designer said that the background for a disabled primary button should be dark blue, and the disabled secondary button should be dark green. Not only that, but disabled buttons should be at 50% opacity.

Here's an example mockup:

Mockup of primary and secondary buttons. The first row is the default style, the second row is the disabled state where the background is darker and the buttons are transparent

Option 1: Break the specificity rule

We can still keep the same HTML:


As for the CSS, this is where it can get complicated. It may make sense to skip strict BEM rules and do something like this:

.btn--primary.btn--disabled {
  background: darkGreen;
  color: white;
  opacity: 0.5;

.btn--secondary.btn--disabled {
  background: darkGreen;
  color: white;
  opacity: 0.5;

This technically breaks the specificity rule, which means that the disabled- and variant-related classes are combined as a single selector, making them more specific than other selectors.

Option 2: Write a joined modifier

Instead of having a single --disabled modifier, you can have one for each variant you need to override.

The HTML could look like this:

<button class="btn btn--primary--disabled">

And the CSS like this:

.btn--primary {
  background: green;
  color: white;

.btn--primary--disabled {
  background: darkGreen;
  color: white;
  opacity: 0.5;

Doing this won't require you to break any rules, but it may add some complexity when writing your modifiers.

When you use only one modifier class for each style, you don't need to worry about the order that you declare your classes in the HTML. Unfortunately, with this approach, you'll need to remember which order your modifiers are written in, or duplicate the order.

That means you need to remember that you put --disabled after --primary, and not vice-versa, or you'll have to duplicate the CSS with the flipped version:

.btn--primary {
  background: green;
  color: white;

.btn--disabled--primary {
  background: darkGreen;
  color: white;
  opacity: 0.5;

That's not so bad with just 2 but if we added a third, you can see how this could exponentially get out of hand. This is why I, personally, would probably break the selector rule.

Example: Coffee Menu

Let's assume you have a coffee menu design that the designer gave you.

Coffee menu mock-up of a table with a brown border, coffees for sale, and their prices

Here is an example of non-BEM HTML:

<!-- bad: does not follow BEM -->

      <td>Long Black</td>

With the non-BEM version, we could've styled the table like this, but it would've made assumptions about all tables. This is a common problem when using a CSS framework that provides a lot of opinionated styles for you:

/* bad */
table {
  border: 4px solid brown;

table th {
  font-weight: 700;

table td {
  padding: 8px;
  font-style: italic;

table td:last-child {
  font-weight: 700;
  font-style: normal; /* we need to override the default here 😞 */

The CSS is nice and short and this works perfectly fine if we have only one table style, but as soon as we have different table styles, things start to get crazy.

Also, it's not very legible why the styles are like that:

  • why is the last child bold?
  • why does the last child have their font style modified to normal, which is the default?

Here's the same example using BEM:

<!-- good -->
<table class="coffee-menu">
      <th class="coffee-menu__heading">Coffee</th>
      <th class="coffee-menu__heading">Price</th>

      <td class="coffee-menu__cell coffee-menu__cell--item">
      <td class="coffee-menu__cell coffee-menu__cell--price">
        Long Black

You'll notice that there's a lot more HTML due to all of the CSS classes that are applied. Each td has 2 classes, one as the base class and one as the modifier.

Here's how we would write the CSS of the BEM-friendly version:

.coffee-menu {
  border: 4px solid brown;

.coffee-menu__heading {
  font-weight: 700;

.coffee-menu__cell {
  padding: 8px;

.coffee-menu__cell--item {
  font-style: italic;

.coffee-menu__cell--price {
  font-weight: 700;

With the BEM-friendly example, we can read the CSS and understand it better. We can see that the price is bold, and the coffee menu item is italic. We don't have to refer to the HTML to see which element is the last child of the table.

So, while the HTML and CSS are more verbose, they provide more information, ultimately making both more legible.

We write code for people, not for computers. You'll thank yourself later as your projects grow.

https://blog.tinaciousdesign.com/bem-css-scalable-maintainablehttps://blog.tinaciousdesign.com/bem-css-scalable-maintainableSun, 13 Sep 2020 03:58:00 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1599969398451/q7boFAr4C.png
<![CDATA[Building a macOS Touch Bar app using interprocess communication in Electron]]>In this post, I will go through the process of building a feature in a macOS desktop application that uses Electron. This feature will implement the native macOS Touch Bar API's.

Feature we need to build

I have a desktop application called TextDiffer which allows you to see the difference between two snippets of text. The app will hopefully be published soon and is currently awaiting for approval from the Mac AppStore. 🤞 Update it's available now.

The TextDiffer app has a button in the UI that says "Perform Diff" which will allow you to run the diff checker. As a user, I would like to access this action using a button in my macOS Touch Bar that says "Perform Diff."

In TextDiffer, I am also able to configure my UI theme between light and dark mode. Depending on the theme I've chosen, the "Perform Diff" button will be a different colour: orange for light mode, and purple for dark mode. As a user, I would like the colour of the "Perform Diff" button in my macOS Touch Bar to be the right colour depending on my theme.

Light and dark variants of the Perform Diff button


Before getting started, you will need to understand JavaScript and how to develop basic web apps using any (or no) framework or library.

You should also be familiar with Node.js.

Having a basic understanding of how Electron works helps too.


For this exercise, we will only be using the standard Electron API's including TouchBar, IPC Main, and IPC Renderer.

IPC Basics

Let's go over some basics for interprocess communication in Electron.


There are two processes that we need to concern ourselves with. If you're familiar with Electron app development, or with developing native mobile apps, you are likely already familiar with these concepts:

  • Main – the main process is the one that runs in Node.js. This is the layer that has access to the native desktop API's via corresponding Electron API's.
  • Render – the web process. This is the one that runs in the client-side web app of your Electron application. This can be an app written in Vue, React, Angular, or simply vanilla JavaScript.

About message passing

In order for the main process to communicate with the render process, and vice-versa, these processes will need to send messages to each other.

Messages in Electron have two parts:

  • the channel
  • the message data

The channel is a string value and represents the name or topic of messages. Messages are sent on a channel. In this application, I have set up two separate channels:

  • main-actions – the channel I use to send messages to the main process
  • render-actions – the channel I use to send messages to the render process

Message passing uses an event-based/observables approach, allowing you to send messages with data to a channel, and to listen for messages and subscribe to a channel, and subsequently perform actions in response to messages you receive.

You can set up your channels as you like—one single channel, one channel per action, one channel per process where actions are shared, etc.

Though I am using two separate channels—one for each process—you may be able to use a single channel to communicate both ways. I have not tested this, but if you have, please let me know in the comments how it turned out!

Listening for messages

Electron allows you to listen for events on the main process via the ipcMain module.

// main.js
const { ipcMain } = require('electron');

ipcMain.on('main-actions', (event, data = {}) => {
  // Do things on the main process

Similarly, Electron allows you to listen for events on the render process via the ipcRenderer module.

// render.js
const { ipcRenderer } = window.require('electron');

ipcRenderer.on('render-actions', (event, data = {}) => {
  // Do things on the render process

Important: You may have noticed that I am using require differently for each of these processes. While the main process uses the standard Node.js require, the render process does things differently. This is because Electron requires that you use require on the window object in the render process. It's a good idea to check the existence of it ahead of time so that no exceptions are thrown—this is useful if you build your JavaScript app as a standalone web app initially and integrate it with Electron afterwards, in which case window.require would not be present in your standalone web app.

Structuring the message data

Messages you send can have any shape. The message data is the second argument in the callback. In my example code above, I've called that argument data and I've defaulted it to the empty object {}. Your data could just be a string if you wanted, or a number, or any value. I use an object so that I can pass multiple values if needed. I'm doing this because I will be sending multiple types of messages on each of these channels.

I like to structure my message data similar to how Redux actions are structured:

  • each action has a type property which is a string that I can use to switch/case on
  • additional properties are custom, based on the action

While a simpler action may just have a type property and no additional data, a more complex action would have a type property and whatever data is required to perform that action.

const performDiffAction = {
  type: 'performDiff'

const initTouchBarAction = {
  type: 'init_touch_bar',
  theme: 'dark'

There is no requirement as to what format your type property should follow. In Redux, the community as a whole is not unanimous between camelCase, snake_case, and SCREAMING_CASE, so feel free to use whichever you prefer.

So, given that my actions are shaped as above, my listeners could look something like this in the render process:

// render.js

  switch (data.type) {
    case 'performDiff':
      return window.globalPerformDiff();

      // no-op

And something like this on the main process:

// main.js

  switch (data.type) {
    case 'init_touch_bar':
      return initTouchBarWithConfig({ theme: data.theme });

      // no-op

Sending messages

While listening for messages on the main and render processes is quite similar, sending messages is slightly different.

I should also mention that there are limitations with the data that can be sent in a message.

Your message data must be serializable. Primitives work well for this case, e.g. strings, numbers, and JSON objects that consist only of primitives. Put simply, if you are passing an object, you should be able to run JSON.parse and JSON.stringify on your data and have it work reliably.

If your data is not serializable, you will encounter some errors. One error that you may see is Error: An object could not be cloned. This can occur if you try to pass a reference to a function as part of your message data. The reference cannot be saved and you will likely encounter an error. So, put simply, you cannot just send a message to the main process with a click handler and attach that to the Touch Bar button—this will not work, i.e. this would likely fail: { type: 'doSomething', onClick: doSomething }

We need to use message passing both ways in order to implement Touch Bar functionality end-to-end.

Render -> Main

This part will go over sending a message from the render process (the client-side web app) to the main process (the Node.js app with access to the native layer via Electron).

Let's say when my app initializes, I would like to initialize the Touch Bar with the dark theme. I am choosing to do this from the render process. Doing it from the render process adds an extra step of message passing, but it also gives me more flexibility.

I could have simply done it in the main process without going through the render process, but since I want to configure the style based on the user's UI theme, I need the user to tell me this (from the browser).

So, given that I'd like to send a message from the render process to the main process, the message could look something like this:

// render.js

function initTouchBarWithTheme() {
  const theme = localStorage.getItem('theme') || 'light';

  ipcRenderer.send('main-actions', {
    type: 'init_touch_bar',
    theme: theme

The above code will result in the init_touch_bar message being sent from the browser to the Node.js process. This would be a good spot to implement the Touch Bar API, creating a button, and configuring the colour based on the theme sent from the render process.

Main -> Render

This part will go over sending a message from the main process (the Node.js app) to the render process (the web app running in the browser).

Given that the main process is where I need to be to interact with native API's, I would set up my Touch Bar and all of its buttons on the main process. But, considering my application is an Electron app and the bulk of my logic is happening in the UI, I would likely need any user interaction with that button to be propagated to the render process.

So far we've sent a message from the render process and included the theme, which tells us on the main process that we'll need to set the button to be a specific colour. Let's implement that instruction now.

Here is some basic code for creating a Touch Bar with a single button where the colour will change depending on the provided theme:

// main.js

function initTouchBar({ theme }) {
  const window = BrowserWindow.getFocusedWindow();

  const touchBar = new TouchBar({
    items: [
      new TouchBarButton({
        label: 'Perform Diff',
        backgroundColor: theme === 'light' ? '#FF9A49' : '#302AE6',
        click: () => {


The code above is enough code to get a Touch Bar with a single button that changes colour depending on the theme.

That's great and all, but the button won't do anything until we implement the click handler. In this case, clicking on this button should trigger a form submit in our render process. We won't go into detail about what that form submission does or how to implement it, we just need to trigger it, which we can do by clicking on the submit button. This will propagate the event and submit the form.

There is more than one way that we can tell the render process to execute JavaScript from the main process. The two ways I'll touch on both use the webContents API:

  • Sending a message – That one is no surprise, given that's what this article is about. This is the approach I prefer.
  • executeJavaScript API – An alternative approach that I do not prefer

So, before we get into how I would do it, let's briefly talk about the alternative (i.e. how I would not do it). The alternative way this can be done is without using message passing, and instead using the executeJavaScript API, which takes a string of JavaScript, and a callback with the result. If we used this approach, it means I could look up my DOM element and trigger a gesture (like a button click) and put that code right in the argument string.

While this API would be adequate for very simple tasks, it may not be ideal in the following cases:

  • my application is using a framework like Angular, Vue, or React, as this would be outside of the framework code
  • my code is relatively complex or comprehensive

I would not personally use this approach. The full range of support via this Electron wrapper is not predictable, may be unreliable, and will likely be quite difficult to debug if you have any issues. Users have experienced some limitations with this approach, at least in its earlier days.

I would prefer to leverage message passing here as well, and send a message from the main process to the render process, and then once I receive that message, execute my code. This is because I can send a very simple message with primitive data I am confident will arrive as intended, and just make sure I'm listening to this message in my web app. The code I execute is not contained in a string, which means I can write tests or execute it in other scenarios without duplicating it into the string argument of a function call.

Message passing from the main process to the render process is also done via the webContents API. I'm going to create the button click handler, which will send a message to the render process. This message will not have any additional data other than the type property.

// main.js

const onPerformDiff = () => {
  const window = BrowserWindow.getFocusedWindow();
  window.webContents.send('render-actions', { 
    type: 'performDiff' 

Then, I'll attach it to the button I just made:

// main.js

function initTouchBar({ theme }) {
  const window = BrowserWindow.getFocusedWindow();

  const touchBar = new TouchBar({
    items: [
      new TouchBarButton({
        label: 'Perform Diff',
        click: onPerformDiff,


This means that once my orange (or purple) button is tapped on the Touch Bar, it will send the message { type: 'performDiff' } to the render process.

I can listen for that message on the render process and execute some JavaScript. A reminder of the listener we've already set up:

// render.js

  switch (data.type) {
    case 'performDiff':
      return window.globalPerformDiff();

      // no-op

In this case, my app is a vanilla JavaScript app and I've saved a method globalPerformDiff to the window so I can easily access it:

// render.js

window.globalPerformDiff = () => {
  const button = document.getElementById('perform-diff-button');

I just need to trigger a click on the "Perform diff" button, which will propagate a form submission event, and my app will take care of the rest.

This is it! With everything we've gone over, you should be able to implement a Touch Bar button end-to-end.

I have put my Touch Bar initialization code in a separate method, initTouchBar, which in reality does more than this article covers. I am also calling it multiple times. Here are some examples of when I'm calling it:

  • when my app loads
  • when the user toggles between the light and dark UI themes
  • when the user performs a diff, where I conditionally show and hide buttons depending on some settings

The IPC lifecycle in Electron, visualized

Now that you've seen the code, let's go over the lifecycle of the interprocess communication events for this feature.

  1. from render: Send a message from the render process to the main process to create the Touch Bar for the specified UI theme
  2. from main: Configure the button with the theme colour using the Electron TouchBar API
  3. from main: Configure the button click handler of the above button to send a message to the render process using the Electron webContents API.
  4. on main: User interacts with the Touch Bar button
  5. from main: Send a message from the main process to the render process for the clicked button
  6. from render: Perform an action based on the message and its data


And that's how I would implement a macOS Touch Bar feature in an Electron app.

Interprocess communication in Electron is useful for implementing any and all native desktop functionality end-to-end, like custom desktop notifications, interacting with the camera, or more. Check out the Electron documentation for a list of available API's.

I hope you've learned something here today. Let me know about your experiences with IPC in Electron, and your experience with Electron in general.

And feel free to check out TextDiffer once it's launched! 🚀🚢

https://blog.tinaciousdesign.com/building-a-macos-touch-bar-app-using-interprocess-communication-in-electronhttps://blog.tinaciousdesign.com/building-a-macos-touch-bar-app-using-interprocess-communication-in-electronSun, 30 Aug 2020 05:54:09 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1598765705878/7NP8Q5-S8.png
<![CDATA[Migrating my blog from WordPress to Hashnode]]>A step-by-step guide for migrating a blog from WordPress to Hashnode.

Why did I switch from WordPress to Hashnode?

Currently, my portfolio website and blog are on WordPress. It's been really great for search engine optimization, and the experience with WP Engine has been almost flawless, but it's been a long time since I built it so everything I use to run it locally is broken: Vagrant, Scotchbox (LAMP stack), WP Distillery (Wordpress-flavoured LAMP), etc. It's no longer as quick as it used to be to make small changes.

I have been evaluating different blogging platforms. I considered Medium, but I am not a fan of the experience of landing on a Medium link and hitting a paywall, so I didn't want users to experience that if they find one of my posts. It also didn't have any ads injected, so that's great.

I also thought about using a headless CMS and a static site generator like Nuxt as I've had a great experience with it, but that would've taken a lot of dev time to build. I think that's something I would do for the rest of my site once I have more time.

Hashnode has a variety of features that I appreciate:

  • Markdown editor
  • Canonical link support so I can link to my original post if I wanted to
  • Automated backups to a Github repository
  • Custom domain linking
  • Nice code syntax highlighting, with the bonus of embedding external services for code snippets

What's great is that these are all free, too. There's also a helpful community on Discord to get some help if needed.

Walkthrough for migrating from WordPress to Hashnode

Download post content

To begin the migration, you'll need to download your existing posts. By default these are in XML format. Since I don't really like working with XML, I used the plugin WP Import Export Lite to export my posts as JSON.


The resulting output was an array of objects that had the following shape.

  "ID": "463",
  "Title": "iTerm colour schemes",
  "Content": "Introducing 2 iTerm colour schemes [...]",
  "Excerpt": "iTerm colour schemes based off of my Visual Studio Code theme",
  "Date": "June 22, 2020",
  "Post Type": "post",
  "Permalink": "https:\/\/tinaciousdesign.com\/blog\/iterm-colour-schemes\/"

Download images

Next, you'll need to download all your images. There's more than one way you can approach it:

  1. Download all of the images uploaded to your WordPress install
  2. Programmatically process a JSON or XML export of the Media Library, which has links to the media

The first option is by far the easiest, so I went with that.

Images are stored in the ./wp-content/uploads directory by year and month.

└── uploads
    ├── 2019
       ├── 01
       ├── 02
       └── 03
    └── 2020
        ├── 01
        ├── 02
        └── 03

Your posts reference images like this:


You can use an SFTP tool like Cyberduck or FileZilla to download this folder.

Upload images elsewhere

Update: This section is out of date. The service Fast no longer offers a free plan and Hashnode has its own CDN so I have been manually updating them in an effort to save on server costs.

That "elsewhere" I chose is Fast.io because it had a generous free plan that leverages a variety of storage options like Dropbox, Google Drive, OneDrive, etc. I can take advantage of one of those amazing storage services and link it to Fast.io. I chose Dropbox. After manually deleting some images that weren't used in the blog posts, I uploaded the entire contents of the downloaded ./wp-content/uploads directory to Dropbox.

Programmatically publish posts to Hashnode using their API

Hashnode has a GraphQL API. It's somewhat limited in functionality but has most of the basic features. There's a helpful blog post on how to use it.

Back-dating posts is not supported in the API so I needed to do this manually for each of my posts, which was tedious.

I've created a repository of the scripts I used to facilitate cleaning up and bulk uploading posts.


Overall, I was happy with my migration to Hashnode. It didn't take very long. Hashnode has a lot of features I look forward to using and I was happy to invest the time in migrating.

Setting up my custom domain was a bit painful compared to other services I've set up domains with, e.g. Heroku, Netlify, Github Pages, Amazon S3. I use Cloudflare, and eventually what worked was contacting support on Discord, disabling Cloudflare's default proxying behaviour, and waiting until the SSL certificate was re-generated. Within a day it was working, so the trouble was only a minor setback.

The authoring experience is nice, typographically. There's also support for a lot of different types of embeds. Showing code is pretty without any effort on my part, and their embeds support the services I use most (Codepen, Codesandbox).

Overall, migrating to Hashnode is a relatively low investment, low risk migration.

If you like the sound of this and want to migrate your WordPress blog to Hashnode, you can use some handy scripts I created.

https://blog.tinaciousdesign.com/migrating-my-blog-from-wordpress-to-hashnodehttps://blog.tinaciousdesign.com/migrating-my-blog-from-wordpress-to-hashnodeWed, 05 Aug 2020 06:17:04 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596515954464/B4y6Ud6g3.png
<![CDATA[iTerm colour schemes]]>Introducing 2 iTerm colour schemes. These are based off of my Visual Studio Code theme. See the Download and installation instructions on Github.

Tinacious Design (Dark) iTerm colour scheme

Tinacious Design (Light) iTerm colour scheme

https://blog.tinaciousdesign.com/iterm-colour-schemeshttps://blog.tinaciousdesign.com/iterm-colour-schemesTue, 23 Jun 2020 01:27:04 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596504738761/_ZzwaDtAe.png
<![CDATA[ImageMagick Cheatsheet]]>An ImageMagick cheatsheet with some helpful commands for working with ImageMagick on the command line. This is a work in progress that will be periodically updated and is by no means a complete list.

Resizing images

Resize a single image:

convert input.png -resize 20x20 -quality 100 output.png

Batch resize some images:

mogrify -resize 600 *.png

Replacing the background colour of a transparent PNG

For transparent PNG, you can add a background colour.

convert source.png -background '#ff3399' -flatten destination.png

Or overwrite all images in a directory:

mogrify -background 'dodgerblue' -flatten *.png

Rounded corners

You can round the corners of an image with the following script:

convert input.png -alpha set -virtual-pixel transparent -channel A -blur 0x8  -threshold 50% +channel output.png

And you can batch round the corners of images using the following:

mogrify -alpha set -virtual-pixel transparent -channel A -blur 0x8  -threshold 50% +channel *.png
https://blog.tinaciousdesign.com/imagemagick-cheatsheethttps://blog.tinaciousdesign.com/imagemagick-cheatsheetSun, 24 May 2020 02:23:02 GMT
<![CDATA[BuildConfig.DEBUG always returns false]]>If you see the error that BuildConfig.DEBUG always returns false in Android Studio, this is likely because you've imported another package's BuildConfig instead of your own. Follow the instructions below to fix it.

About BuildConfig

BuildConfig is a generated class. It gets output to your build folder, which is ignored by source control, so you likely are not realizing the file being generated at all. Many projects appear to expose their BuildConfig class so the intellisense in Android Studio will pick up on these, especially if yours hasn't been generated yet.

Why wouldn't be BuildConfig be generated?

Your BuildConfig may not be generated for a number of reasons:

  • The build directory has been deleted
  • You did a Project clean via Gradle, which deletes the build directory
  • You updated Android Studio and Gradle, which may have a side effect of doing a Gradle clean

How to fix BuildConfig.DEBUG always returns false

Delete any BuildConfig import from the file

Delete any imports that import BuildConfig. You don't need them.

Ignore import prompts

Just ignore the import prompts. Do not import another project's BuildConfig. Of course this will always be false because projects don't ship debug builds. If you haven't generated you're likely to see this error. Android Studio prompting to import BuildConfig If you import another package's build config, you're going to get the warning: BuildConfig.DEBUG: condition is always false. Android Studio presenting import options BuildConfig.DEBUG always returns false warning

Run Gradle build

Run the project as usual. You should see this error disappear. BiuldConfig.DEBUG no longer has warning


TLDR: BuildConfig is a generated class in your project. If Android Studio can't find it, it'll prompt you to import it. Don't import it or you'll get the lint warning. Run the project as usual, ignoring the red. Your problem should be solved.

https://blog.tinaciousdesign.com/buildconfigdebug-always-returns-falsehttps://blog.tinaciousdesign.com/buildconfigdebug-always-returns-falseTue, 19 May 2020 02:23:02 GMT
<![CDATA[Writing a simple Gulp plugin tutorial]]>In this simple Gulp plugin tutorial, I will be going through a plugin I wrote. The plugin is simple and transforms an input JSON file. Though Gulp was a great tool I fully leveraged for bundling front-end apps at its inception, I do not currently recommend using Gulp for new projects. You may, however, find yourself in an existing project that already uses Gulp. Keeping an existing Gulp build system is perfectly reasonable. This tutorial would be useful in that case. In order to gain value from this blog post, you should have some basic experience setting up Gulp to bundle your projects.

Our problem

Writing a Gulp plugin that transforms JSON would be useful when bundling a Chrome extension for distribution. In the final bundle, developers need to provide a manifest.json file. This manifest, among other things, declares lists of domains that you are authorizing your Chrome extension to use. Such domains can include your Chrome extension's back-end API server, for example, a value that changes based on the environment. A plugin that manipulates the manifest.json, adding/removing/replacing values depending on the environment, can be useful to publish different Chrome extension builds for each of the supported environments (development, staging, production). We currently have the following manifest.json file:

  "manifest_version": 2,
  "name": "My Cool Extension",
  "version": "1.0.0",
  "content_security_policy": "default-src 'self'; connect-src 'self' http://localhost:1337 https://staging.example.com https://app.example.com",
  "permissions": [

For production, we would like the manifest.json file to look like this:

  "manifest_version": 2,
  "name": "My Cool Extension",
  "version": "1.0.0",
  "content_security_policy": "default-src 'self'; connect-src 'self' https://app.example.com",
  "permissions": [

Our Gulp plugin needs to remove the localhost and staging entries (http://localhost:1337 and https://staging.example.com) from both the content security policy and the permissions list.

Structuring our Gulp plugin

Ideally we should structure our Gulp plugin in 2 files:

  1. Primary business logic
  2. The glue that ties the business logic to the Gulp context

When we break our plugin into 2 files instead of one, we can isolate our business logic into a simpler method that is a simple function that takes simple arguments. We can then write tests for our business logic alone. This makes our plugin's logic much easier to test, compared to if we had only one file and we needed to test it within the context of a Gulp stream.

The business logic

This file is all of the business logic. Given a file buffer as an argument, as well as any other arguments passed through from the Gulpfile, we can perform actions on the provided input.

// transform-manifest.js
const transformManifest = (opts = {}, manifestJsonBuffer) => {
  const manifest = JSON.parse(manifestJsonBuffer.contents.toString());

  // Modify CSP
  const csp = '[my new CSP that is possibly created by removoing things]'
  manifest.content_security_policy = csp;

  // Modify permissions
  const permissions = manifest.permissions.filter(myFilteringCriteria);
  manifest.permissions = permissions;

   * Rewrite file buffer
  const stringContents = JSON.stringify(manifest, null, 2);
  const newBuffer = Buffer.alloc(stringContents.length, stringContents);
  manifestJsonBuffer.contents = newBuffer;

  return manifestJsonBuffer;

module.exports = transformManifest;

Our resulting file exports a function that takes a JSON file Buffer and returns a JSON file Buffer to continue down the Gulp stream.

The Gulp glue

This is the part that ties our primary business logic to the Gulp stream. The Gulp documentation provides multiple options for how to create plugins, but in this example I'll use the module through2 to help tie it together.

// gulp-transform-manifest.js
const through = require('through2');
const transformManifest = require('./transform-manifest');

module.exports = function (opts) {
  return through.obj(function(file, encoding, callback) {
    callback(null, transformManifest(opts, file));

You can essentially just copy and paste this Gulp glue verbatim. Keep in mind that opts are options that you can pass in from the Gulpfile. In my Gulpfile, the implementation is as follows:

// Gulpfile.js
const gulp = require('gulp');
const transformManifest = require('./lib/gulp-transform-manifest');

gulp.task('process-manifest', () => {
  return gulp.src([
  .pipe(transformManifest({ env: process.env.NODE_ENV }))

In the package.json I call my my task like so:

NODE_ENV=production gulp dist

The Gulp task dist uses the process-manifest task as a dependency alongside other tasks. The environment variable is passed through from the executed NPM script to Gulp, which passes it through to our Gulp plugin.


That wraps up this simple Gulp plugin tutorial. Let's take a look at what we've learned:

  • Our plugin transforms JSON and passes it through the Gulp stream on to any following plugins to process
  • We can pass arguments from the invocation of the call in the NPM scripts, to the Gulpfile (where we can add additional options), all the way down to the business logic of our function
  • We've structured our plugin in such a way that we can write unit tests for the business logic of our plugin
https://blog.tinaciousdesign.com/writing-a-simple-gulp-plugin-tutorialhttps://blog.tinaciousdesign.com/writing-a-simple-gulp-plugin-tutorialTue, 14 Apr 2020 02:23:02 GMT
<![CDATA[COVID-19 data by country]]>When the world gives you COVID-19, use the community-driven APIs it provides for COVID-19 data by country to make a tool to show data better? COVID-19 Today shows daily stats for the coronavirus. It's a web app built in React that displays COVID-19 data by country.

COVID-19 data by country, sorted by default criteria for most confirmed cases today

Sorting criteria

The tool allows you to sort countries using the following criteria:

  • total confirmed cases
  • total new cases today
  • total deaths
  • total deaths today
  • total recovered
  • current active cases
  • current cases in critical condition
  • highest cases per 1 million population
  • highest deaths per 1 million population

You can click or tap on the different buttons to sort the countries.

COVID-19 data by country (mobile view) for the countries with the most recoveries

Search by country

The tool also makes it easy to search for COVID-19 stats for a specific country. You can type a country name in the search box to find that country. It filters the countries list by the provided search criteria. This uses the country name provided by the API.

COVID-19 data by country search filtering


This tool couldn't have been made possible without the dated collected and shared by the open-source community:

There's also lots of other data available, but that's the one I used.

Source code

The source code for this app that displays COVID-19 data by country is available on Codepen here. The web application is built using the following technologies:

  • HTML
  • SCSS
  • React
  • jQuery (Ajax)
  • the above-mentioned API for COVID-19 data by country
https://blog.tinaciousdesign.com/covid-19-data-by-countryhttps://blog.tinaciousdesign.com/covid-19-data-by-countrySun, 29 Mar 2020 02:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596513558998/yIyuu_PUt.png
<![CDATA[JWT ECDSA signing in JavaScript]]>This article will go through how to implement JWT ECDSA signing to provide trusted, read-only tokens to a front-end app. This example is in JavaScript (Node.js on the back-end), but these concepts can be easily translated to other languages.

What's a JWT?

The jwt.io website says it best:


It's essentially a way of packaging data up and sharing it so that it can be trusted. Here is an example of what a JWT looks like:


There are 3 parts to the JWT token, and they are separated by a period:

  1. Header: Contains information about the token, e.g. identifying itself as a JWT, and also mentions which algorithm it uses.
  2. Payload: The data contained within the token
  3. Signature: The signature, which includes relevant signing details for the selected algorithm, as well as expiry, if applicable.

If you go to the jwt.io website, you will see the following output:

Asymmetrically encoded JWT token with signature verification using the ES256 algorithm which leverages private and public key pairs

Here is the contents of that token:

"name": "Tina",
"email": "info@tinaciousdesign.com",
"iat": 1572058477

Why are JWTs useful?

JWTs are a useful way to share data so it can be trusted. The format is JSON which makes it nice to work with, especially in JavaScript. One of the benefits of using JWT to share data over the wire is that you do not need to verify or validate anything contained in the JWT with a third-party—you can save on the extra network requests to your database and trust the contents of it to be true. One of the drawbacks with using JWTs is, even though they can expire, they cannot be revoked—you should be able to trust the data contained within the token, but whether that data is still relevant is uncertain.

Why do it this way instead of the default way?

The default implementation of JWT encoding and decoding uses a single private key for both reading and writing. This is fine if you are only reading it on the back-end, where your private key is safe. If you are trying to read a JWT token on the front-end, you do not want to expose the private key. The reason for this is because the private key can be used to both read and write JWT tokens. If you've exposed your private key on the front-end, then you can no longer trust it as anyone could've seen it and could use it to encode data. Exposing your private key is about as good as not even implementing JWT encoding and decoding protections.

What's ECDSA?

ECDSA stands for Elliptic Curve Digital Signature Algorithm. It's essentially a cryptographic strategy for creating private and public key pairs. ECDSA is very similar to how SSH keys work (which use RSA), except it uses a different algorithm. Though not as popular as RSA, it's a safer option with less foot guns. Modern browsers implement ECDSA as a crypto strategy for reading SSL certificates. ECDSA differs from other JWT signing approaches that are symmetrical:

  • Symmetrical: read and write using the same key—these should never live on the client or be publicly accessible
  • Asymmetrical: read and write use different keys (public and private key pairs). If you need to read the contents of a JWT on the front-end while still trusting it, this is your best bet.

What does a JWT ECDSA implementation look like?

Essentially, to implement JWT with ECDSA, you would follow a series of steps:

  1. Generate a private-public key pair signed using elliptic curve cryptography (ECC) algorithm ECDSA
  2. Use the private key on the back-end to sign your token
  3. Use the public key on the front-end to verify your token and read the data

Generating a private-public ECDSA key pair

First, you'll need to generate a private-public key pair, similar to how SSH keys work. I used OpenSSL to generate my keys. You should keep the private key private, but it's fine if the public key is out there as JWT tokens cannot be authored with this key.

Sign your data with the private key on the back-end

Now that you have your keys, it's time to start making some data. You can encode any JavaScript object. Please note that this data is not private as any JWT token can be decoded on jwt.io whether there's a key or not, so if you are trying to conceal sensitive data, this is not the way to do it. For example, never store passwords or sensitive data with JWT.

Read your data with the public key on the front-end

Using the public key, use the JWT.verify method to verify the validity of the signature and view the contents of the data. If the signature does not pass, you should obviously not trust it. There is a method available in the referenced library to decode JWT tokens without verifying the signature. You want to make sure you use verify so that you verify the signature.

Sample code implementing JWT ECDSA asymmetrical signing

Back-end code

  1. Generate private and public keys
  2. Sign the data using the private key
  3. Share the public key with the front-end

Front-end code

  1. Make an API request to get encoded data and public key
  2. Securely decode the token using JWT.verify
  3. Display the data in the view

This JWT ECDSA implementation will have all the code examples you need.


Asymmetrical signing with private-public key pairs is useful if you ever need read-only access to data on the front-end. As specified above, if you plan on reading JWT tokens on the front-end, you definitely want to use an implementation that leverages private and public key pairs. This is because if you expose your private key to the front-end, there's actually no point to even using JWT tokens as this data can now be forged and is no longer trustable. Implementing JWT ECDSA signing is a full stack effort where the back-end does most of the heavy lifting, and the front-end reads it.

Further reading

https://blog.tinaciousdesign.com/jwt-ecdsa-signing-in-javascripthttps://blog.tinaciousdesign.com/jwt-ecdsa-signing-in-javascriptMon, 09 Sep 2019 02:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596511124691/D1IP-IF4j.png
<![CDATA[Basic HTTP auth in Express and Node.js]]>When you're looking for a quick and dirty way to add password protection to a website, basic HTTP auth is handy. This tutorial will go through implementing basic HTTP auth for a static website using Express.js in Node.js.

What is basic HTTP auth?

Basic HTTP auth is simple username and password login protection for a website. It is not recommended to use this in a real, production-level project as it is not very elegant, but it works for the simple task of password protecting a website.

Basic HTTP auth prompt

I implemented basic HTTP auth in this resumé website project.

Setting up our Express app

This is some basic code for setting up an Express app.

// ./server/index.js 
const express = require('express'); 
const Path = require('path'); 
const app = express(); 
const port = process.env.PORT || 8000; 

 * Routes
app.use('/', express.static(Path.join(__dirname, '../static'))); 
app.listen(port, () => {   console.log(`Listening on port ${port}`); });

Essentially what this code does is create an Express server that listens on port 8000 and serves a directory located in the project root in ./static. This will be the site we password protect.

Adding basic HTTP auth in Express

To add basic HTTP auth in Express, we are going to leverage a handy plugin that the Express team recommends. It's called basic-auth. There are similar packages out there (including ones with the name express) but this is the one that works and that the Express team recommends. We will also be using the tsscmp library, which stands for timing-safe string comparison. This will allow us to perform time-safe equality checks on our strings. Further reading on implementing time-safe string comparisons can be found in the blog post or by reading this library's source code.

Now let's add these to our project with Yarn:

yarn add tsscmp basic-auth

Or regular NPM:

npm install tsscmp basic-auth --save

Let's create a new file to modularize our auth middleware. We could just put it all in the same file, but to keep it neat, let's follow the recommended approach to developing Express.js middleware and get started:

// ./server/middleware/auth.js 
const auth = require('basic-auth'); 
const compare = require('tsscmp'); 
const basicAuth = (request, response, next) => { }; 

module.exports = basicAuth;

All Express.js middleware follows the same pattern where it's a function that executes with the provided 3 arguments:

  • request: Express Request object
  • response: Express Response object
  • next: function to continue

Middleware can either stop everything by executing a response, or it can allow the application to continue by calling next() which can allow the next middleware (if applicable) or final route handler to run.

// ./server/middleware/auth.js 
const auth = require('basic-auth'); 
const compare = require('tsscmp'); 

  const credentials = auth(request);   
  if (credentials && check(credentials.name, credentials.pass)) {     
    return next();   

  response.set('WWW-Authenticate', 'Basic realm="my website"');   
  return response.status(401).send("🙅🏻 nope"); 

module.exports = basicAuth;

This is our basic middleware, where we ask for credentials from the request, which will prompt the user to enter a username and password, then we check those credentials safely using our check() function (that we haven't yet implemented). Upon successful check, we call next() to allow the next middleware (or route handler) to run.

If our check fails, we respond with a 401 Unauthorized.

This is what the check() function looks like:

const compare = require('tsscmp'); 
const check = (name, pass) => {   
  let valid = true;   // Simple method to prevent short-circuit and use timing-safe compare   
  valid = compare(name, '[my_username]') && valid;   
  valid = compare(pass, '[my_password]') && valid;   
  return valid; 

It implements tsscmp to safely compare strings.

All together it looks like this:

// ./server/middleware/auth.js 
const auth = require('basic-auth'); 
const compare = require('tsscmp'); 

const check = (name, pass) => {   

  return valid; 

  const credentials = auth(request);
    return next();   


module.exports = basicAuth;

Then we import it and implement it in our main server file:

const basicAuth = require('./middleware/auth'); 

The final code should look like this:

// ./server/index.js 
const express = require('express'); 
const Path = require('path'); 

const app = express(); 
const port = process.env.PORT || 8000; 

 * Routes

app.listen(port, () => {   

Now when we run node server/index.js we should have a password-protected server running on port 8000.

https://blog.tinaciousdesign.com/basic-http-auth-in-express-and-nodejshttps://blog.tinaciousdesign.com/basic-http-auth-in-express-and-nodejsMon, 27 May 2019 02:23:02 GMT
<![CDATA[Animated Show More toggle in React]]>React Animated Show More is a simple, fully-customizable component that expands and collapses to show your content.

Animated GIF of React Animated Show More

Why use React Animated Show More?

  1. It works!
  2. It uses the latest React version which introduces Hooks, and it's awesome!
  3. It's well-tested and boasts 100% test coverage for both rendering and behaviour
  4. It uses CSS transitions for animating
  5. It's fully customizable
  6. It's open-source, so if you think it could use an improvement, you can contribute on Github!

Build Passing badge and 100% code coverage

CodeCov.io coverage chart visualizing tested areas of the library

CodeCov table of test coverage by file


This Animated Show More toggle allows you to customize a number of items:

  • Custom toggle component which supports render prop isOpen
  • height to customize the collapsed height
  • speed to customize the animation speed
  • shadowColor to customize the small shadow overlay that helps your users see that there's more to read. This was designed to be used on any background colour.

I built this library to test out Hooks in React 16.8. That said, this project requires at least React 16.8 in order to use it.



To use this component in your React project that supports hooks, simply run one of the following scripts.

For NPM:

npm install --save react-animated-show-more

For Yarn:

yarn add react-animated-show-more


To implement the React Animated Show More toggle, just wrap it around your lengthy content to trim it down.

You just pass in your configuration props for height, overlay shadow gradient colour, animation speed, and custom toggle component.

import React from 'react';
import AnimatedShowMore from 'react-animated-show-more';

// ...

  toggle={({ isOpen }) => isOpen ? 'Close!' : 'Open!' }

  {/* Lots of stuff goes here */}
  <DemoText />


That's all there is to it. It's a simple plugin with minimal styling that allows you to provide your own component that expands and/or collapsed when you click it.

More info

If you like what you see and would like me to speak at your conference or help you part-time on a project, feel free to contact me.

https://blog.tinaciousdesign.com/animated-show-more-toggle-in-reacthttps://blog.tinaciousdesign.com/animated-show-more-toggle-in-reactSun, 21 Apr 2019 02:23:03 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596509686437/3lrLw81wJ.png
<![CDATA[Express route param validation (Node.js)]]>If you do Node.js development you're probably familiar with Express and maybe even Hapi. If not, they both solve the problem of server-side routing for APIs and server-rendered apps. Update (May 25, 2019): The Hapi team have deprecated their joi package in favour of @hapi/joi so the content of the blog post has been updated to reflect that change.

Express vs. Hapi

I'm not going to get into the pros and cons of each framework, I will simply list what I love about each of them. I love how Express is so simple and concise. It doesn't require much setup, and its concept of middleware is straightforward and easy. Hapi's middleware ecosystem is similar except they're called plugins. One great plugin is the Joi validation library. It integrates nicely with Hapi and can be used to validate route params.

Route param validation

If you've done any sort of back-end or full stack development, you'd know that it's important to validate the incoming request for the right parameters. For example, perhaps you'd like to validate an ID, you'd want to make sure it's in the format your app is looking for, e.g. auto-incrementing integer, or following a slug format, e.g. record-123abc. Or maybe you want to validate that a phone number is in the right format.

Express route param validation using Joi

I wanted to take these two great things and put them together, so two years ago I wrote the express-joi-validate library. This library allows you to use the simplicity of Express while giving you powerful route parameter validation using Joi. My library implements Express's middleware API to process the request before it is passed off to your route handler. Let's say we have the following sample app in Express:

const express = require("express");
const app = express();
const port = process.env.PORT || 3000;
const contactsHandler = require('./handlers/contacts'); // resolves the request

app.get('/contacts/:id', contactsHandler);

app.listen(port, () => {
  console.log(`Example app listening on port ${port}!`);

Without route parameter validation, a route in Express could look like this:


If we wanted to validate the ID param, we could do so in the handler, but arguably there's a better way! These kinds of concerns are best delegated to middleware. With route validation using express-joi-validate, it could look like this:

const validate = require('express-joi-validate');
const Joi = require('@hapi/joi');

const contactSchema = {
  params: {
    id: Joi.number().required()

app.get('/contacts/:id', validate(contactSchema), contactsHandler);

The above example is simple, requiring that ID be a number, but you can get really creative with validation. If the above validation fails, the server will return 400 Bad Request automatically. Joi comes with a bunch of validators, including lots of different string validation, e.g. email, phone number, dates, and if you like to get creative, even regular expression. Look at the Joi API reference to see what it can do.

Why do Express route param validation with Joi

You may ask why this is necessary. In my opinion, separating your API route validation from the handler makes sense in order to separate concerns.

  • Separation of concerns: validation happens outside of business logic
  • Cleaner code in your handlers
  • Don't reinvent the wheel and write validation: this is a problem that has been solved already

This may be best illustrated with sample code.

Sample without express-joi-validate

In this example, the handler does the validation.

// route definition
const createContactHandler = require("./handlers/contact/create");

app.post("/contacts", createContactHandler);

// handler without express-joi-validate
const Contact = require("../models/contact");

const createContactHandler = (req, res) => {
  if (
    // Validate phone (optional, must match pattern)
    (req.body.phone && !req.body.phone.match(/^[0-9]*$/)) ||
    // Validate birthday (required, must match ISO date format pattern)
    (!req.body.birthdate ||
      )) ||
    // Validate first name (required, letters only)
    (!req.body.first_name || !req.body.first_name.match(/^[a-zA-Z]+$/))
  ) {
    return res.send(400);

  return Contact.create(req.body).then(contact => {
    return res.json({ contact });

module.exports = createContactHandler;

Sample using express-joi-validate

In this example, the middleware handles the validation, which separates the concerns and keeps the handler code clean and tidy.

// route definition
const Joi = require("@hapi/joi");
const validate = require("express-joi-validate");

// This could be moved to a separate file to keep this even cleaner
const contactSchema = {
  body: {
    first_name: Joi.string().required(),
    birth_date: Joi.isoDate().required(),
    phone: Joi.number()

app.post("/contacts", validate(contactSchema), createContactHandler);

// handler where express-joi-validate middleware was implemented

const createContactHandler = (req, res) =>
    .then(contact => res.json({ contact }));

module.exports = createContactHandler;

Look how clean that handler code is! The handler ends up just being the call to create the model and return it. Joi has already solved the problem of validating dates, for example. Let's use it instead of writing our own validation code. It's also much nicer to read than the complex conditional statement.

How to do Express route param validation using express-joi-validate

You can download this library from NPM or from Github. See the documentation in the README file on the npm.js website.

https://blog.tinaciousdesign.com/express-route-param-validation-nodejshttps://blog.tinaciousdesign.com/express-route-param-validation-nodejsMon, 11 Mar 2019 02:23:04 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596509131736/FXVMlHmWi.png
<![CDATA[Using an SSL certificate on Vagrant for local development in Google Chrome]]>Two words: SSL Chrome! Some recent changes with Chrome can cause breaking changes in local development environments.

Problem #1: the website doesn't load

First problem? – Chrome updated itself and now my local development environment won't load. Instead of navigating to http://192.168.aa.bb I wanted to be able to navigate to tinaciousdesign.dev. You can learn how to do that by reading my blog post .dev domain extension for local development.

Chrome site cant be reached

The problem occurs because Chrome automatically redirects all domains (other than localhost) from using HTTP to using HTTPS. It doesn't appear to do it for all domains on the internet, I realized later, but it did for this local domain. To solve this, you can follow a bunch of instructions on the internet on how to create an SSL certificate to use in Vagrant, which, at least in my case, resulted in a few failures, as follows.

Problem #2: my self-signed SSL certificate doesn't work

Another problem? – A big red lock that prevents you from doing local development in Google Chrome. My process was as follows:

  • Generate a private RSA key
  • Generate a certificate with that private key
  • Tell Apache to use it
  • Restart Apache
  • Trust the certificate in macOS Keychain Access
  • Restart Chrome and visit website
  • See big red warning symbol 🙁😒

This is a good start, and this works in Safari (at the time of writing), but the problem with this is that there are many ways to create a self-signed SSL certificate, and Chrome is quite strict about what it accepts. Newer versions of Chrome do not allow you to bypass security issues anymore like they used to so if you want to access the site, you'll need to make Chrome trust it.

Chrome warns that the common name is invalid with NET::ERR_CERT_COMMON_NAME_INVALID

To get around this, you could easily switch to a different browser. For example, Safari isn't as strict, and the site loads fine using SSL in Safari. If that's a good enough answer for you, you can stop reading now. If you prefer using Google Chrome because of the developer tools, and you still want to use your custom dev domain, continue reading! In order to get Google Chrome to load the website at your-domain.dev, you'll need to properly create a self-signed certificate in the way that Google Chrome expects it.


In order to solve this, we will be creating a self-signed certificate for our domain using a root certificate, a domain CSR, and then trust the certificate authority for this certificate. More on all this will follow. You will need to have the openssl command line utility installed. You can do so with homebrew on Mac. We will do the following:

  • Generate a private root key
  • Generate a private root certificate
  • Generate a domain Certificate Signing Request for our domain
  • Generate the SSL certificate
  • Tell our website to use the certificate
  • Trust the certificate
  • Restart Apache
  • Restart Chrome

Generate a private root key

In the site project, create a directory called CA. We will put stuff there. Now generate a private key.

openssl genrsa -aes256 -out CA/rootCA.key 4096

Generate a private root certificate

This key will be used to generate a private root certificate.

openssl req -x509 -new -nodes -key CA/rootCA.key -sha256 -days 3650 -out CA/rootCA.crt

Generate a domain Certificate Signing Request

Next, we will generate a domain Certificate Signing Request (CSR):

A CSR (Certificate Signing Request) is a small, encoded text file containing information about the organization and the domain you wish to secure. It is required for the activation of a digital SSL certificate and, as a rule, is generated on the server where the certificate is to be installed. A CSR is submitted to the Certificate Authority and used to generate the certificate.


We will generate this for our domain. In this case, my domain is example.dev. Replace all instances of that in the following snippets with your domain name.

openssl req \
    -new \
    -nodes \
    -keyout example.dev.key \
    -out domain.csr \
    -days 3650 \
    -subj "/C=CA/L=ON/O=TD/CN=example.dev"

Generate an SSL certificate

Next we will need to generate a certificate with the domain Certificate Signing Request we just created.

openssl x509 \
    -req \
    -days 3650 \
    -sha256 \
    -in domain.csr \
    -CA CA/rootCA.crt \
    -CAkey CA/rootCA.key \
    -CAcreateserial \
    -out example.dev.crt \
    -extensions v3_ca -extfile <(
cat <<-EOF
[ v3_ca ]
subjectAltName = DNS:example.dev

Google Chrome requires that the Subject Alternative Names field be completed correctly. The output of all of my actions resulted in the following files being created:

├── CA
│   ├── rootCA.crt
│   ├── rootCA.key
│   └── rootCA.srl
├── Vagrantfile
├── domain.csr
├── tinaciousdesign.dev.crt
└── tinaciousdesign.dev.key

Tell Vagrant to use your certificate

Next you'll need to SSH into your Vagrant machine and tell it to use your new SSL certificate. Find the *.conf file in Vagrant. It should be in/etc/apache2/sites-enabled/. Once you've located it, you'll need to provide the following. I use Scotchbox for my WordPress environment provisioning, so my Apache config file looks like this:

    ServerAdmin webmaster@localhost
    ServerName scotchbox.local
    ServerAlias www.scotchbox.local
    DocumentRoot /var/www/public

    # Adding custom SSL certificate -- START
    ServerName tinaciousdesign.dev

    SSLEngine on
    SSLCertificateFile /var/www/tinaciousdesign.dev.crt
    SSLCertificateKeyFile /var/www/tinaciousdesign.dev.key

    #RedirectPermanent / https://tinaciousdesign.dev/
    # Adding custom SSL certificate -- END

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

Don't forget to replace all instances of tinaciousdesign.dev with your domain name.

Trust your certificate in macOS Keychain Access

Now that you've created this awesome certificate, you'll need to tell your computer to trust the certificate authority since it's not trusted by default. Google Chrome uses macOS's Keychain Access to manage SSL certificates. After creating the perfect certificate, you'll need to trust it.

  • Open Keychain Access
  • Highlight the System section on the left
  • Drag and drop your new certificate onto the System tab—my certificate is tinaciousdesign.dev.crt so this is what I dragged and dropped onto Keychain Access > System
  • Navigate to your certificate and double click it
  • In the dropdown "When using this certificate" choose "Always trust"
  • Close the window to save your changes—this will prompt you for your administrator password

Trust the certificate in macOS Keychain Access

For obvious reasons, you only want to trust certificates you actually trust. Since we'll only be using this certificate locally, this is fine, but I don't recommend dropping random certificates in here and trusting them.

Restart Apache

You can restart Apache with the following command:

sudo service apache2 restart

Restart Google Chrome and load up your website

Restart Google Chrome to make sure it loads up your new, trusted certificate. There it is! It should be working now!

Google Chrome says the connection is secure

This article is accurate as of the time of posting. The version of Chrome at this time is Version 72.0.3626.109 (Official Build) (64-bit).

https://blog.tinaciousdesign.com/using-an-ssl-certificate-on-vagrant-for-local-development-in-google-chromehttps://blog.tinaciousdesign.com/using-an-ssl-certificate-on-vagrant-for-local-development-in-google-chromeSat, 16 Feb 2019 08:00:00 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596508020912/cAFm1fFhZ.png
<![CDATA[Moving inventory management tool case study]]>I built a quick moving inventory management tool in order to assist me in one of my recent moves. In previous moves, I had used a spreadsheet to track my moving inventory, but this time around I wanted a more efficient and fun way to manage my inventory when moving from one apartment to another. The box names are courtesy of the local liquor store, and the addresses are from TV shows—can you guess which shows they're all from? 😄



  • Make moves and display their address on a map
  • Make an inventory list for every move
  • Ability to search the list using your browser's search field to find items quickly
  • Ability to check items off the list once you're moved in to see if all items are accounted for
  • Mobile-friendly viewing and editing modes

Dynamic and static maps using the Google Maps API

In edit mode, you are able to find your destination address using the Google Maps API. You can load in a dynamic map and configure zoom and framing. Then, on the main page where your moves are listed, you can see a static map load for each of your move destinations. This static map loads using the configured zoom and framing specified in the editing mode. This feature utilizes two separate Google Maps APIs.

Dynamically choose an address in the inventory management system dashboard

A moving inventory list for each move

The ability to have multiple moves scales better for people who have multiple moves going on at the same time, or who would like to keep historical data of their previous moves. You can specify the attributes for each item in the moving inventory list:

  • Box identifier (e.g. Box 1, Red luggage, etc.)
  • Description of the contents
  • Destination room
  • Markers for whether the contents are fragile or heavy




Keep track of your items using search and checklists

Since this results in having a moving inventory list displayed in your web browser rather than written on a piece of paper, you can easily search your list for items. Where did you pack that commonly-used item? Is the cutlery moved in yet? When you can search your list quickly, you can answer your questions quickly. You can also check items off one by one once you're moved into the new place to make sure everything is accounted for.

Check items off one by one as they land

Make sure everything is accounted for


We're always on the go, so ensuring that the tools you rely on are versatile and can be used wherever you need to use them. This prototype has mobile-friendly layouts for phones and tablets.

Moving inventory software shows a mobile-friendly view of all moves and their maps


v1.1 – Feature roadmap

This app is just a prototype for now, but if there ever was a need for something more robust, an infinite amount of features could be built:

  • Ability to search for specific items across multiple moves
    • This feature can be useful for people who move a lot of furniture around between multiple properties and need to keep track, e.g. real estate agents and staging companies.
  • Ability to create multiple user accounts with different permissioning models to allow selective access to moves and items
    • This could be useful for larger teams where managing different roles is important
  • Other attributes, e.g. weight, size, etc.
    • This could be useful if you wanted to track the total size and weight of your inventory, either actually or historically
  • Ability to add photos
    • A picture paints a thousand words! This could be useful if you have a lot of inventory. Visuals can help make it easier to scan, as well as make usage more attractive and enjoyable.

The possibilities are endless, and depending on needs, a tool like this could be extended to provide more information and functionality.

Tech stack

Though this prototype was built using the ProcessWire content management system, should this be built out as a software as a service (SaaS) product, it would likely benefit from an overhaul of the tech stack. That said, here's what it's built with:

  • PHP, MySQL
    • ProcessWire content management system (v3.x)
    • Twitter Bootstrap
    • Custom CSS
  • JavaScript
    • jQuery
    • Custom JavaScript
    • Google Maps API (JavaScript maps, Static maps)

If this wasn't a personal productivity tool and there was a budget to build this out correctly so that it can scale better, I would opt to build the front-end and back-end separately—the back-end would likely be built using Node.js or Ruby on Rails, and the front-end would be built with a JavaScript framework like React, Ember, Angular, or Vue.

Demo time!

If you'd like to try it out, visit the demo here

username: demo password: demodemo123!

Have fun and be nice.

https://blog.tinaciousdesign.com/moving-inventory-management-tool-case-studyhttps://blog.tinaciousdesign.com/moving-inventory-management-tool-case-studyTue, 30 Oct 2018 07:00:00 GMT
<![CDATA[Speaking at Geeky Summit in Calgary]]>I was invited to speak on a panel for women in tech at Chic Geek's Geeky Summit conference. I got to sit with two other amazingly talented and accomplished women. Some of the topics I addressed included advising women to speak up at meetings and contribute bravely. I also talked about promoting yourself, and not holding back on your accomplishments. I also mentioned the importance of finding an amazing and diverse team to work with.

Tina Holly speaking at Geeky Summit with moderator Aaron

Geeky Summit was the first women-focused tech conference to take place in Alberta. There was lots of great conversation around being brave as a woman, a feature not generally associated with femininity. Chic Geek promotes three values:

  • Be brave
  • Stay curious
  • Support others

The conference featured a lot of great women talking about being brave, encouraging attendees to stay curious and to seek mentorship to grow. Geeky Summit also talked about supporting others, especially other women, and especially in male-dominated fields.

Claim your space

One of the things I talked about at the panel was about making a space for yourself as a woman. Unlike on a panel, there is no taking turns in real-world meetings, and no one will grant you the time and space to speak—you need to claim that space for yourself. In male-dominated fields, the opinions of women can sometimes be dismissed and disregarded, where comparatively the opinions of men can sometimes be looked at more seriously. This is not the case at every workplace or on every team, but it does occur. These negative experiences can sometimes discourage women from contributing at meetings. You have something valuable to contribute—don't wait for a pause. Opportunities do not happen for us, we need to claim them for ourselves.

Promote yourself

You need to be your own hero, your best advocate, and you need to toot that horn! As women we tend to be modest and downplay our accomplishments out of fear of sounding cocky. We are very self-aware beings, which is great, but it can lead to us downplaying our achievements. Don't hold back, don't be modest. Talk about everything you do. Choose projects that you will be proud to show off, otherwise don't bother working on them. Share everything you do in a portfolio, on Github, and on social media. Some things may not seem like much to you, but people will be impressed! We are often our own worst critic, which can hold us back from promoting ourselves. Be brave and support yourself.

Find a great, diverse team

Surround yourself with a team of amazingly smart and diverse individuals you can share knowledge with. Whether this is at work, or on a side project, it's important to surround yourself with a diverse group of people. Even though Geeky Summit was about being a woman in tech, there's more to diversity than being a woman. Working with people of all genders, ethnicities, cultures, and religions is important and valuable, to develop yourself professionally but also personally as an individual. People who are self-taught think differently than those that studied computer science or engineering. People that have worked at startups think differently than those that have worked at large corporations with a lot of red tape. Young individuals see the world differently than those who started in the tech industry 10 to 20 years ago. And imagine the intersectionality of all those diversities and how they multiply a person's experiences. A team that can think in a number of different ways can apply that much more experience to a problem, and come up with solutions from all angles. Sometimes it's easy to think when we're in a room with a bunch of smart people that we don't have much to offer, but that's not true! Your experiences are valuable, and you've done things others haven't done.

Tina Holly speaking with moderator Aaron at a panel at Geeky Summit in Calgary

Speaking on the panel at Geeky Summit was a great experience, and I look forward to many more experiences like this in the future. Check out what you missed at Geeky Summit on Twitter. I am available for speaking at tech conferences about web and application development. Contact me if you're interested in having me speak at your conference.

https://blog.tinaciousdesign.com/speaking-at-geeky-summit-in-calgaryhttps://blog.tinaciousdesign.com/speaking-at-geeky-summit-in-calgarySun, 26 Nov 2017 03:23:03 GMT
<![CDATA[React Rails travel app case study]]>Have you ever wanted to track countries you visit using an app? As someone who loves to travel, I do, and as a developer who strongly believes in lifelong learning, I decided to take the opportunity to build a simple app while familiarizing myself with some awesome new tech, including React Rails. This post will go into the building of this app using Ruby on Rails 5, which comes with the React Rails gem. If you'd like to see it and try it out, check it out here.

Track countries you visit with this browser-based travel app


The user:

  • will be able to login with Facebook and will not have to type in a password
  • can choose countries using a type-ahead style input, or by clicking on the interactive map
  • can share their customized map with friends using a permalink by copying and pasting the link, or clicking the buttons to share to Facebook, Google Plus, or Twitter
  • can customize the colour of the selected countries using a colour picker


Having heard great things about the new React integration with Ruby on Rails, I wanted to see for myself how great it was. I also wanted to take the opportunity to try out a mapping library.

Getting started

To get started, I used the newest version of Ruby on Rails to generate a new project. Some additional gems include the following:

  • pg for PostGres
  • OmniAuth and OmniAuth Facebook for authenticating with Facebook
  • jQuery turbolinks
  • React Rails (already included with the newest Rails v5.x as of this post)
  • jquery-minicolors-rails for a quick and dirty colour picker that sits on top of an input field
  • FontAwesome Rails for awesome font icons


I chose PostGres instead of MySQL because of the JSON field, which I figured would be useful for storing the countries a user visits. This format plays nicely with JavaScript, and since most of the heavy lifting would be done there, I decided to use that rather than a more strictly defined schema.


I used the OmniAuth gem with the OmniAuth Facebook add-on to allow for authentication via Facebook. There are quite a few security benefits to doing it this way.

No passwords required

Signing in with Facebook means that users don't have to memorize another password for your app. Memorizing yet another username and password will make users contemplate whether your app is worth the real estate in their memory, and requiring them to memorize credentials may makes them more reluctant to sign up. If they know it's as easy as clicking a button to authenticate with an existing service, they may be more likely to sign up.

Sign in with Facebook button means users don't need to enter a username and password to use your app

This also means that Facebook (or another auth provider) does the heavy lifting of authenticating your user, and allows you to validate their session using an app-specific token. You don't store users' passwords in a database, so if your app gets hacked, your users' credentials are worthless to attackers—only the aforementioned token is available to them, which can only be used for your app at the domain it lives.

App permissions page requests only necessary basic permissions

When creating an app using Facebook login, you can choose the scope of the information you get from your users. This app uses only the bare minimum, including public profile and email address. Facebook only allows one account per email address, so this is a reliable unique identifier to store in the users table in the database.

JavaScript development in Rails using React

Rails 5 comes with the React Rails gem which integrates React with JSX transpilation in the asset pipeline. If you've worked with React before, you may have realized there are many ways to work with React, including but not limited to Browserify and Webpack. React Rails doesn't appear to use either of these approaches for loading modules—it only runs your JavaScript code through Babel in the asset pipeline. Out of the box, everything is in the global scope, so to avoid polluting your global scope, you may want to wrap your React components in Immediately-Invoked Function Expressions, and expose only the components you wish to access from other files. Another more involved option is to set up React Rails to work with Webpack using the Webpacker gem, which is a much more familiar way JavaScript developers can use to work with React. Using Webpack will allow you to use ES6 modules (i.e. import and export statements). IIFEs are also not a thing when using Webpack. The root component of my app looks like this:

(function () {
   * Bootstrap the app with the Redux Provider and initialize the server-side data into Redux
  class App extends React.Component {
    render() {
      return (
        <Provider store={window.__store}>
          <DataComponentContainer defaultData={this.props}>
            <WhereIveBeenContainer />

  window.App = App;

I wrapped every component in an IIFE and explicitly exposed components I'd like to access from the global scope. I can then access my App component in the ERB templates using the following:

<%= react_component("App", {
  countries: @countries,
  userCountries: @user_travels.countries,
  customColour: @user_travels.colour
}) %>

My React app uses Redux. Bootstrapping the Rails data from the ERB template was less than glamorous, and involved the participation of a component specifically for that, which I've called DataComponent. It takes the props passed in from the ERB template, and puts it in the Redux store. I used a single file for bootstrapping the Redux portion of my app. Here's a sample:

 * Bootstraps the Redux store on window.__store
(function () {

  const { createStore, applyMiddleware } = Redux;
  const thunkMiddleware = ReduxThunk.default;

   * State
  const initialState = {
    countries: [],
    // ... Other initial state variables here

   * Action types
  // ...

   * Actions
  const actions = {

    updateCountries: (countries) => ({
      type: SET_COUNTRIES,
      countries: countries

    // ... Mo' actions


  const reducer = (state = initialState, action = {}) => {
    switch (action.type) {
      case SET_COUNTRIES:
        return {
          countries: action.countries

      // ... Mo' action type handlers

        return state;

   * Globals
  window.Provider = ReactRedux.Provider;
  window.__actions = actions;
  window.__store = createStore(reducer, applyMiddleware(thunkMiddleware));

Not all Redux plugins worked in the browser and need to use Node's require module, so if you have to develop a complex web app, I would hook up Webpack so you can use any of the Redux middleware via Webpack. The above bootstrapping code is quite hideous by the standards of anyone who's ever worked with Browserify or Webpack, but if you wanted to use React out-of-the-box with Rails 5 without any additional bootstrapping, wrapping all the bootstrapping code in an Immediately-Invoked Function Expression (IIFE, pronounced "iffy") is probably the quickest way to get started. Here's a fun pun:

To get a React Rails app done in a jiffy, use an IIFE! 😂

Directory structure

My JavaScripts directory structure is as follows:

app/assets/javascripts/ ├── application.js ├── cable.js ├── channels ├── components │ ├── App.jsx │ ├── CountryCount.jsx │ ├── CountrySelector.jsx │ ├── DataComponent.jsx │ ├── MapComponent.jsx │ └── WhereIveBeen.jsx ├── components.js ├── facebook.js ├── main.js ├── options.jsx ├── redux_bootstrapping.jsx └── server_rendering.js

There are only three actual components to the app, the rest are bootstrapping. The components.js file just pulls in the components directory tree. The facebook.js file has the code for handling Facebook login.

Countries map data

If you're wondering how I got the country data, it was provided by the amCharts Maps low-resolution map data. If you look at the source code, you will be able to see it, but essentially all of the SVG paths are specified in the script file you need to include to get your map going.

{ "id": "CA", "title": "Canada", "d": "M198.93,96.23l-0.22,.......108.88zM248.49,155.83l3.08,5.1l0." }

I created a Node script to consume this data and export it into a JSON file that provides an array of countries including only the id and title fields. It's a simple cleanup script that just takes everything and returns only those fields.

Miscellaneous things

Randomizing background images

A fun and easy feature I added was the randomizing background images. I found a bunch of Creative Commons-licensed stock photos and included them in the app in a directory called backgrounds. Details on how to get a random image from a directory using Ruby on Rails can be found at my Stack Overflow answer to a related question.

jQuery is still a tried, tested and true way to work with the DOM on websites. One of the features I developed was the ability to customize the colour of the selected countries.

jQuery-powered colour picker

This is using the above-mentioned colour picker gem, which can easily be initialized in an ERB template as follows:

<%= f.text_field :colour, class: 'js-colour-picker options__colour' %>

You can then activate the plugin in JavaScript using the following code:


You may have noticed some turbolinks stuff—Rails uses a feature called Turbolinks which allows you to change pages without refreshing your JavaScript on every page load. This makes for a faster front-end, but plugins that would traditionally be executed on document ready now need to be executed on document turbolinks:load otherwise they risk not being initialized when you'd normally expect them to be.

Type-ahead component

I used the React Typeahead plugin which was installed using NPM and included in the application.js file as so:

//= require react-typeahead/dist/react-typeahead

The node_modules directory lives on the path, so the assets pipeline understands to look there for Node module using the above syntax. You can use the Twitter Bootstrap styles, or style it yourself. I opted for the latter approach.

The React Rails app's text-based country selector uses React Typeahead and amCharts JavaScript maps

It uses the Tokenizer approach, and each of the pink boxes is considered a token. Check the React Typeahead documentation for details on how to implement React Typeahead.

Map sharing feature

One of the features I developed was the ability to share the map using a custom link.

Share your map with friends using a custom URL

I added a share identifier to the travel data, which is essentially just a random hash. I created an indexed column on the Travel data. This share ID is automatically created on the on-create controller lifecycle method callback.

class Travel < ApplicationRecord
  belongs_to :user
  before_create :set_defaults


  def set_defaults
    self.share_id = SecureRandom.hex

I have a Travel model that belongs to a User. Before the Travel record is created, it creates its own share ID. The social media share links are generated with each social site's recommended way for sharing. Twitter and Google Plus use intent URLs, whereas Facebook's uses the Facebook web SDK.

Deploying the React Rails app to Heroku

I used Heroku to deploy the app since it's the quickest, easiest, and cheapest way I've found so far. My package size is a bit large due to the randomizing background images, but other than that, the experience was flawless. Heroku is a great way to get an app hosted immediately with very little commitment. Provided your Gemfile has all the required gems in it, you should be able to deploy without any additional setup—Heroku will auto-detect your Rails app and use its Rails buildpack to compile and serve your app. You'll need to configure your production database to use the URL of your PostGres database add-on, but that is easily done in the database.yml file:

default: &default
  adapter: postgresql
  encoding: utf8
  timeout: 5000

  <<: *default
  database: where_ive_been

  <<: *default
  database: where_ive_been_test

# Heroku

The above configuration uses PostGres locally on my machine, and uses the Heroku-provided environment variable for the database URL in production. You will need to add a plugin to Heroku to support PostGres. Once you do, the database URL environment variable will automatically be made available. There's no setup other than using this URL in the database configuration.


React Rails is a very welcome addition to Rails. As a (primarily but not limited to) Front-end Developer, I appreciate how they tried to make it quick and easy to get started with React as the front-end to a Rails-powered back-end. I haven't used a faster React setup, though I do hear good things about React Create App. If you want quick and dirty, for example, if you're building something at a hackathon where you have a limited amount of time and are comfortable in both React and Rails, I would totally recommend this approach. I would definitely recommend using Webpack if you're going to have a more complex React app, but for mine that had only three actual components other than the bootstrapping ones, this was a fine approach. Try out the app for yourself!

https://blog.tinaciousdesign.com/react-rails-travel-app-case-studyhttps://blog.tinaciousdesign.com/react-rails-travel-app-case-studyMon, 18 Sep 2017 02:23:04 GMT
<![CDATA[Sign and verify Git commits using GPG signatures]]>You may have seen a hollow badge next to a Github user's commit timestamp that says "Verified" and wondered what that meant:

Verified Git commit

That indicator means that a user has added an extra level of trust certifying that she or he has signed off on that work, using cryptography software called GPG to sign and verify Git commits. After finding many pieces of information scattered about the internet but not a single resource that had all of the instructions spelled out clearly step by step, I decided to write up this tutorial. Feel free to share with others who may be interested and are not GPG pros!

Unverified commits

Unverified commits are the default, common type of commit, i.e. those that do not have a "Verified" badge. You may have realized that when initially configuring your Git configuration through the command line, that there was no way to check with Github, Bitbucket, or another Git service, that you are who you say you are. You would simply set your username and email and that was it:

git config --global user.name "My name"
git config --global user.email info@tinaciousdesign.com

As you can see, you can set your name and email to anything. Git alone has no way of authenticating that you are who you say you are. Github will display a small question mark icon next to the user's email if the commit author email is different than an email associated with the Github user's account. For example, if you have more than one email, and you're signed up with Github using one, if you commit using a different one, it'll show a question mark next to your user because that second email is not associated with your Github account. You can avoid that by adding that additional email to your Github account emails, but it won't get you the "Verified" badge and will only clear the question mark. It makes sense, however, that a commit found on Github is authored by whom it says it is because in order to push up to Github you need access to a Github account, so the value of signing your commits can be debated.

Verify your commits

Assuming you see the value in this, let's get started! There are a series of steps that will need to happen in order for you to verify Git commits:

  1. Creating a secret key with which to sign your commits
  2. Associating your key with your commit
  3. Creating a public key that pairs with your secret key
  4. Uploading the key to Github

Before beginning, you will need to ensure meet the following prerequisites:

  • The Git command line utility installed on your computer
  • A Github account with a verified email address
  • GPG installed—if you have Homebrew on Mac, this is easily done with brew install gpg

Please note these instructions are for Unix platforms (Mac, Linux). Once you're ready, open a terminal window to get started!

Creating a GPG key

First, we will be creating our GPG key. Run the following command to generate your first key:

gpg --gen-key

This will begin an interview-style process where you will be asked your name, email, and passphrase (optional). Once you're done, in your home directory, you should see a new .gnupg directory that was generated:

├── S.gpg-agent
├── S.gpg-agent.browser
├── S.gpg-agent.extra
├── S.gpg-agent.ssh
├── openpgp-revocs.d
│   └── E6FF...7CE.rev
├── private-keys-v1.d
│   ├── 91C...CC3E.key
│   └── E05...CA.key
├── pubring.kbx
├── pubring.kbx~
└── trustdb.gpg

This means that your key was successfully generated. We are ready to move onto the next step.

Signing your commit with the GPG key

Now that we have created a key, we can use it to sign. Run the following command to see what your key's ID is:


You should get output that looks similar to this:

sec   rsa2048/XXXXXXXXXXXXXXXX 2017-07-22 [SC] [expires: 2019-07-22]
uid                 [ultimate] Tina Holly <info@tinaciousdesign.com>
ssb   rsa2048/YY............YY 2017-07-22 [E] [expires: 2019-07-22]

Where I have XXXXXXXXXXXXXXXX, that's where your key ID is. That's the key you're going to be assigning to your commits using Git, and Git will then search for the key on your computer using that key ID. Copy the key ID to your clipboard before proceeding. Run the following code to globally associate your Git user with your GPG key by your key's ID:

git config --global user.signingKey XXXXXXXXXXXXXXXX
git config --global commit.gpgsign true

To test that you are ready to properly sign your commits, make some changes to a repository and make a commit. There is no indication during the commit process that this has happened, but you can run the following command to see if your signing worked:

git verify-commit HEAD

You should see output similar to the following:

gpg: Signature made Sat 22 Jul 13:16:44 2017 EDT
gpg: Good signature from "Tina Holly <info@tinaciousdesign.com>" [ultimate]

If you do not see that, you have not correctly associated your key to your user account. Go through the steps and try again, or see the Further Reading section below.

Creating a public key from your private GPG key

In order to associate your private key (which should never be shared) with services to verify your identity, you need to create a public key. Cryptographic methods like SSH already create a public key for you by default when generating the private key, but you will need to generate the public key so you can share it with Github. To do so, run the following command with the email address associated with your key:

gpg --armor --export info@tinaciousdesign.com > ~/.gnupg/public_gpg_key.pub

This should create the file public_gpg_key.pub in the same hidden directory. You can now cat the file and see its contents, which can then be copy and pasted—it should look similar to this:



Copy this key to your clipboard. You can also do that in one command by doing the following:

cat ~/.gnupg/public_gpg_key.pub | pbcopy

Visit your Github settings and go to the SSH and GPG Keys tab. GPG keys are at the bottom. Create a new one and paste it in there.

Click "New GPG Key" and paste your GPG public key

Save it, push up your signed commit and check on Github that it works. You should now see the "Verified" badge next to your commits.

Further reading

https://blog.tinaciousdesign.com/sign-and-verify-git-commits-using-gpg-signatureshttps://blog.tinaciousdesign.com/sign-and-verify-git-commits-using-gpg-signaturesSun, 23 Jul 2017 02:23:02 GMT
<![CDATA[Using Vagrant with WordPress]]>Prerequisites for using Vagrant and WordPress

Before beginning, you will need to have the following installed:

Vagrant WordPress configuration

We will be using a tool called WPDistillery. This tooling uses ScotchBox, which is a pre-configured Vagrant box configuration that provides a LAMP stack setup for Vagrant. WPDistillery provides the WordPress application-related provisioning, while ScotchBox provides the LAMP stack infrastructure. Let's assume your project root directory is called my-website. We will clone the required files into our project directory:

git clone https://github.com/flurinduerst/WPDistillery.git my-website

This should clone a Vagrantfile and a ./wpdistillery directory into your project root. We will need to modify the file ./wpdistillery/config.yml. It's pretty much ready to go but we should change the database information from the default, which can be found in this section:

  url: wpdistillery.dev
# scotch box db access
  name: scotchbox
  user: root
  pass: root
  prefix: wp_

Optionally, change the website URL from wpdistillery.dev to something more memorable, e.g. my-website.dev, and the database name from scotchbox to something more memorable for your website, e.g. mywebsitesdatabase. One of the benefits of doing this is the flexibility to run more than one WordPress website using ScotchBox on your computer. If all of your WordPress websites use the default database name, they will overwrite each other whenever you run them.

Run it

Once you've done this, run the following command:

vagrant up

This should start downloading the required Linux image if it has not already been downloading, and provisioning it with the ScotchBox configuration. Once the ScotchBox provisioning has run, it will start running the WordPress-specific commands that WPDistillery provides. At this point, if you've changed the default database name from scotchbox, this command should show an error saying that the provided database could not be found. This is to be expected. You should then at this point, log into the virtual machine and create this database:

vagrant ssh
create database mywebsitesdatabase;

Once that's done, you can Ctrl + D out of everything. You should be able to re-run the command and have it complete successfully.

Configure routing

Before loading your WordPress site in the browser, you will need to add the following line to your hosts file, found at /etc/hosts.   my-website.dev

Now that you've successfully provisioned a virtual machine that runs WordPress, you should be able to access it at the URL or http://my-website.dev/.

Start your theme development

Your theme files are located at ./public/wp-content/themes/. You can create a new theme here and get started with developing your WordPress project.

https://blog.tinaciousdesign.com/using-vagrant-with-wordpresshttps://blog.tinaciousdesign.com/using-vagrant-with-wordpressMon, 17 Jul 2017 02:23:02 GMT
<![CDATA[Web Hosting recommendations]]>So you want a website, but what about web hosting? There are plenty of options to choose from when deciding how to host your website. This page should be informative and provide you with some basic information for choosing web hosting that's most ideal for you.

Types of hosting

Shared web hosting

Shared web hosting is the most common type of web hosting available. You get some space on a shared machine.


  • Affordable, ranging from $7 to $12 per month
  • Full featured with cPanel, phpMyAdmin, an interface for managing domain names, FTP logins, and everything else you expect from a proper web hosting service
  • Convenient 1-click installs for common content management systems like WordPress, Drupal, Joomla, etc.


  • No root access, so you may be limited in what you can do with your web hosting
  • If one of the sites or applications on the shared server gets hacked, your website may suffer the consequences (rare occasion)

Virtual Private Servers (VPS)

Virtual Private Servers are great, and now they're more affordable than they used to be. They used to cost upwards of $30 per month but now you can get them for as cheap as $5. You get your own virtual private space with root access, so you can do whatever you want on the servers, provided that your activities are within the terms of use of the provider, of course.


  • Root access to install whatever you want and do whatever you want—oh the power!
  • Affordable, starting at $5 per month for a machine with 512 MB memory, 20 GB SSD storage, 1 TB of transfer
  • Convenient 1-click installs for common application infrastructure like WordPress, Node.js, Ruby on Rails, Java, etc.
  • Only pay for your droplet's uptime—if you destroy a VPS you don't need to pay for it for the rest of the month.


  • Very barebones—you have to set up everything yourself so you should have a web developer or enjoy hanging around in the terminal while you learn how to manage your website or app. No fancy interfaces for the server, but many of the VPS providers have great interfaces.

Managed hosting for Content Management Systems (WordPress, Drupal, etc.)

Managed hosting is the most expensive option but it provides high-value to clients where website reliability is important, performance is important, and whose sites see a fair amount of traffic. Pricing for managed hosting starts at roughly $30 per month.


  • High-performance, thanks to the aggressive caching methods used
  • Protection against Distributed Denial of Service (DDoS) attacks
  • CMS application core is kept up-to-date for you automagically
  • Allows for multiple environments, e.g. staging and production, so that you can work on your site in a sandboxed environment while ensuring users only see your site when you're ready to launch
  • Great deployment tools in place for developers, often using Git, the most common version control system that all web developers nowadays should know


  • Higher costs may not be justifiable for businesses whose sites see little traffic, or for businesses that generate little revenue

Cheap or free hosting options for static websites

If you are building a static website or a client-side JavaScript application, you can host your project for quite cheap or even free. You're usually quite limited with what you can host on these platforms, and usually you need to be quite technically-inclined to work with these options. These options are a perfect web hosting option for static websites and client-side JavaScript applications.


  • Very cheap or free
  • Very fast and high-performing because everything is static


  • Static websites and client-side apps will often require you to rely on web developers to update them (unless the content is served from an API, for which you would need to host separately)
  • You cannot host Content Management Systems like WordPress or Drupal using this type of service
  • You need a web developer to use these services as they are geared towards technical people
  • Github Pages – Free
  • Amazon S3 – monthly from $0.004 per 10,000 requests for data transfers, and from $0.004 per GB of storage

Factors to consider

Search Engine Optimization

If search engine optimization is important to you, you may not want to build a website that is client-rendered using JavaScript since this is generally not indexed by search engines. It may be more advantageous of you to develop a CMS-powered site or a static website in this case, rather than JavaScript application.

Updating the website yourself or relying on a developer

If you are updating the website yourself through a Content Management System like Drupal or WordPress, you will need to choose either shared hosting, a VPS, or a managed hosting service. If you do not want a CMS, you can choose any of the options (except for managed web hosting), but you will need to rely on a web developer to update your website. If you would like to get a website and want to talk about your web hosting options, contact me and I would be happy to discuss your options with you in greater detail.

https://blog.tinaciousdesign.com/web-hosting-recommendationshttps://blog.tinaciousdesign.com/web-hosting-recommendationsWed, 07 Dec 2016 03:23:03 GMT
<![CDATA[Caching in WordPress: Caching and cache busting]]>There are plenty of options for developers and clients in terms of caching in WordPress. You may have heard of popular caching plugins like W3 Total Cache, CDNs like Cloud Flare, or managed hosting services like WP Engine that offer caching features, all of which promise to greatly increase page performance. In this post I'll go over why caching in WordPress is important, the different kinds of cache available to you, and how to bust the cache so that your visitors don't get outdated cached stylesheet and JavaScript assets.

Why should I use caching?

There are plenty of reasons to use caching in WordPress.

It's easy

One of the reasons to implement caching in WordPress is that caching functionality is built into WordPress by default. WordPress automatically appends a version query parameter to the stylesheet URI, which is a nice setup for developers to implement specific criteria for which to cache files. See more in the Different types of caching available section.

It speeds up the performance of your website

A common problem with many low-quality WordPress websites is that the site performance is terrible. This is common in all those trendy click-bait websites you end up visiting from social media networks. One of the reasons those websites are so slow is because they serve advertisements, but it is also highly likely that they have not implemented efficient caching to improve the user experience on their website. If you have a product- or service-based business—rather than one that generates revenue through advertising—you may care about the user experience on your website.

It lightens the load on your server resources to prevent Internal Server Errors

Better caching means that your server will be working less, which is important as your website scales and you get more visitors. You likely already know this, but if your server gets overloaded it means that your website could go offline. This can happen if the server-side script and/or database use too much memory and crash. A common error is one that says "Error establishing a database connection." This error occurs for a number of reasons.

  1. Invalid database credentials: The database host, credentials, or database user permissions for MySQL have been changed. If your website was running fine and you suddenly see this page and haven't modified the database credentials or the WordPress config, this is likely not the issue.
  2. Database corruption: It is possible that your database is corrupt and needs to be repaired. WordPress makes this fairly easy for developers to troubleshoot, but you may need to go into MySQL manually if this step does not work.
  3. Database server is down: MySQL can crash if it uses too much memory and the system kills it.

Implementing server-side caching will help alleviate stress on your server resources, which would prevent your application from using memory when it isn't necessary, which could save you from having to troubleshoot down time on your website.

WordPress database error: Error establishing a database connection

Different types of caching available

There are a variety of different types of caching available, as previously mentioned.

Caching using asset version numbers in WordPress

WordPress ships with a built-in caching feature out-of-the-box. This allows you to assign a version number to your assets. Your visitors' browser then stores this version with the contents of the file in its cache. When the version your browser has is different than the version your website is serving, the browser will request a fresh copy with the new version number. In the source code of your WordPress website you may have noticed a path similar to: http://mydomain.com/wp-content/themes/my-theme/style.css?ver=4.6.1. This is WordPress' built-in caching functionality. The WordPress version is appended to the version query parameter by default, but developers can override it to be more intelligent. With the default option, the cache would only be flushed when you upgrade your WordPress core install, which doesn't happen as often as it should, and could likely happen less often than you update your website. This means that your visitors may not be seeing the newest styles and scripts when you want them to. Please see the section on Cache busting below for more information on solving this problem.

Caching using a Content Delivery Network (CDN)

There are services available called Content Delivery Networks. They provide a number of services, but one of the most valuable ones is caching. A CDN is an intermediary service that sits between your server and your visitors. Traffic goes through the CDN firstly, and then is routed to your server. It essentially acts as a highly-efficient middleman (or woman), and only goes back to your server when necessary. Common CDN providers include CloudFlare and CloudFront. These services can help protect your website against Distributed Denial of Service (DDoS) attacks, which are attacks with the sole purpose of overloading your server resources enough to make it crash. If a CDN is sitting in front of your website, the CDN gets this load, and they have intelligent measures in place to prevent the traffic from going through to your website.

Server-side caching using WordPress plugins on your self-hosted WordPress installation

When hosting your own WordPress website, you are responsible for ensuring that the server stays up, and if you have quick access to a web developer or systems administrator, then this is a great option to avoid the higher costs of managed hosting services. This has the lowest up-front cost, since you can download WordPress and a theme for free, and pay approximately $5-15 per month for web hosting or $5-$20 per month on a Virtual Private Server with root access. There are plugins available—a popular one being W3 Total Cache—which are developed to make WordPress efficient with caching. This plugin is great if you prefer to manage server-side caching at the application level rather than the infrastructure level. Setting up server-side caching using WordPress plugins on a self-hosted WordPress installation involves time and knowledge in many areas, including but not limited to software development, infrastructure, and networking.

Server-side caching in WordPress using a managed hosting service

If you would like to implement server-side caching but do not want to rely on a systems administrator or web developer to manage caching at the application or infrastructure levels, you can opt for a managed hosting service like WP Engine. Managed hosting services come at a higher cost (upwards of $30 per month) because there are teams of developers and developer operations guys and gals standing by to make sure your website never goes down. These managed hosting services can provide great value for clients whose websites experience heavy loads. Managed hosting services often have their own more intelligent forms of server-side caching implemented. These features are available as an alternative to managing caching with plugins on your self-hosted WordPress install. The benefits of going with a managed hosting service can include features such as:

  • Very high and reliable uptime rates—some boasting as much as 99.9% uptime
  • Preventative measures to protect your site from DDoS attacks
  • Other security features, including preventing hackers from brute forcing your website
  • Ensuring your WordPress core installation is the most recent at all times, which means that you have the latest security patches applied automatically without you having to do anything

Managed hosting provides great value, and, in my opinion is worth the additional cost if it is important that your website doesn't go down.

Cache busting to ensure users get the freshest assets

Client-side cache busting

As mentioned above, WordPress comes with client-side caching out-of-the-box. Currently it appends the WordPress core version to the file. It's much more intelligent to have the version code be generated using the file modified date to output, for example, this: style.css?ver=201612050921, which is a much more accurate representation of when you would like to update the file from cache. If the file has been modified and the version your browser tries to get is different than the version stored, then the browser will get the newer version. You can read the file modified date in your functions file using PHP.

Cache busting using a CDN

You can also ensure that your cache is busted when you deploy a new version of your site. Content Delivery Networks like CloudFlare have an API endpoint that you can hit to programmatically clear cache after a deploy. If you automate your site deployment (or deploy with one command), you can also add cache purging to the automation process. In order to clear your CDN cache programmatically, you will need to learn how to use the API of your CDN, which would likely involve creating/finding and using an API key and your website's identifier, and making an API request to the specific endpoint with instructions to clear the cache.

Cache busting server-side

Self-hosted WordPress

Depending on your setup, this could vary. If you use W3 Total Cache on a self-hosted WordPress installation, you will need to clear the cache through their interface in your WordPress dashboard.

Managed hosting on WP Engine (or similar)

If you used a managed hosting service like WP Engine, you can easily do that through their menu option in the WordPress dashboard. In the general settings, there is a button that says Purge All Caches. You can click that to purge the cache.

Clear cache on WP Engine using the WP Engine Purge All Caches button

You don't always have to purge the cache on a managed hosting service, but if you encounter issues, you may want to:

We aggressively cache everything from pages to feeds to 301-redirects on sub-domains; this makes your site load lightning-fast for your non-logged-in readers. 99.9% of the time this is what you want, but every once in a while something happens where our cache should have been purged but wasn't. For example, some URL plugins change behavior without alerting WordPress. You use this button to purge all caches -- on our caching proxies, on the CDN, in memcached, everything.


Essentially, caching in WordPress is a great idea that is easy to implement. It will increase your site speed and reliability, which makes the User Experience of your website much more enjoyable. Caching using a Content Delivery Network offloads some work from your server, and client-side caching in the browser means your users only download the assets they need. Purging cache is fairly simple for a skilled developer to implement. If you have a WordPress site there really is no reason to not use caching—it'll make your life and the life of your visitors easier! Contact me if you'd like to discuss getting a WordPress website or improving your current one.

https://blog.tinaciousdesign.com/caching-in-wordpress-caching-and-cache-bustinghttps://blog.tinaciousdesign.com/caching-in-wordpress-caching-and-cache-bustingTue, 06 Dec 2016 08:00:00 GMT
<![CDATA[Syntax theme for Atom, VSCode, and Sublime]]>I've designed a syntax theme that works in the most popular text editors for front-end development, including Visual Studio Code, Sublime Text, and Atom.

A dark syntax theme

The syntax theme is dark with high-contrast colours. The primary colour is a deep pink, with complementary colours including purple, blue, orange, and green. Syntax theme LESS syntax

A syntax theme for JavaScript and CSS

When developing this theme, I wanted it to look great in JavaScript, since that is where I spend a lot of my time. Photo of Tinacious Design syntax theme in an Ember.js application Screenshot of Tinacious Design syntax theme in an ES6 React app with JSX

Editor support

The theme is supported in a number of editors:

  • Visual Studio Code
  • Sublime Text
  • Atom

Visual Studio Code

You can get the theme from the Visual Studio Marketplace page. The feature set has been updated in April 2020. You can read about the updated features here. You can also install it right from VSCode by running the following command in the command palette:

ext install theme-tinaciousdesign

It should take you to the extensions tab.

Tinacious Design Light theme

Tinacious Design Dark theme

Sublime Text

You can get the theme for Sublime from Package Control. It's available from the install theme command—simply search "Tinacious Design."

Installing Tinacious Design syntax theme in Sublime Text


You can get it from the Atom themes site. You can also install it right from Atom—just go to the Settings, click on the Install tab, click Themes beside the search bar, and type in "Tinacious" or "tinacious-design."

Installing Tinacious Design syntax theme from the Install tab in Atom

https://blog.tinaciousdesign.com/syntax-theme-for-atom-vscode-and-sublimehttps://blog.tinaciousdesign.com/syntax-theme-for-atom-vscode-and-sublimeFri, 19 Aug 2016 02:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596511723177/sdGIZFJvW.png
<![CDATA[dev domain extension for local development with Vagrant]]>I like using a dev domain extension for some of my websites in local development. When doing local development using Vagrant, you often have to configure specific IP addresses for your website or service. For example, if I configure my Vagrant host URL to be http://192.168.xx.yy I would have to visit that link in the browser. That's inconvenient to type—I'd rather type a real domain name, one similar to my production site. For this website, I've made my local dev domain host tinaciousdesign.dev.

1. Find your vagrant URL

This can be found in your Vagrantfile. Mine looks like this:

Vagrant.configure("2") do |config|
  # ...
  config.vm.network "private_network", ip: "192.168.xx.yy"
  # ...

2. Point your Vagrant URL to your dev domain

You can do so by modifying the hosts file found at /etc/hosts. You'll need root privileges to modify this file:

# Host Database
# localhost is used to configure the loopback interface
##    localhost    broadcasthost
::1             localhost

192.168.xx.yy   tinaciousdesign.dev

3. Visit your new domain

Now go to your browser and visit your new URL with the dev domain extension. You should see it load your Vagrant website. As of the most recent version of Chrome, this no longer works without configuring SSL. For now, visit your site in Firefox or Safari. Stay tuned for another blog post about that.

https://blog.tinaciousdesign.com/dev-domain-extension-for-local-development-with-vagranthttps://blog.tinaciousdesign.com/dev-domain-extension-for-local-development-with-vagrantWed, 17 Feb 2016 03:23:03 GMT
<![CDATA[How I built an Instagram image gallery using Node.js and Ember.js]]>I built an image gallery application that uses the Instagram API. The application displays posts of a specific hash tag from a user (me) with a private Instagram account. It's mostly just a bunch of photos of me drinking coffee and coding but a little insight into the life of Tina from Tinacious Design! This Instagram client was built as a proof of concept to build something in Ember utilizing the Instagram API. Ember itself does not communicate with the Instagram API but rather consumes data that a back-end service that communicates with Instagram has made available. There are both server-side and client-side parts to this application:

  1. A back-end written in Node.js that authenticates against and communicates with the Instagram API
  2. A front-end written in Ember.js that consumes the server-generated data.

You can continue reading to learn how it was done.

Exposing posts in a private Instagram account

The application exposes specific posts using a specific hash tag of a private Instagram account. These posts cannot be seen in the Instagram app unless you are one of my Instagram friends, but because I have authenticated with my account, the posts become available. It is a way to expose specific posts to the public while maintaining the privacy of the account. Essentially, I tag my posts with #tinaciousdesign and then run a script to hit the Instagram API to generate the data that my Ember app can use.

Communicating with Instagram

In order to communicate with Instagram, you need to authenticate with the Instagram API using OAuth. Setting this up can be a tricky process. Since my application was just built to display my own Instagram posts with a specific hash tag, my use case did not require the support of user authentication. My solution instead was to create an endpoint that once hit would make a request to the Instagram API with the required token and get all of the Instagram posts with that specific hash tag.

The server-side application built in Node.js

The basis of the application is very simple. It's a Node.js application that uses Hapi.js to create a server and endpoint to hit in order to spawn off the process.


The route is very simple and uses Hapi.js and a custom handler. More about the photosHandlerlater, but here is how you set up a simple route in Hapi.js:

const Hapi = require('hapi');
const server = new Hapi.Server();

  port: process.env.PORT || 1337, 
  routes: { cors: true } 
  method: 'GET', 
  path: '/photos', 
  handler: photosHandler 

server.start(() => {
  console.log(`Server running at ${server.info.uri}`)

Contacting Instagram

Initially I tried some of the Instagram wrappers out there for Node.js but I found that they were not necessary. I instead opted to just use the request module, a simple module. It's very similar to the request package in Python. In order to make requests to the Instagram API, a token is required. Setting up authentication and obtaining a token can be tricky. I'm not going to get into it. For the sake of this tutorial, the access token is available as an environment variable. Below is a Hapi.js route handler function. I'm not going to get into Hapi.js but essentially, the callback takes a request and a reply object. We just need to pass the reply object to the next function,requestUserMedia, which is the method that does all of the magic:

const photosHandler = (req, reply) => {
  const requestUri = `https://api.instagram.com/v1/users/self/media/recent?access_token=${process.env.TOKEN}`;

  requestUserMedia(requestUri, reply);

The requestUserMedia method takes the request URI and the reply object, and makes a request to the Instagram API for the recent media of the specified user, in this case self, which is the authenticated user's recent media.

const requestUserMedia = (url, reply) => {
  console.log(`Requesting: ${url}`);

  request.get(url, { json: true }, (err, res, body) => {
    // More code goes here

I used the request module to make a GET request to the provided Instagram API URL with the access token, requesting JSON format, and providing a callback. The callback is in the standard Node.js callback format, where the error is the first returned parameter, followed by the response object and the response body.

Cherry picking Instagram posts

The posts I want are the ones with the hash tag #tinaciousdesign. Conveniently, the response object has a property tags which is an array of all of the available hash tags. I can use this value to find out if my posts are tagged in the request callback. You can see all available properties in theInstagram API documentation. Node.js best practices usually involve handling the error first, e.g.:

if (error) {
  return reply({ message: 'Failure.', error }).code(res.statusCode);

I just relay the Instagram API error message and status code back to the client. Now let's talk about the happy path! Assuming we have no error, we should have received body, the third parameter in the request callback. Here's how we can iterate over all of the posts we received:

let tinaciousDesignPosts = [];

body.data.forEach(media, index => {
  if (media.tags.indexOf('tinaciousdesign') >= 0) {

Doing this should return about 10 items. Wait, we don't want 10, we want all of them! One of the reasons I opted to use the Node request module instead of an Instagram wrapper is lack of support (or documentation) for pagination. Thankfully, the Instagram API documentation has the information I need in order to figure out how to get the next items. There is a property on the body called pagination which has a property called next_url. This URL is the next one we need to call in order to get more images. Thankfully, since the logic to make requests for Instagram posts was put in its own function, this is as easy as calling the method with our new URL:

if (body.pagination.next_url) {
  requestUserMedia(body.pagination.next_url, reply);

We want to return early from the next step and recursively call the requestUserMedia function until we have all of the posts with the specified hash tag. Once we have reached the last page of our posts and body.pagination.next_url returns false, we can prepare the data in a nice consumable format we can use and return it to the client:

let data = tinaciousDesignPosts.map(post => {
  return {
    id: index,
    thumbnail: post.images.thumbnail.url,
    image: post.images.standard_resolution.url,
    caption: post.caption.text,
    date_utc: parseInt(post.created_time, 10)
return reply({ data });

The fields I need:

  • id for Ember
  • thumbnail for the gallery view
  • full-sized image for the single image view
  • caption to display with the full-sized image
  • date in UTC

Once it's all in a nice package called data I can do a Hapi reply with it. The provided data looks like this:

  "data": [
      "id": 1,
      "thumbnail": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/s150x150/e35/10012553_435208453335078_714900051_n.jpg",
      "image": "https://scontent.cdninstagram.com/hphotos-xap1/t51.2885-15/s640x640/sh0.08/e35/10012553_435208453335078_714900051_n.jpg",
      "caption": "JavaScript app development in React. A nice hot cuppa tea and some fancy chocolate truffles. ☕️ #tinaciousdesign",
      "date_utc": 1451420323
      "id": 3,
      "thumbnail": "https://scontent.cdninstagram.com/hphotos-xpf1/t51.2885-15/s150x150/e35/12256593_1661803950744133_1845805885_n.jpg",
      "image": "https://scontent.cdninstagram.com/hphotos-xpf1/t51.2885-15/s640x640/sh0.08/e35/12256593_1661803950744133_1845805885_n.jpg",
      "caption": "Coffee and code ☕️ #tinaciousdesign",
      "date_utc": 1448749779
    // ... More posts here ...

And that's it for the server-side portion!

The client-side application built in Ember.js

Using the data in Ember

The Ember app is quite basic and utilizes the generated JSON as a fixture. I create a model with the fields caption, image and thumbnail, and paste the resulting JSON as the fixture. The rest includes some controller methods to help with paginating the single image post.

The next trick is to create square thumbnails that take up all of the space available while only spanning to their full width, i.e. 100 pixels. When visiting the page, resize the window and see how the thumbnails behave. With the help of a CSS preprocessor, I was able to create a for loop that iterated over a range of numbers and generated a bunch of media queries, calculating the required breakpoint and thumbnail width for each media query. I chose Stylus, but you can do the same with Sass. The resulting output of looks like this:

@media screen and (min-width:300px) {
  .thumbnail {
    width: 33.333333333333336%;

@media screen and (min-width:450px) {
  .thumbnail {
    width: 25%;

@media screen and (min-width:600px) {
  .thumbnail {
    width: 20%;

@media screen and (min-width:750px) {
  .thumbnail {
    width: 16.666666666666668%;
/* ... etc., all the way to (min-width: 4500px) ... */

Yay, math! That goes up to @media screen and (min-width:4500px). Imagine writing that out all by hand! With a couple of lines of Stylus and a for loop, we can let the preprocessor do the math and grunt work for us. There was somewhere around 28 to 30 media queries generated with about 4 lines of Stylus.

Check it out!

Now that you had a quick peek at how it was built, check it out! If you've built something with the Instagram API or a different API and would like to share, feel free to leave a comment with links to some code samples!

https://blog.tinaciousdesign.com/how-i-built-an-instagram-image-gallery-using-nodejs-and-emberjshttps://blog.tinaciousdesign.com/how-i-built-an-instagram-image-gallery-using-nodejs-and-emberjsTue, 05 Jan 2016 02:07:43 GMT
<![CDATA[Canned Replies Chrome Extension for faster replies in Gmail]]>I developed the Canned Replies Chrome Extension to make it easier for me to manage text snippets for commonly-sent replies. It uses the Inbox SDK to create a seamless experience within Gmail. It uses Chrome Storage to sync and manage your text snippets across Chrome installs of the same profile, and offers exporting and importing features to share your snippets between Chrome profiles. Feel free to check out the portfolio entry with screenshots or download the Canned Replies Chrome Extension from the Chrome Web Store. If you like it, please rate it on the Chrome Web Store and share it on social media using the share links below.

https://blog.tinaciousdesign.com/canned-replies-chrome-extension-for-faster-replies-in-gmailhttps://blog.tinaciousdesign.com/canned-replies-chrome-extension-for-faster-replies-in-gmailMon, 04 Jan 2016 03:23:03 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596510207792/X7F9ooP3J.png
<![CDATA[IE-friendly mobile-first CSS]]>Almost everyone we know and do business with has a smart phone, and these people are using their phones to browse online. A common argument to not doing mobile-first responsive design is supporting Internet Explorer 8 and under. In this post I'll talk about doing both with IE-friendly mobile-first CSS.

Why do mobile-first?

Two great reasons:

  1. Not everyone has a generous data plan
  2. Not everyone has the latest, most powerful smart phone

When we go mobile-first, we serve only the files that the mobile user will be using, and ensure that those files are optimized for their devices. That includes optimizing images for a user's screen size with responsive images, making sure your videos are fluid and responsive, as well as optimizing your CSS and JS so as to only load what's required for each browser.

Why should I not do mobile-first?

Because it's easier to go desktop-first when you have to add support for Internet Explorer 8 and other browsers that don't support media queries. And that's about it.

Mobile-first responsive design with CSS preprocessors

The old way of doing responsive design

What often happens with the usual responsive design that isn't mobile-first, is that CSS is declared for desktop and then overridden for mobile. This means that we specify a value twice when we could either specify it only once or not at all (if it isn't needed). We want our body background colour to be white for mobile and pink for desktop. The usual responsive design would declare it pink and then use a media query that targets phones to override it and make it white. Here's an example of what not to do:

/* don't do this! ;) */
body {
    background-color: #f39;

/* lots of CSS goes here */
/* ... */
/* and then somewhere at the bottom of your file */

@media (max-width: 959px) {
    body {
        background-color: #fff;

The better way using in-context media queries

The following snippet shows how we can use Stylus, one of the many flavours of CSS preprocessor available, to declare our background colour only for desktop:

desktop = '(min-width: 960px)'
pink = #f39

    @media desktop
        background-color pink

That's literally 5 lines of code where 2 of them are variable declarations, compared to the regular CSS way, which is 8 lines, and the media query possibly lives quite far from the original declaration. With media queries nested inside of the element, we can see everything in context. With this approach, we can modularize components, and break modules out into separate files to keep it cleaner.

IE-friendly mobile-first CSS with Gulp

So I have mobile-first CSS—now what do I do for Internet Explorer?

Unfortunately, Internet Explorer 8 and under cannot read media queries, which means that our mobile-first websites end up looking terrible in these browsers. In order to convert our mobile-first CSS into IE-friendly mobile-first CSS, we need to parse all of the CSS declared in the media queries, write it to a separate stylesheet, and load that stylesheet for Internet Explorer 8 and under. Like CSS preprocessors, we can post-process CSS with similar tools. This solution uses a build tool called Gulp and a couple of contributor modules to post-process the CSS file. Just add the following to your Gulpfile.js:

var gulp = require('gulp');
var nmq = require('gulp-no-media-queries');
var rename = require('gulp-rename');

gulp.task('iestyles', function () {
        width: '960px' || '480px'
        path.basename = 'ie';
        path.extname = '.css';

The stars of the show are no-media-queries and the Gulp plugin gulp-no-media-queries. There's also a Grunt plugin. The Gulpfile code above, once run through Gulp with its helper modules, will get the compiled file in css/styles.css, run it through a tool that strips out media queries, and output the results in dist/css/ie.css. Once we have all of the required Node.js modules, we can run our Gulpfile:

gulp iestyles

This solution is great but developer beware: when developing your CSS, you need to declare your styles in order of most mobile to least mobile, i.e. no breakpoints (phones), tablet breakpoint, and then desktop breakpoint. You can selectively choose which media queries to pluck out styles for. The above configuration assumes that Internet Explorer will use both tablet (480px) and desktop (960px) styles.


So there you have it! That's how you can get IE-friendly mobile-first CSS. A summary of the tools and approaches used:

  • Node.js for using all of the mentioned build tools
  • Stylus for CSS-preprocessing
  • modular components with media queries nested with their module, declared from smallest screen to largest
  • Gulp.js + some helper modules for stripping out media queries, renaming the file, and outputting it where we want it

Mobile-first Responsive Design while still supporting old browsers used to be harder but with great build tools like Node.js, Stylus, Grunt, and Gulp, we can configure our development environment and automate our build processes to make CSS development for phones and old browsers a breeze.

https://blog.tinaciousdesign.com/ie-friendly-mobile-first-csshttps://blog.tinaciousdesign.com/ie-friendly-mobile-first-cssMon, 28 Dec 2015 03:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596510965505/8v4fsTSq_.png
<![CDATA[Dollars 4 Scholars Startup Weekend Toronto prototype]]>I developed the prototype for the Dollars 4 Scholars Startup Weekend Toronto project November 2013. Startup Weekend Toronto was a hackathon, and this one had the theme of developing innovative, education-focused solutions. The Dollars 4 Scholars Startup Weekend Toronto project is a web-based product that aims to bring merit-based scholarships to students based on their marks and achievements. I did the front-end development, implementing the designs provided by a talented graphic designer, and creating a simulated prototype of how the app is intended to function. You can view the YouTube video here as well as read about the project below.

Dollars 4 Scholars home page design

The home page has a brief intro, a tally of scholarships paid out through Dollars 4 Scholars, and an introductory video of how the service is intended to work. Users can log in to the app from here, update their marks and view the scholarships they are eligible for.

Dollars 4 Scholars Startup Weekend Toronto project home page

Fully Responsive Design

The website prototype for the Dollars 4 Scholars app is fully responsive and optimized for mobile phones. You can access everything on the website from your phone just as if you were on your computer.

Dollars 4 Scholars Startup Weekend Toronto mobile menu


Users will be able to login either from their phones or their computers and access their dashboard.

Dollars 4 Scholars Startup Weekend Toronto login screen


The Dollars 4 Scholars dashboard allows users to alter their grades and view their eligibility for scholarships.

Dollars 4 Scholars Startup Weekend Toronto dashboard

Again, you can see the Dollars 4 Scholarship prototype in action here on YouTube.

https://blog.tinaciousdesign.com/dollars-4-scholars-startup-weekend-toronto-prototypehttps://blog.tinaciousdesign.com/dollars-4-scholars-startup-weekend-toronto-prototypeFri, 13 Nov 2015 03:23:02 GMT
<![CDATA[Get a visitor's IP address and email it with Mandrill in Go on Heroku]]>I developed a simple web application in Go that when visited would find the visitor's IP address and email it via Mandrill. This application is a proof of concept, simply to try my hand at building an application in Go:

  • utilizing dependency management (i.e. GoDeps)
  • performing practical tasks like obtaining IP address information from behind a firewall (as with Nginx or Heroku)
  • interacting with an API, i.e. Mandrill
  • deploying a Go application to Heroku

This tool is ready to go out-of-the box—all that's required is setting some environment variables as per the documentation to customize it to work with your email address, redirect URL, and Mandrill account. Check it out on Github.

https://blog.tinaciousdesign.com/get-a-visitors-ip-address-and-email-it-with-mandrill-in-go-on-herokuhttps://blog.tinaciousdesign.com/get-a-visitors-ip-address-and-email-it-with-mandrill-in-go-on-herokuMon, 17 Aug 2015 07:00:00 GMT

    share-title="Article Title">



https://blog.tinaciousdesign.com/social-media-sharing-in-angularjshttps://blog.tinaciousdesign.com/social-media-sharing-in-angularjsMon, 13 Apr 2015 02:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596512201902/tD4ZbuVMk.png
<![CDATA[Installing or migrating ProcessWire on Ubuntu]]>ProcessWire is an open-source, PHP/MySQL-based content management system. It’s one of my favourite systems to use for building powerful, scalable websites and applications. You can see how easy it is to install ProcessWire on Ubuntu in the following articles. Digital Ocean is an affordable cloud hosting platform built for developers. They use solid state drives for all their virtual private servers and have a very user-friendly admin panel. I authored some documentation that gives step by step instructions on how to install ProcessWire on Ubuntu, or migrate an existing ProcessWire website or app to Ubuntu using Digital Ocean. You can read them here:

https://blog.tinaciousdesign.com/installing-or-migrating-processwire-on-ubuntuhttps://blog.tinaciousdesign.com/installing-or-migrating-processwire-on-ubuntuSat, 04 Apr 2015 02:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596511996086/nkBlc9PLM.png
<![CDATA[Progress Bar jQuery plugin]]>I developed this Animated Progress Bar jQuery plugin to simplify adding an animated bar graph on a website. Some example scenarios where a tool like this is useful:

  • Signatures collected
  • Funds / Donations raised
  • Skill level for designer/developer infographics

To use, just fill in the required input and the plugin calculates the progress of your goal and animates the bar. You can specify text to put before and after the amount raised. With all the right moves, this plugin can be integrated into a content management system-powered website. If you like this and want to use it on your CMS-powered site, feel free to contact me about developing a WordPress or ProcessWire version of this plugin. Progress bar screenshot You are welcome to download the plugin and use it in your personal projects provided that you leave the credits in the source code. Adding this plugin to your website using jQuery is as easy as copying and pasting the following code:

$(document).ready(function() {
        goalAmount: 150,
        currentAmount: 100,
        textBefore: '',
        textAfter: ' raised'

You can download the jQuery plugin from Github and see the live demo here. This plugin can also easily be added to any Angular application by injecting it into your application's module and adding this small snippet of code:

<div goal-progress current="100" goal="150" before="$" after=" raised!"></div>

For support with AngularJS, see this repository.

https://blog.tinaciousdesign.com/progress-bar-jquery-pluginhttps://blog.tinaciousdesign.com/progress-bar-jquery-pluginSat, 14 Feb 2015 03:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596512638319/g5cynJX9F.png
<![CDATA[Developing a DocPad website and deploying it to an Ubuntu server]]>Lately, I've been doing a lot of development in Node.js and decided I was going to change my website over to a Node.js-powered tool. I was looking for something lightweight, simple, and optimized for developers.

My experience with DocPad

After a bit of research, I opted to try Docpad, an open-source static site generator built using Node.js. This tool is a pretty good tool for developers, allowing you to author your content in Markdown format and generate static HTML files. I opted for a database-free installation, so all of my site's content can be checked into version control as text files that are later generated into HTML on the server. There are many plugins available to extend the basic functionality of a DocPad site, including front-end frameworks, and integration with the build tools you already love, like CSS preprocessing with Less, Sass, and Stylus, JavaScript preprocessing, and HTML templating engines. There are still a few quirks with it, and, from my experience, and that of others, performance during development can be slow or come to a halt (i.e. file watching and re-rendering)—and this problem grows as your site does—but it works for small, low-maintenance sites. I would not personally use it for enterprise or client sites—unless your client's site will be very small and they will be completely hands-off—but it's a great, open-source tool with a good amount of plugins available for commonly desired functionality and build tools. At the time of this article, DocPad is actively being maintained. I think DocPad is in its early stages and has a lot of potential to be a great tool for developer websites provided that the performance kinks get worked out.

Serving a DocPad website on Ubuntu

Ubuntu is my operating system of choice when it comes to serving a website. Digital Ocean is an affordable VPS platform with plans as low as $5 per month with 512 MB of RAM and 20 GB of storage on a solid-state drive. A LAMP stack would require the next plan up to run reliably but the $5 plan works great for a static site with minimal dynamic functionality. I wrote a tutorial for Digital Ocean that provides step-by-step instructions on deploying a DocPad website to Ubuntu. If you are interested in learning how to serve a DocPad website on an Ubuntu VPS, please see the tutorial article How to Deploy a DocPad Application on Ubuntu.

https://blog.tinaciousdesign.com/developing-a-docpad-website-and-deploying-it-to-an-ubuntu-serverhttps://blog.tinaciousdesign.com/developing-a-docpad-website-and-deploying-it-to-an-ubuntu-serverMon, 19 Jan 2015 03:23:02 GMT
<![CDATA[SVG to PNG fallback in AngularJS]]>SVG images are great for crisp graphics on retina displays. They are a necessity in any responsive design website in this day and age but the file format is not supported in older browsers. You can get SVG to PNG fallback in AngularJS with this simple directive. To address dynamically loaded images in AngularJS we can use a directive. I developed a simple directive that works off of the image element whose only feature is to replace the SVG file extension with PNG in browsers that do not support SVG. Provided that you have a PNG with the same file name in the same location as the SVG file, this solution will work perfectly. The SVG to PNG fallback in AngularJS directive is available for download and install into your Angular apps using Bower or can be manually downloaded. Check out angular-svg-png on Github. SVG to PNG fallback in AngularJS preview in the browser

https://blog.tinaciousdesign.com/svg-to-png-fallback-in-angularjshttps://blog.tinaciousdesign.com/svg-to-png-fallback-in-angularjsSat, 17 Jan 2015 03:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596513447814/T501hyp-5.png
<![CDATA[Fluid video jQuery plugin]]>One of the challenges in Responsive Web Design is making Vimeo and YouTube videos respond to the user’s device or screen size. I developed a fluid responsive video jQuery plugin that makes it easy for designers and developers to add fluid and responsive videos for use in their Responsive Design projects. Designers and developers simply copy the files to their server, call them in their HTML head and initialize the function on document ready passing through required parameters:

  1. Import jQuery
  2. Import the plugin files
  3. Create an HTML element to target for the video to append to. This can and usually should be empty.
  4. Initialize the script in the document ready and specify video information: video ID and type (Vimeo or YouTube).

The target element will be appended with the specified video. Files include working and minified versions of the plugin, required CSS file without any styles, a demo and instructions on implementation. Implementing the plugin is as easy as creating an empty element in your HTML, and initializing the plugin with that element:

        id: 'idAbc123', // Video ID goes here
        type: 'vimeo', // `youtube` or `vimeo`

Download the plugin from Github.

Fluid video jQuery plugin screenshot

https://blog.tinaciousdesign.com/fluid-video-jquery-pluginhttps://blog.tinaciousdesign.com/fluid-video-jquery-pluginSat, 17 Jan 2015 03:23:02 GMT
<![CDATA[Responsive images in AngularJS with Picturefill]]>Picturefill is a great responsive images plugin. It is used for implementing responsive images, and serves up different image sizes based on screen size and pixel density. This means mobile users don’t download huge images and retina screens don’t see fuzzy, pixelated images. I wanted to have responsive images in AngularJS but Picturefill didn't work out of the box with dynaimc images because the images loaded dynamically, but only after the plugin was fired. To solve this problem, I developed an AngularJS directive to fire the Picturefill plugin at the right moment. The images are populated dynamically by Angular’s scope object and the plugin is fired after the images have loaded. It’s also important to note that data-src used to call the plugin conflicts with Angular’s ng-src so I developed a workaround: pf-src is how you declare the image source using this Angular directive. The directive can be downloaded and installed into your project using Bower and Grunt. To implement this directive with static images in your Angular project, you can do so like this:

<span picture-fill data-alt="Juicy hanger steak on a croissant.">
    <span pf-src="images/sizes/steak-croissant.png"></span>
    <span pf-src="images/sizes/steak-croissant-100.png" data-media="(min-width: 1px)"></span>
    <span pf-src="images/sizes/steak-croissant-300.png" data-media="(min-width: 400px)"></span>
    <span pf-src="images/sizes/steak-croissant-600.png" data-media="(min-width: 645px)"></span>
    <span pf-src="images/sizes/steak-croissant-1024.png" data-media="(min-width: 960px)"></span>
    <span pf-src="images/sizes/steak-croissant.png" data-media="(min-width: 1200px)"></span>

For updated usage documentation, as well as documentation for using Picturefill with dynamic data, please see the project page on Github. To see it in action, view the Responsive images in AngularJS demo.

https://blog.tinaciousdesign.com/responsive-images-in-angularjs-with-picturefillhttps://blog.tinaciousdesign.com/responsive-images-in-angularjs-with-picturefillSat, 17 Jan 2015 03:23:02 GMThttps://cdn.hashnode.com/res/hashnode/image/upload/v1596512861018/Xg820KxCf.png
<![CDATA[Generate iOS app icons with this easy Node.js script]]>I created a Node.js-based utility that uses ImageMagick to automatically generate iOS app icons for use in Cordova projects and native iOS app development projects.

Easily generate iOS app icons

The tool is very easy to use. The only prerequisite is that you have Node.js installed on your computer. Once that's done, simply provide the image as the first argument to the Node-executed script, as follows:

node index sample.png

Using the tool is as easy as dragging and dropping the image into the Terminal. Resulting output of the iOS icon generating script To use this tool, download it from the Github repository.

https://blog.tinaciousdesign.com/generate-ios-app-icons-with-this-easy-nodejs-scripthttps://blog.tinaciousdesign.com/generate-ios-app-icons-with-this-easy-nodejs-scriptSat, 17 Jan 2015 03:23:02 GMT