HTML: Making browsers wrap long words

By default, browsers will not wrap text at syllable boundaries. Text is wrapped at word boundaries only.

This card explains some options to make browsers wrap inside a long word like "Donaudampfschifffahrt".

Option 1: hyphens CSS property (preferred)

Modern browsers can hyphenate natively. Use the hyphens CSS property:

hyphens: auto

There is also hyphens: none (disable hyphenations even at ­ entities) and hyphens: manual (hy...

Defining new elements for your HTML document

Browsers come with a set of built-in elements like <p> or <input>. When we need a new component not covered by that, we often build it from <div> and <span> tags. An alternative is to introduce a new element, like <my-element>.

When a browser encounters an unknown element like <my-element>, the browser will proceed to render <my-element>'s children. The visual rendering of your page will not be affected.

If you care about their HTML being valid, your new element should contain a dash character (-) to mark it as a *custom el...

Using attribute event handlers with a strict Content Security Policy (CSP)

Given you have a strict CSP that only allows <script src> elements from your own domain:

Content-Security-Policy: script-src 'self'

This will block JavaScript handlers inlined as attribute into your HTML elements. Clicking on the following link will only log an error with a strict CSP:

<a href="javascript:alert('hello')">click me</a>
<a href="#" onclick="alert('hello')">click me</a>

Solution 1: Move the handler into your JavaScript

The recommended solution is to move the handler from the HTML to the allowed ...

Caution when using the || operator to set defaults

I often see the use of || to set a default value for a variable that might be nil, null or undefined.

x = x || 'default-value'

This pattern should be avoided in all languages.

While using || works as intended when x is null or an actual object, it also sets the default value for other falsy values, such as false. false is a non-blank value that you never want to override with a default.

To make it worse, languages like JavaScript or Perl have [many more fal...

Unpoly: Showing the better_errors page when Rails raises an error

When an AJAX request raises an exception on the server, Rails will show a minimal error page with only basic information. Because all Unpoly updates work using AJAX requests, you won't get the more detailled better_errors page with the interactive REPL.

Below is an event listener that automatically repeats the request as a full-page load if your development error shows an error page. This means you get...

Does <html> or <body> scroll the page?

TL;DR: All modern browsers default to using the <html> element as the main document viewport. In CSS, prefer to set overflow properties to html (or :root).

Scrolling the main viewport with JavaScript

The browser's main document viewport is also scrollable by default. The element that corresponds to the main viewport is either <html> (document.documentElement) or <body> (document.body). Which one depends on the browser.

When you want to update the current `sc...

Tasks, microtasks, queues and schedules - JakeArchibald.com

The way that Javascript schedules timeouts and promise callbacks is more complicated than you think. This can be the reason why callbacks are not executed in the order that they are queued.

Please read this article!


This is an extract of the example in the article which demonstrates the execution order of tasks and microtasks.

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
})...

How to make your application assets cachable in Rails

Note: Modern Rails has two build pipelines, the asset pipeline (or "Sprockets") and Webpacker. The principles below apply for both, but the examples shown are for Sprockets.


Every page in your application uses many assets, such as images, javascripts and stylesheets. Without your intervention, the browser will request these assets again and again on every request. There is no magic in Rails that gives you automatic caching for assets. In fact, if you haven't been paying attention to this, your application is probabl...

Accessibility: Making non-standard elements interactive

A common cause of non-accessible web pages are elements that were made interactive via JavaScript but cannot be focused or activated with anything but the mouse.

❌ Bad example

Let's take a look at a common example:

<form filter>
  <input filter--query name="query" type="text">
  <span filter--reset>Clear Search</span>
</form>

The HTML above is being activated with an Unpoly compiler like this:

up.compiler('[filter]', function(filterForm) {
  const resetButton = filterForm.querySelec...

Lazy-loading images

Note

This card does not reflect the current state of lazy loading technologies. The native lazy attribute could be used, which is supported by all major browsers since 2022.

Since images are magnitudes larger in file size than text (HTML, CSS, Javascript) is, loading the images of a large web page takes a significant amount of the total load time. When your internet connection is good, this is usually not an issue. However, users with limited bandwidth (i.e. on mobile) need to mine their data budget...

Escape a string for transportation in a URL

To safely transport an arbitrary string within a URL, you need to percent-encode characters that have a particular meaning in URLs, like & or =.

If you are using Rails URL helpers like movies_path(:query => ARBITRARY_STRING_HERE), Rails will take care of the encoding for you. If you are building URLs manually, you need to follow this guide.

Ruby

In Ruby, use CGI.escape:

# ✅ good
CGI.escape('foo=foo&bar=bar')
=> "foo%3Dfoo%26bar%3Dbar"

Do not ever use `URI.en...

Event delegation (without jQuery)

Event delegation is a pattern where a container element has a single event listener that handles events for all descendants that match a CSS selector.

This pattern was popularized by jQuery that lets you do this:

$('.container').on('click', '.message', function(event) {
  console.log("A message element was clicked!")
})

This technique has some advantages:

  1. When you have many descendants, you save time by only registering a single listener.
  2. When the descendants are changed dynamic...

Code splitting in esbuild: Caveats and setup

Code splitting is a feature of esbuild that can keep huge libraries out of the main bundle.

How code splitting works

Like Webpack esbuild lets you use the await import() function to load code on demand:

// application.js
const { fun } = await import('library.js')

fun()

However, esbuild's code splitting is disabled by default. The code above would simply inline (copy) `l...

What we know about PDFKit

What PDFKit is

  • PDFKit converts a web page to a PDF document. It uses a Webkit engine under the hood.
  • For you as a web developer this means you can keep using the technology you are familar with and don't need to learn LaTeX. All you need is a pretty print-stylesheet.

How to use it from your Rails application

  • You can have PDFKit render a website by simply calling PDFKit.new('http://google.com').to_file('google.pdf'). You can then send the...

How to auto-resize a textarea (or other inputs) in pure CSS

Modern CSS offers the field-sizing property to allow elements to automatically adjust size (width and/or height) to fit their contents.
The most common use case are textareas which start fairly small (e.g. 2 or 3 rows tall) but grow when users enter longer text.

Usage

textarea {
  field-sizing: content;
}

That's it! At least in modern Chromium-based browsers.

Limited browser support

Support is still lacking in Firefox and Safari (as of mid 2025)....

Playing audio in a browser

If you want to play music or sounds from a browser, your choice is to use either Flash or the new <audio> tag in HTML5. Each method has issues, but depending on your requirements you might not care about all of them.

Flash

  • Works in all desktop browsers, even Internet Explorer. Does not work on iPads or iPhones.
  • Requires you to embed a Flash component into your page which will later play the audio for you.
  • Can play MP3s or Wave files. Cannot play OGG Vorbis audio.
  • Cannot reliably seek to a given position when playing VBR-enco...

Webpacker: Configuring browser compatibility

Webpacker uses Babel and Webpack to transpile modern JavaScript down to EcmaScript 5. Depending on what browser a project needs to support, the final Webpack output needs to be different. E.g. when we need to support IE11 we can rely on fewer JavaScript features. Hence our output will be more verbose than when we only need support modern browsers.

Rails 5.1+ projects often use Webpacker to preconfigure the Webpack pipeline for us. The default configuration works something like this:

  1. Webpack checks w...

Understanding the Selenium error "Modal Dialog Present" (aka Selenium::WebDriver::Error::UnhandledAlertError)

So your Cucumber feature sometimes dies with this exception:

Modal Dialog Present (Selenium::WebDriver::Error::UnhandledAlertError)

As a seasoned Selenium veteran you are used to misleading error messages. Hence you might be surprised that the reason for this particular error is that there is actually a modal dialog present and preventing Selenium from executing commands like click or page.have_css?.

How your code triggers this issue

The reason why a dialog is shown is somewhat fucked ...

Unpoly 3.12.0 released

This release adds asynchronous compilers and many other features requested by the community.
We also fixed a number of performance regressions introduced by Unpoly 3.11.

Breaking changes are marked with a ⚠️ emoji and polyfilled by unpoly-migrate.js.

Asynchronous compilers

Compiler functions can now be [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...

Lightning Talk: Coverage based Test Case Prioritization in Ruby on Rails

For my computer science bachelor's thesis I programmed and evaluated a CLI Test Case Prioritization (TCP) tool for makandra. It has been written as a Ruby Gem and was tested and evaluated against one Ruby on Rails project. This card will summarize and present the research results, the evaluation and the programmed CLI tool.

The code has been published for educational purposes on GitHub. The german bachelor's thesis has also been included for download at the end.


...

Integrating ESLint

Introduction

To ensure a consistent code style for JavaScript code, we use ESLint. The workflow is similar to integrating rubocop for Ruby code.

1. Adding the gem to an existing code base

You can add the following lines to your package.json under devDependencies:

"devDependencies": {
  "@eslint/js": "x",
  "@stylistic/eslint-plugin": "x",
  "eslint": "x",
  "eslint-plugin-import": "x",
  "globals": "x",
}

...

Upgrading Rails 2 from 2.3.8 through 2.3.18 to Rails LTS

This card shows how to upgrade a Rails 2 application from Rails 2.3.8 through every single patch level up to 2.3.18, and then, hopefully, Rails LTS.

2.3.8 to 2.3.9

This release has many minor changes and fixes to prepare your application for Rails 3.

Step-by-step upgrade instructions:

  1. Upgrade rails gem
  2. Change your environment.rb so it says RAILS_GEM_VERSION = '2.3.9'
  3. Change your ...

How to avoid multiple versions of a package in yarn

To avoid multiple versions of a package, you can manually maintain a resolutions section in your package.json. We recommend you to do this for packages like jQuery. Otherwise the jQuery library attached to window might not include the functions of your packages that depend on jQuery.

Note: This is only an issue in case you want to use a package functionality from window e.g. $(...).datepicker() from your dev console or any other javascript within the application.

Background

By default yarn will create a folder node_modules ...

SASS: Adding, removing and converting units

Adding a unit

Multiply by 1x the unit:

$number = 13
$length = $number * 1px // => 13px

Removing a unit

Divide by 1x the unit:

$length = 13px
$number = $length / 1px // => 13

Converting a unit

the result of an addition or subtraction between two numbers of different units is expressed in the first member’s unit

Thus, to convert a number, add it to 0 of the desired unit:

$duration: .21s
$duration-in-milliseconds: 0ms + $duration // => 210ms

An example is storing a transition duration as CS...