Drop #646 (2025-04-30): Web-Slinging Wednesday

CSS text-box-trim; 12-Bits; CSS Shapes

We’ll use the midweek Drop as a literal palette cleanser as we cover some clever CSS capabilities.

Type your email…

Subscribe

TL;DR

(This is an LLM/GPT-generated summary of today’s Drop using Ollama + Qwen 3 and a custom prompt.)

I did switch over to Qwen 3 and, so far: so good!

CSS text-box-trim

CSS text-box-trim is a new property designed to give us precise control over the vertical space above and below text within its container, addressing a long-standing challenge in web typography and layout. Historically, the space around text — especially the extra space above and below — has been dictated by the font’s metrics and the web’s handling of “half-leading,” which splits the line spacing (leading) equally above and below the text. This often results in inconsistent and unpredictable spacing, making it difficult to achieve optical balance and true alignment, especially when working with different fonts or aiming for perfectly centered text in buttons, badges, or headings.

The property allows you to trim the “over” (top) and “under” (bottom) edges of a text box, effectively removing the extra vertical space that comes from the font’s internal metrics. This is particularly useful for components where you want equal padding or precise alignment with other elements, such as icons or images.

The syntax is straightforward:

  • text-box-trim: trim-both; trims both the top and bottom.
  • text-box-trim: trim-start; trims just the top.
  • text-box-trim: trim-end; trims just the bottom.
  • text-box-trim: none; (default) makes no adjustment.

We can pair text-box-trim with text-box-edge to specify exactly where the trimming should align-such as the top of capital letters (cap), the x-height of lowercase letters (ex), or the baseline (alphabetic):

h1 { text-box: trim-both cap alphabetic;}

This example trims the top to the cap height and the bottom to the alphabetic baseline, which is a common use case for visually balanced headings.

Before this new properts we often had to use trial and error with padding values to make text look optically centered in buttons or aligned with adjacent images. For example, you might set padding-block: 5px and padding-inline: 10px to offset the unwanted space, but this solution is fragile and varies across fonts and platforms. With text-box-trim, you can confidently use equal padding (e.g., padding: 10px) and know the result will be visually balanced.

Many demos and playgrounds like this one are now available to help us see and tweak these effects in real time. We can experiment with different fonts, trim values, and see how trimming only one side or both affects the layout.

As of early 2025, text-box-trim is supported in Chrome 133+ and Safari 18.2+, with ongoing work for broader adoption.

The linked post has some great examples, links, and more technical details.

12-Bits

Kate Morley designed the 12-bit rainbow palette with twelve carefully chosen colors for data visualization. This palette debuted in the National Grid: Live project, focusing on human color perception across luminance, chroma, and hue.

As we’ve somewhat covered in more than a few Drops, standard RGB color systems treat red, green, and blue equally, but human vision processes these differently. Green appears brighter than red, while blue looks darker. This creates jarring brightness shifts in RGB-based rainbow palettes, causing problems in visualizations needing smooth transitions.

Kate addressed this using the LCH (Luminance, Chroma, Hue) color space. LCH offers perceptual uniformity, where equal numerical changes in any component create visually equivalent changes regardless of starting color. When varying hue while keeping chroma and luminance constant, colors appear equally spaced to viewers.

Simply fixing chroma and luminance while changing hue doesn’t produce an effective rainbow. Yellow looks muddy at low luminance, red becomes pink at high luminance, and blue appears washed out with increased luminance. The solution allows controlled luminance variation: yellow receives the highest luminance (since yellow only appears yellow when bright), with red and blue serving as anchors. Luminance for other hues creates smooth transitions across the spectrum.

The “12-bit” name refers to color depth: each palette color uses just four hexadecimal digits (like #e94), equaling 12 bits of information. This constraint slightly limits available colors, but adjustments required for 12-bit compatibility remain visually imperceptible. The result features evenly spaced hues, minimal chroma variation, and smooth luminance variation, creating an effective and compact visualization tool.

Here are some handy, pre-built data structures for the palette for R, JavaScript, and CSS, plus a full set of {ggplot2} palettes in {hrbrthemes}:

c( plum = "#817", rose = "#a35", coral = "#c66", apricot = "#e94", lemon = "#ed0", lime = "#9d5", mint = "#4d8", teal = "#2cb", sky = "#0bc", azure = "#09c", cobalt = "#36b", violet = "#639") -> bit12 const bit12 = ["#817","#a35","#c66","#e94","#ed0","#9d5","#4d8","#2cb","#0bc","#09c","#36b","#639"]; :root { --plum: #817; --rose: #a35; --coral: #c66; --apricot:#e94; --lemon: #ed0; --lime: #9d5; --mint: #4d8; --teal: #2cb; --sky: #0bc; --azure: #09c; --cobalt: #36b; --violet: #639;}

CSS Shapes

The CSS Shapes Module Level 1 and Level 2 specifications introduce modern and spiffy ways to control how content flows around and within elements using arbitrary shapes, moving beyond the traditional rectangular box model.

Module Level 1 focuses on defining shapes for float areas.

A float area is the region defined around a floated element that determines how surrounding inline content, such as text, wraps around it. By default, when you float an element using the float property (with values like left or right), the float area is the element’s margin box, meaning the content wraps around the outermost edge of the element, including its margins.

It introduces properties like shape-outside, which allows a floated element to define a non-rectangular float area using basic shapes (such as circle()ellipse()polygon()inset(), and path()) or by referencing images and box edges (like margin-box or border-box). These shapes determine how inline content wraps around floats. For example, you can float an image to the left and use shape-outside: circle(50%) to make text wrap around a circular area instead of the image’s rectangular bounds. The module also introduces shape-margin, which expands the float area outward from the defined shape, and shape-image-threshold, which sets the opacity cutoff for extracting shapes from images. Importantly, these shapes only reduce the float area-they cannot extend it beyond the float’s margin box, and the underlying box model, including stacking and positioning, remains unaffected. The module is strictly limited to floats and initial-letter boxes, although it anticipates future expansion to other elements and contexts.

Module Level 2 builds on this foundation by extending shape application beyond floats to exclusions and, perhaps more importantly, by introducing the shape-inside property. With shape-inside, you can define a non-rectangular area inside a block-level element, causing the element’s content to flow within the specified shape, rather than filling the usual rectangle. This enables layouts such as text flowing inside a circle or along a custom path. Level 2 also introduces the shape-padding property, which adds padding inside the shape defined by shape-inside, analogous to how shape-margin works outside shapes. The new shape() function is a more flexible and CSS-native alternative to the SVG-inspired path(), allowing for dynamic, parametric, and responsive shapes using standard CSS syntax, units, and variables. Additionally, Level 2 allows referencing SVG shapes directly via url() and expands the image-based shape extraction mechanism. The properties from Level 1, like shape-outsideshape-margin, and shape-image-threshold, are updated to apply to exclusions and the new inside shapes as appropriate.

It’s much easier to see/play how this all works (though it is important to read through the specs).

MDN has a super nice resource for this, and the code for the section header can be found in their playground.

You can also find tons of pre-built CSS shapes on sites like “The Ultimate CSS Shapes Collection”.

FIN

Remember, you can follow and interact with the full text of The Daily Drop’s free posts on:

  • 🐘 Mastodon via @dailydrop.hrbrmstr.dev@dailydrop.hrbrmstr.dev
  • 🦋 Bluesky via https://bsky.app/profile/dailydrop.hrbrmstr.dev.web.brid.gy

☮️

#09c #0bc #2cb #36b #4d8 #639 #817 #9d5 #a35 #c66 #ed0

CSS text-box-trim  |  Blog  |  Chrome for Developers

Take back space from above and below your text content; achieve optical balance.

Chrome for Developers
The 12-bit rainbow palette
----
- 7 minutes ago | 3 points | 1 comments
- URL: https://iamkate.com/data/12-bit-rainbow/
- Discussions: https://news.ycombinator.com/item?id=43827108
- Summary: The 12-bit rainbow palette was designed for use on National Grid: Live. It consists of 12 colors chosen for their perceived luminance, chroma, and hue. The palette uses a 12-bit color depth, allowing each color to be specified with a 4-character hexadecimal code. The designer used the LCH color space to create a palette with evenly-spaced hues and smoothly increasing and decreasing luminance. The resulting palette has 12 colors: #817, #a35, #c66, #e94, #ed0, #9d5, #4d8, #2cb, #0bc, #09c, #36b, and #639. The palette was designed to avoid large changes in luminance between neighboring colors, creating a more visually pleasing effect.
The 12-bit rainbow palette

A palette of twelve colours chosen with consideration for how we perceive luminance, chroma, and hue

sketch_2022_02_01 #Processing #Python #py5 imported mode #shapely #trimesh #3D

import trimesh
import shapely

# https://iamkate.com/data/12-bit-rainbow/
palette = (
'#817', '#a35', '#c66', '#e94',
'#ed0', '#9d5', '#4d8', '#2cb',
'#0bc', '#09c', '#36b', '#639'
)

def setup():
global m
size(400, 400, P3D)
no_stroke()
polygon = shapely.geometry.Polygon([(-100, -100), (0, -100),
(0, 0), (-50, -50), (-100, 0)])
m = trimesh.creation.extrude_polygon(polygon, 30)

def draw():
background(0)
translate(width /2, height / 2)
rotate_x(QUARTER_PI)
rotate_y(radians(mouse_x))
for i, face in enumerate(m.faces):
fill(palette[i % 12])
with begin_closed_shape():
vertices([m.vertices[v] for v in face])

The 12-bit rainbow palette

A palette of twelve colours chosen with consideration for how we perceive luminance, chroma, and hue

#Processing #Python #py5 #genuary #genuary31 #トゥートProcessing

# https://iamkate.com/data/12-bit-rainbow/
palette = (
'#817', '#a35', '#c66', '#e94',
'#ed0', '#9d5', '#4d8', '#2cb',
'#0bc', '#09c', '#36b', '#639'
)

def setup():
size(800, 800)
no_stroke()
background(0)

def draw():
xc = yc = 400
for i in range(6):
m = 1 - abs(cos(radians(frame_count / 2))) ** 5
r = 150 + 150 * m
a = radians(frame_count / 2 + 60 * i)
x = xc + r * cos(a)
y = yc + r * sin(a)
fill(palette[i])
circle(x, y, 150)
r = 300 - 150 * m
a = a + radians(30)
x = xc + r * cos(a)
y = yc + r * sin(a)
fill(palette[-1 -i])
circle(x, y, 150)

The 12-bit rainbow palette

A palette of twelve colours chosen with consideration for how we perceive luminance, chroma, and hue

#Processing #Python #py5 imported mode #genuary #genuary30 #トゥートProcessing
# Kate Rose Morley's palette
# https://iamkate.com/data/12-bit-rainbow/

from itertools import product

palette = (
'#817', '#a35', '#c66', '#e94',
'#ed0', '#9d5', '#4d8', '#2cb',
'#0bc', '#09c', '#36b', '#639'
)

def setup():
global palavras
size(800, 800)
no_loop()
rect_mode(CENTER)
no_stroke()

def draw():
w = 400
i = 0
for x, y in product(range(0, width, w), repeat=2):
for z in range(3):
fill(palette[i])
square(w / 2 + x, w / 2 + y, w / (z / 2 + 1))
i += 1

The 12-bit rainbow palette

A palette of twelve colours chosen with consideration for how we perceive luminance, chroma, and hue

# Kate Rose Morley's palette
# https://iamkate.com/data/12-bit-rainbow/ @kate
#Processing #Python #py5 imported mode style
#genuary #genuary29 "maximalism"

from random import sample
from itertools import product

palette = (
'#817', '#a35', '#c66', '#e94',
'#ed0', '#9d5', '#4d8', '#2cb',
'#0bc', '#09c', '#36b', '#639'
)

def setup():
global palavras
size(800, 800)
no_loop()
rect_mode(CENTER)
no_stroke()

def draw():
w = 100
for x, y in product(range(0, width, w), repeat=2):
d = width // w
cores = sample(palette, d)
for z in range(d):
fill(cores[z])
square(w / 2 + x, w / 2 + y, w / (z / 2 + 1))

def key_pressed():
redraw()

The 12-bit rainbow palette

A palette of twelve colours chosen with consideration for how we perceive luminance, chroma, and hue

×

#Processing #Python #py5 imported mode #genuary #genuary30 #トゥートProcessing
# Kate Rose Morley's palette
# https://iamkate.com/data/12-bit-rainbow/

from itertools import product

palette = (
'#817', '#a35', '#c66', '#e94',
'#ed0', '#9d5', '#4d8', '#2cb',
'#0bc', '#09c', '#36b', '#639'
)

def setup():
global palavras
size(800, 800)
no_loop()
rect_mode(CENTER)
no_stroke()

def draw():
w = 400
i = 0
for x, y in product(range(0, width, w), repeat=2):
for z in range(3):
fill(palette[i])
square(w / 2 + x, w / 2 + y, w / (z / 2 + 1))
i += 1