How I Build Scalable Modern Web Applications for Real Users

CSS Library — None

I really wanted to have full control over the user experience and design, so I decided to code the entire UI from scratch. This allows me to optimize for the value that I’m trying to deliver. All my components are being built by hand with the exception of some lower-level libraries to facilitate functionality.

One thing I am considering adding is Tailwind CSS. I’ve found myself writing a lot of boilerplate styles and wasting time. I also dread building new components for simple things. I think Tailwind will help be an additional benefit to further improve iteration speed.

Also, accessibility (a11y) is very important to me, and from my research, not many frameworks do a great job. It was also an opportunity for me to really understand how to correctly build an accessible website and continue to improve it over time. It’s an ongoing process, but my hope is that I’m building something that’s usable by everyone.

The important pieces of accessibility that I’ve learned:

  • The most important thing is well-structured HTML. Computers understand how to handle this.
  • Manage cursor and focus.
  • Use a screen reader to test pages.
  • Aria should only need to be used for a small number of cases when HTML structure isn’t enough.

If you’re experienced with a11y please comment and let me know if something is incorrect or key improvements I can add!

CSS-in-JS for UI Styles

I was reluctant for a long time to adopt this approach, but now I consider it an essential part of my stack. I’m using Styled Components, but I’ve also had an excellent experience in the past using Emotion.

CSS-in-JS allows me to think of the UI style as simply a component in my app. The styled-components can receive props and can dictate the way the style renders based on the state of the application.

It also allows you to share styles using the standard import/export syntax, so you’re always just writing JavaScript.

So instead of doing this where our component manages the style:

We simply pass the prop to our styled component, and it decides how to render:

I used to be against CSS-in-JS because I thought it broke separation of concerns, but after years of painfully managing classes to dictate styles, I realized the concerns aren’t separate at all. The style, state, and layout of your application are so tightly coupled, that it makes more sense to unify them.

So, it takes a different view on separation of concerns — instead of a component managing the class names to style a component, the component that actually is changing its style receives the data and makes the decision.

Markdown Extended (MDX) for Content

I was torn between using a content management system (CMS) or writing the lessons inside the codebase. I want to add dynamic functionality to the content to improve the educational experience which means I likely need to wire JavaScript up to it — this means a CMS isn’t really an option since it’s static strings.

This made MDX the perfect option. It allows me to write content using markdown inside the codebase, but also marry it with React. So, I can import components into my markdown to add additional functionality without sacrificing the ease of how I write lessons.

As an example, I wanted to help my users visualize what it means to recursively generator permutations, so I built a component that generates them and embedded it alongside my text content:

My backend requires very little coding. I get a full GraphQL API managed by a dashboard through Hasura which is perfect for handling CRUD operations. For more complex business logic, I use API routes provided by Next.js (from my frontend), so it takes minimal additional effort.

The one area that I did things the hard way is authentication. I coded that all myself because any provider requires heavy lock-in, and I wasn’t ready to commit yet.

GraphQL with Hasura

Hasura has saved me hundreds of hours of coding and has totally changed how I think about my stack. Their dashboard allows you to manage your database with a user interface, and it creates migration files for you automatically. Hasura also builds your GraphQL API with all the necessary CRUD operations.

About: admin