Pure CSS rounding for crisp chessboard images
Immediately after deploying a responsive web design for lichess.org, users were complaining about blurry chessboards. In a fluid grid layout, the board sizes were no longer guaranteed to be a multiple of 8px! Surprisingly, the problem is worst in Chrome, which sometimes(!?) even has trouble with vector graphics.
As of 2019, CSS calc()
does not support rounding. So what to do?
Note that using JavaScript would be a major performance regression. In the fixed size design the layout (including board and pieces) could be rendered before running any scripts, making for very snappy page loads.
A rendering quirk in Chrome
Because the issue is worst in Chrome, we can use another quirk of Chrome to work around it.
div.gray {
width: 399px;
}
div.blue {
width: 12.5%;
}
div.green {
width: 12.5%;
display: table;
}
12.5%
of the parent 399px
is evaluated to 49.875px
, and Chrome happily
uses subpixel rendering to show that (blue div).
However when using display:table
, the width of the element is truncated to full
pixels (green div).
Screenshot taken in Chrome 78, in case you're viewing in a different browser, or if rendering changes in the future:
The horrible hack
Now that we can round to full pixels, we can also round to arbitrary full pixel intervals.
Have one container of arbitrary size.
div.gray {
width: 399px;
}
Add a child with size 12.5%
. Use the display:table
quirk to round
the size to full pixels (green).
div.green {
width: 12.5%;
display: table;
}
Add another child with size 800%
, essentially computing
floor(width / 8) * 8
of the outer element size in pure CSS (yellow).
div.yellow {
width: 800%;
}
Screenshot taken in Chrome 78:
As always, the well-known padding-bottom:100%
trick can be used to make the
inner-most element a square. As of May 2019 the hack is live on Lichess and the required
wrapper elements are part of
chessground.
JPG samples
To demonstrate the issue, here is the same JPG board with original size 800px in sizes 400px (hopefully sharp) to 393px (not consistently sharp).
SVG samples
Even scaled vector graphics can sometimes(!?) look blurry, regardless of
shape-rendering="crispEdges"
.
Remaining issues
-
A hack is a hack. There is no guarantee that
display:table
will always behave this way. Hopefully it does not change before there is an alternative. Update: Indeed it stopped working in Chrome 91. -
For unknown reasons, there is a weird wobbly interaction with the board resize slider/handle.
-
So far no one has managed to perfectly position square highlights when browser zoom is involved (without adding DOM elements for every single square).
niklasf, 27th December 2019.