Controlling smooth scrolling in browsers
It can be hard to understand what causes a browser scroll smoothly or instantly. CSS, JavaScript and the browser settings all have options to influence the scroll behavior. They all interact with each other and sometimes use the same words for different behavior.
CSS
Scrolling elements can use the scroll-behavior
CSS property to express a preference between smooth and instant scrolling.
Preferring instant scrolling
CSS can prefer instant scrolling behavior:
html {
scroll-behavior: auto; /* the default */
}
An `aut...
How to apply transparency to any color with pure CSS
To apply transparency to an element, you can use opacity
in CSS. However, sometimes you don't want to make the entire element transparent, only a color.
Using not-so-modern CSS, you can simply generate non-opaque versions of a color. This card describes how to do that.
Note that we're not talking about defining colors with transparency. This is about the case where you have a color but need a more transparent variant of it (e.g. for a border, background, etc.), and where your component does that without defining the transparent variant ...
How to invert currentColor with pure CSS
The currentColor CSS keyword references the current text color and can be used to apply an element's text color to other properties.
Using modern CSS, you can also use it to calculate colors from it. This card describes how to invert currentColor
, but you can use this technique for other calculations as well.
How to invert currentColor
They key is CSS' hsl()
function and from
keyword for it:
--inverted-color: hsl(from ...
Selecting the nth element matching a selector
The :nth-child
pseudo class is commonly used for targeting elements based on their position within a parent container, for example the third element in a list or every even-numbered item. However, :nth-child
can do more.
In modern CSS specifications (Level 4), there’s an additional feature that lets you use :nth-child
in combination with a list of css selectors. This way, you can ta...
Implementing upload progress and remove button with ActiveStorage DirectUpload
DirectUpload allows you to upload files to your file storage without having to wait for the form to submit. It creates AJAX requests to persist the file within your form and wraps them in a little API. This card will show you how to use it in order to create in-place file uploads with progress and a remove button.
This is basic functionality, you may add additional elements, styles and logic to make this look fancy, but the core functionality is the same. I created a file upload that looks like this:

that parses the HTML and returns a reference to all created elements:
let [list, item1, item2] = htmlFixtures(`
<ul type="square">
<li>item 1</li>
<li>item 2</li>
</ul>
`)
Because the effective HTML is visible...
How to write a good image alt text
This decision tree describes how to use the alt attribute of the
<img>
element in various situations. For some types of images, there are alternative approaches, such as using CSS background images for decorative images or web fonts instead of images of text.
Questions asked:
- Does the image contain text?
- Is the image used in a link or a button, and would it be hard or impossible to understand what the link or the button does, if the image wasn’t there?
- Does the image contri...
CSS & a11y: When hiding with opacity, also set visibility:hidden (transitions supported)
Elements can be hidden and shown by toggling the display
property. However, this is not animatable, so we often turn to opacity
. At opacity: 0
, the element is hidden, and with a nice transition
on that property, it can be faded in and out smoothly.
Yet, opacity
only hides visually, not technically: the element is still focusable and visible to screen readers. So, how can we fade an element while maintaining accessibility?
Enter visibility
. It also hides elements, bu...
How to combine unknown CSS selectors
You are given two CSS selectors that you do not control. How can you build a new selector that matches both of them?
item_selector = 'div'
active_selector = '.is-active'
Can't I just concat these selectors?
# Bad
new_selector = "#{item_selector}#{active_selector}"
# => "div.is-active"
Don't! This will break as soon as one of the selectors is actually a selector list.
item_selector = 'div, span, p' # <- Selector list
new_selector # => "div, span, p.is-active" (wrong)
Solution
Wrap both selectors ...
JavaScript: Humanizing durations
Modern JavaScript includes Intl.NumberFormat to format numbers in different formats and locales.
In this card, we describe a wrapper for it that humanizes a given number of seconds in the "next best" unit, like seconds, minutes, etc.
Example usage
>> new Duration(42).humanized()
=> '42 Sekunden'
>> new Duration(123456).humanized()
=> '1 Tag'
>> new Duration(123456).humanized('es')
=> '1 día'
Code
Here is the code ...
Open UI: Future development in web components and controls
tl;dr When browsers start to adapt proposals from Open UI, it might not be necessary to use any 3rd party libraries to have nice components and controls in web applications e.g. selects. It would require only a minimum of CSS and Javascript to get them working and looking good.
The purpose of the Open UI, a W3C Community Group, is to allow web developers to style and extend built-in web UI components and controls, such as
<select>
dropdowns, checkboxes, radio buttons, and date/color pickers.To do that, we’ll need to fully speci...
Caution: rem in @media query definitions ignore your font-size
Note
Using rem only ever makes sense when the root font size is dynamic, i.e. you leave control to the user. Either by a) respecting their user agent defaults, or by b) offering multiple root font sizes in your application.
By defining @media queries in rem, they will accommodate to the root font size of your page. At a larger root font, breakpoints will be at larger widths, scaling with the font. However, there is a catch in case b) mentioned in the note above.
Relative length units in media queries are based on the initial value,...
Grid by Example: a website about CSS Grid
Rachel Andrew has built a website about CSS Grid.
- Video tutorials
- More than 30 layout examples for feature demonstration
- Layout patterns for copy-paste use
- All grouped by topic: "Placing items onto the grid", "Sizing of tracks and items" etc. with video, linked articles, examples each
Top Accessibility Errors in 2023
These are the top ten accessibility errors as researched by TPGi, a company focusing on accessibility. See the linked article for details on each item, as well as instructions on how to do it correctly.
- No link text
- Non-active element in tab order
- Missing link alt attribute
- No alt text
- List not nested correctly
- Duplicate labels used
- Positive tabindex value
- Invalid aria-describedby
- No label for button element
- Invalid aria-labelledby
Generally, I am surprised by these items. I would have expected more complex i...
You should be using the Web Animations API
The Web Animations API has great browser support, and you should be using it to animate DOM elements from JavaScript, or to control or wait for CSS animations.
Here is a quick overview of a few useful features:
Animating elements from JavaScript
Use the Element#animate() function to perform animations on an element.
Its API probably a bit different from how your...
In Chrome 121+ the now supported spec-compliant scrollbar properties override the non-standard `-webkit-scrollbar-*` styles
Up until Chrome 120, scrollbars could only be styled using the various -webkit-scrollbar-*
pseudo elements, e.g. to make the scrollbars have no arrows, be rounded, or with additional margin towards their container.
Starting with version 121, Chrome now also supports the spec-compliant properties scrollbar-width
and scrollbar-color
.
These allow less styling. You may only specify the track and thumb colors, and a non-specific width like auto
, thin
, or none
.
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...
CSS: Letting text flow around a round element
If you have an element with significant border-radius (e.g. 50%
for a circle) and you want inline content (i.e. text) to flow around it, do it like this:
- Place the element right before the text and
float: right
orfloat: left
- Tell the browser to take the
content-box
for the element's shape, i.e. without margin, padding and border.shape-outside: content-box
- Set the
margin
where you want it, e.g. 10px left and bottom - Set the
shape-margin
to the same size ...
Livereload + esbuild
Getting CSS (and JS) live reloading to work in a esbuild / Rails project is a bit of a hassle, but the following seems to work decently well.
We assume that you already use a standard "esbuild in Rails" setup, and have an esbuild watcher running that picks up your source code in app/assets
and compiles to public/assets
; if not change the paths below accordingly.
Basic idea
We will
- use the
guard-livereload
gem as the livereload server (which send updates to the browser), - use the
livereload-js
npm package in the browser to con...
List of default browser stylesheets
Even when you app has no CSS at all, you still inherit a default user agent stylesheet from your browser.
These default styles vary from browser to browser:
Links found in A look at CSS Resets in 2018.
A reasonable default CSP for Rails projects
Every modern Rails app should have a Content Security Policy enabled.
Very compatible default
The following "default" is a minimal policy that should
- "just work" for almost all applications
- give you most of the benefits of a CSP
In your config/initializers/content_security_policy.rb
, set
Rails.application.config.content_security_policy do |policy|
policy.object_src :none
policy.script_src :unsafe_eval, :strict_dynamic, :https # Browsers with support for "'strict-dynamic'" will ignore "https:"
po...
RSpec: Leverage the power of Capybara Finders and Matchers for view specs
View specs are a powerful tool to test several rendering paths by their cases instead of using a more costing feature spec. This is especially useful because they become quite convenient when used with Capybara::Node::Finders and Capybara::RSpecMatchers. This allows to wirte view unit specs as you can isolate specific part...
Spreewald, Cucumber: Selector for the nth element
The recommended additional setup of the spreewald gem, a useful set of cucumber steps, includes adding a file for defining custom selectors which can be used as prose within steps:
When I follow "Edit" within the controls section
Where the controls section
can be any arbitrary defined css selector within selectors.rb
Often it can be useful to select the nth element of a specific selector. Luckily, this can ...
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.
...