This Month in Ladybird: August 2025
Hello friends! We just finished August in style with 244 merged PRs from 43 contributors! Let's take a look at some highlights..
Welcoming new sponsors
Ladybird is entirely funded by the generous support of companies and individuals who believe in the open web. This month, we're excited to welcome the following new sponsors:
- Bastian Müller with $5,000 (renewed sponsorship!)
- Timely Learning with $1,000
- OakHost with fast macOS CI infrastructure
We're incredibly grateful for their support. If you're interested in sponsoring the project, please contact us.
Web Platform Tests (WPT)
Progress on WPT continues. This month we squeezed out 8,106 new passes for a total of 1,839,962.
For context, here are the current top 6 browser engines and their WPT scores today vs one month ago.
Google Sheets support
This month we got Google's web-based spreadsheet running in Ladybird! We've got a lot of correctness and performance work to do here, but it's great to see major web apps start working.
The most impactful change here was fixing a bug where 2D canvas elements stopped updating visually after changing the canvas width or height (PR #5976).
Gamepad API
We've implemented the Gamepad API, which allows you to connect a gamepad and use it in Ladybird. (PR #5902)
This is built using the SDL3 Gamepad API, providing cross-platform support, with many connection methods working out-of-the-box, such as Bluetooth.
Cookie Store API
We landed support for the Cookie Store API. This brings modern cookie handling with promise-based methods (get
, getAll
, set
, delete
) and CookieChangeEvent
notifications, replacing the old document.cookie
string parsing. (PR #5766)
CSS env()
function
The CSS env()
function lets authors style the page based on "environment variables": properties of the user's device,
for example the safe area of the screen on a non-rectangular display, or the user's preferred text scale. This month we
implemented support for it. (PR #5736)
Most of these environment variables apply more to mobile devices, but we now have the groundwork for future environment variables, as well as upcoming CSS features that allow author-defined variables.
CSS Typed OM API
Since JavaScript was first able to style page elements, it has always worked with strings—reading a width as "120px"
or the color as "rgb(240, 246, 252)"
. This works, and it's intuitive to set the
background-color to "yellow"
, but it makes it very awkward to work with any values that are read. As CSS syntax gets
more complex with features like calc()
and var()
, scripts would have to parse these values themselves into something
meaningful. The Typed OM API, part of Houdini, provides an alternative where style properties have structured values
that are easier to manipulate. A width would instead be a CSSUnitValue
whose value
is 120
and its unit
is "px"
.
A color provides its different color channels as numbers. Calculations form a tree of functions, etc.
This month we began the long process of implementing this API. So far, a few different types of value are readable, but we don't yet support setting values in this way, and many different JavaScript methods are unimplemented. But it's an encouraging start to a very exciting web feature.
:heading pseudo-class
We gained another new pseudo-class this month: :heading
, also in its :heading()
form. As :heading
, it applies to
all of the <h1>
... <h6>
elements, but the :heading()
form lets authors specify which heading levels to target,
for example :heading(1, 3, 5)
. (PR #5830 and PR #6009)
CSS clamping and interpolation improvements
The result of a CSS calculation, or an interpolated value (using animations, transitions, or any other method), is
required to be clamped and normalized to be valid where it appears. For example, font-size
can't be negative, so
font-size: calc(1000px * -1)
would result in 0
, not -1000px
. Infinite and NaN values should also get replaced by
finite numbers. We previously didn't apply these constraints, but this month we've started doing so more correctly.
WebGL support on Linux
Linux now has WebGL parity with macOS. The tricky part was cross-API GPU memory sharing between ANGLE and Skia. (PR #5864)
Buttons inside flex layout
We improved the layout logic for <button>
elements inside flex layouts, allowing them to be sized correctly when
needed. Additionally, we improved how coordinates were transformed from our layout tree to our painting tree, which is
used to render everything to screen. This results in subtly improved visual rendering of tight-fitting grid and flex
layouts. (PR #5894)
Nested inline margin boxes
Ladybird's layout logic needs to take all kinds of nested boxes and inline content into account when determining where
everything needs to go. We were still lacking support for certain nested inline content; for example, a nested <span>
could have padding, borders, or margins applied that should increase the size of its parent inline element. This was very
noticeable in the top menu of the 'monobook' Wikipedia theme, which now looks great! (PR #6000)
Grapheme clusters
A grapheme cluster is a sequence of one or more Unicode code points that are perceived as a single character by the user. For example, consider the "face with spiral eyes" emoji, 😵💫. This emoji consists of the code points U+1F635 (😵 dizzy face), U+200D (zero-width joiner), and U+1F4AB (💫 dizzy symbol). Special care must be taken by text processing systems to treat these code points as a single character during user interaction.
This month, we improved our handling of grapheme clusters for both text editing and cursor placement. For example, we now take care to delete an entire grapheme cluster when the user presses the backspace key, rather than deleting a single code unit (PR #5859). We also now place the cursor in the correct location when the user moves the cursor with the arrow keys (PR #5868) or clicks on / selects text (PR #5930).
Credits
We'd like to thank everyone who contributed code this month:
Abhinav, ahl-trifork, Ali Mohammad Pur, Aliaksandr Kalenik, Andreas Kling, ayeteadoe, Ben Eidson, Bernard Niset, Callum Law, CountBleck, Dan Vittegleo, devgianlu, Edwin Hoksberg, Emmanuel Ferdman, Erik Kurzinger, EvoPot, Glenn Skrzypczak, Idan Horowitz, InvalidUsernameException, Jamie Mansfield, Jelle Raaijmakers, joanvilarrasa, Kenneth Myhra, Lukas Schmidt, Luke Wilde, Michael Manganiello, mikiubo, Nico Weber, norbiros, quonverbat, rmg-x, Rocco Corsi, Sam Atkins, sayhan, stelar7, Tete17, Tim Ledbetter, Timothy Flynn, Tuur Martens, Vaxry, Viktor Szépe, zac