Methodology
Hereâs how we build websites and web applications in 2024.
HyperText Markup Language
Weâre going to go out on a limb and declare that HTMLâin all its piecemeal and compromised gloryâis the greatest âsemantic communication syntaxâ ever invented by humanity. Thus itâs a real shame how abused and neglected HTML has gotten in some corners of the industry over the years. Our skin crawls at the mere hint of HTML as a low-level âmachine formatâ never intended for reading and writing by mere mortals.
Despite its many critics, HTML has not only survived against the onslaught of numerous competing systems over its 35-year history, it has so solidly established itself as a universal and boundary-defying cultural treasure that we have no doubt HTML will still be with us 100 years from now.
We prize and value HTML. Everything starts with HTML. Before we consider what CSS or JS frameworks to use, what build tooling to introduce, where to store and retrieve data, how to deploy the final product, and so forth, we start with the breadth of meaning and functionality we wish to express through HTML. Itâs the baseplate of all web development.
Custom Elements
As we begin to build out the structure of the site in generalized templates, we consider which built-in and custom elements weâll need for expressing the intent of each section of each page. There are many built-in elements in HTML and they should always be prioritized wherever suitable.
<nav>
for a navbar. <h1>
for a primary heading. <article>
to represent a unit of content. But for situations where a custom element is required, we will liberally define and use those throughout our projectsâtag names such as <layout-column>
, <ui-label>
, <footer-inner>
, or <main-content>
.
Weâve essentially stopped using <div>
and <span>
in all projects going forward because they convey no semantic meaning and serve no useful purpose in an era when custom elements are baked into the fabric of the HTML standard. In case thereâs any confusion, weâre not talking about web components (yet). In our lexicon, custom elements are HTML-only tags which optionally can be used for styling via CSS or scripting via JavaScript. In the case of the latter, read onâ¦
Web Components
The emergence of the web components standard is perhaps the greatest leap forward for HTML since the arrival of the <img>
tag. With web components, you can program bits of encapsulated functionality which are able to be embedded in any web page ânativelyâ.
For example, HTML provides a <textarea>
tag. But anyone could write their own <fancy-textarea>
tag which uses either <textarea>
under the hood or offers a bespoke editor built out of other HTML/CSS/JavaScript primitives. To you, the downstream HTML author, it doesnât matter. Use <textarea>
or <fancy-textarea>
or <super-dee-dooper-textarea>
because of the capability each component affords, not because of its implementation details.
Some legacy JavaScript component libraries such as React have struggled to fully embrace and encorporate web components. Thatâs on them, not a knock against the web components spec. We choose to utilize newer, lightweight libraries which take full advantage of web components and well as other modern patterns such as signals.
We sometimes may opt to write frontend code in a Ruby-derived syntax with 1:1 transpilation provided by Ruby2JS. Because thereâs no runtime required, the output JavaScript looks much the same as if we hand-coded it ourselves. Pretty neat! (See our Tech Specs page for further details.)
Token-based Semantic CSS Design Systems
Building upon the semantic elements and web components methodology above, we have recently made a fundamental upgrade to our approach to CSS (Cascading Style Sheets). Itâs a methodology so new weâre not entirely sure thereâs a name for it yet, but it all starts with CSS Custom Properties/Variables and some forward-looking syntactic sugar provided by PostCSS.
We start by defining a series of âtokensâ as custom properties defined on :root
in a global stylesheet. We often co-mingle them with tokens imported from a library such as Open Props or Shoelace (more on that below). Example tokens might be --base-font-size: 125%
, --primary-color: #ff6f59
, or --max-content-width: 50rem
. We even create tokens for responsive breakpoints (not yet browser-native, but enabled by PostCSS). You can see these sorts of :root
-based design tokens on this very website by opening your developer inspector.
After a basic design system is in place, we begin creating styles using only element names as selectors. section
, p
, a
, main
, etc.âas well as custom elements such as navbar-inner
. We use classes sparingly (no .foo.bar .baz
here!) while readily reaching for attribute selectors, especially for custom elements, e.g. sl-input[size="medium"]
. Occasionally we might override design tokens for particular element scopes, or within responsive media queries. In addition, when using web components which offer CSS Shadow Parts for advanced styling, weâll use those as well when strictly necessary (sl-dialog::part(title)
for example).
This combination of CSS Variables, element/attribute selectors, and the mechanisms provided by Shadow DOM + Parts has resulted in a shocking reduction in the amount of CSS we write as well as import. In the past you couldnât do much quickly without reaching for something like Bootstrap. Lately that would be the main selling point of Tailwind. (Er, use with extreme caution!). However, we increasingly find ourselves not needing any âCSS frameworkâ at allâ¦only some simple boilerplate and typically a web component-based UI library such as Shoelace.
Shoelace
Soon to become Web Awesome! Created by Cory LaViska, Shoelace is the most impressive unified collection of web components and design tokens weâve seen to date. Thatâs saying a lot considering there are component libraries out now from Salesforce, Microsoft, Google, Adobe, GitHub, and many other large companies.
Shoelace at first glance might seem like Yet-Another-Bunch-oâ-Components with the usual suspects of buttons, icons, menus, and dropdownâhowever, such simple appearances can be deceiving. What makes Shoelace so impressive are five things:
- It looks great right out of the box.
- It takes full advantage of modern web component standards.
- Itâs extremely customizable, but only if you really need to.
- The HTML you write using Shoelace is fantastically elegant.
- Shoelace ships with a comprehensive set of design tokens you can use directly.
A button in Shoelace is <sl-button>Hi!</sl-button>
. An icon is <sl-icon name="person-circle"></sl-icon>
. A star rating is <sl-rating precision=".5" value="2.5"></sl-rating>
. On that last example, you can see how element attributes allow for precise control over various component properties. All properties are also controllable of course through JavaScript while requiring no additional library or framework of any kind.
You can customize how Shoelace looks simply by overriding various design tokens via CSS variables, and you can also use Shoelace tokens directly in your own styles and markupâeven inside of inline styles! For example: <h1 style="margin-bottom:var(--sl-spacing-2x-large)">Lots of Space</h1>
We regularly reach for Shoelace as the default UI library for ambitious projects, and love that it pairs so well with a variety of design systems and frontend stacks.
Bridgetown + Roda: Like Peanut Butter & Jelly
Now that our basic frontend stack is set, letâs turn our attention to the backend. We unabashedly employ Ruby for its natural language-like expressiveness and object-oriented intellectual rigor. Ruby was designed from the ground-up to maximize programmer happiness and endow tiny teams with fantastic productivity. Itâs our âforce multiplierâ secret sauce to get more done with less.
When evaluating the backend technology for a project, we start by examining the type of project, how it will be used, and what is expected by its audience. Working backward from user experience, we choose the appropriate developer experience we believe makes the most sense.
Typically, if the site is a âpublicationââ¦meaning itâs essentially content-driven (e.g., marketing sites/brochureware, blogs, educational destinations, etc.), then weâll go the static site route and use Bridgetown.
If the site is primarily a dynamic application orbiting a databaseârequiring user authentication and up-to-the-second live dataâweâll build out a fullstack Roda app with Sequel and Rodauth to offer the necessary dashboard, API endpoints, and more.
Increasingly though, weâre finding that Bridgetownâs static-first approach (aka âJamstackâ) coupled with dynamic routes powered by its native integration of Roda is a potent combination for a variety of applications. Itâs true we have extensive experience with Rails as well. But this year, weâre taking the bits we like and paring them with a dramatically different (and much cheaper!) take on Ruby web development. Best of both worlds? Most definitely!
Cloudinary
In a break from conventional wisdom, we generally shy away from relying on third-party APIs for functionality. Sure, using APIs for things like email delivery or error monitoring is pretty much a given. Otherwise we believe in the value of building and hosting features in-house. That being said, there is an external API we add to virtually every project we work onâno matter the stackâand that is Cloudinary.
Cloudinary, simply put, is a CDN and live transformation service for images and videos. You upload high-resolution, large-size images to Cloudinary, and then you ask Cloudinary back for exactly the size, quality, and format you need. They all get transformed on the fly and subsequently cached for maximum performance. Files can be stored and retrieved solely through Cloudinaryâs API, or they additionally provide a full web-based UI where you can view, edit, upload, and delete images. Itâs about as awesome as image management can get.
Markdown
As a shorthand method of authoring textual content intended for HTML generation, Markdown has become a defacto standard. We love Markdown, and we constantly reach for tools (both native apps as well as web services or libraries) that make working with Markdown a delight. Weâve helped train non-technical content authors how to utilize Markdown effectively, and weâre committed to building better editing experiences that take Markdown to greater heights without sacrificing the developer story.
Next-Generation Hosts
The story of web hosting has changed dramatically over the past few years. Between hosting companies like Render or Vercel offering essentially free hosting for static sites, you can get incredible performance and full scalability and security at minimal cost. And for more powerful dynamic Ruby applications, convention-over-configuration hosts such as Render, Fly.io, and Railway are changing the way we deploy applications by focusing out-of-the-gate on simple framework detection, containerization, and multi-region availability.
This is a rapidly-evolving space and what makes sense today might not make sense tomorrow. Generally speaking, weâre not super-thrilled with that sort of churn. But weâre bullish on next-gen hosting solutions which place zero-config or easily-configurable build artifacts at their core. Weâre trying our best to work out the kinks, document our findings, and make this easier for everybody. May the best cloud win.
For more information on some of the tools we use and build on projects like these, visit our Tech Specs page. Or visit Resources to learn more about applying techniques such as these to your own projects.