Thanks for posting this, it was fascinating! I had to implement a hex grid recently, so when I started reading through this page and seeing some of the same things I had thought about, I thought I wouldn't find much that was new. I couldn't have been more wrong!
Came here to post this, and saw that it's the very first comment... I am not disappointed, and instead very happy to be part of this knowledgeable community.
I see a lot of convoluted CSS things like this. Is there not much awareness of SVG in the webdev community, or is there some other reason people don't want to use it?
If you wanted arbitrary HTML content in the hexagons, would you be able to use SVG? I know you could hack together similar output since SVG supports images, links, text formatting, etc., but I can see people liking the idea of the hexagonal containers being plain divs that you can put whatever content you want into.
No. An iframe is a separate browsing context which as a developer you can’t interact with perfectly, and is segregated from the rest as a user, which has implications on keyboard interactivity especially. Essentially, <iframe> is an accessibility headache. <foreignObject> is just the opposite of <svg>, so it’s no trouble at all.
I'm familiar with those docs. That method was a poor experience for my mobile users. SVG support on mobile is not that great. Sadly FF had the most issues so I'm stuck with my workaround for now.
This example doesn't even show how it would place content inside the grid... and I suspect any attempt to do so with anything more than trivial text excerpts would result in the layout shattering, or the content clipping. And attempting to fit hexagonally-shaped content into the exact elements of the grid seems even less likely, here.
To explain why that is: In SVG everything has x and y coordinates relative to the SVG view box. There is no fundamental declarative layout like in HTML/CSS where the browser will calculate where elements go for you.
SVG is great as something to embed in HTML, but not the other way around.
I have worked with hexes in little side project games, it was a difficult process and I went through a phase of trying to home-roll canvas drawing of hexes (and making some interesting looking bugs). I ended up using a js library (Konva / https://konvajs.org/) which was pretty easy to implement.
One completed (albeit ugly) project was an implementation of a minigame I played in Magnum Opus:
It's been a little project kicking around that I want to make a hex game engine of sorts in Vue. The difficult part I keep running in to is connecting the state of the engine (in vue) with the state of the canvas (outside). I've ended up often hitting lower level APIs to watch/update/rerender parts, but at that point I feel like I end up reimplementing parts of vue within itself, and that nagging 'you are missing an obvious solution' worm starts up.
A pure CSS approach would make this a lot simpler, because there'd be no need to do any manual wiring up of state to canvas state, you'd just let vue handle that cycle because that's its core competency.
Oh excellent, thank you, I didn't know you had written about hexes + vue -- your site has been absolutely invaluable to me and is one of the primest examples of visualization done well.
One problem I remember running in to (drawing hexes) is that the redraw of one hex overwrote what had been drawn in another, causing corners of the bounding box of a hex to clear its neighbors. Did you run in to any issues like that, or was that likely just a quirk of how I coded it (I am amateur with canvas, so it's a likely cause).
There are some gotchas when using sprites/images for hexagons. 1. The bounding boxes overlap, so you'll want to make sure that the sprites have transparent corners so that they don't overwrite what's in the next hexagon. 2. The images won't be exactly regular (equal sided) hexagons because regular hexagons have an irrational side width or height. The image size will be rounded off to the nearest pixel. You'll need to adjust for this in your hexagon calculations, either shrinking or expanding one of the axes a tiny bit so that both axes are integer sized. For example if your hexagon spacing is 173.20508 x 150 then you'll change your spacing to be 173 x 150, or whatever matches your image size.
Sorry for the off-topic comment but I just want to thank you for your site (and in particular that article). I've been using your site for years and it's an invaluable resource to me.
I think not, because despite the high quality of the pure CSS result, there's probably a js lib out there that will get the same result for a few hundred extra kilobytes in the bundle. But knowing CSS is still very useful as a skill, it's usually your "moat" that makes it hard for backend devs to do your work while you're on vacation.
I cheated and used hex-shaped png files but looking back using CSS to just auto-shape the PNG would be good.
That said, if you ARE using textures in the hexes and you'd like to actually tile them, you'll have to explicitly design an image that will tile. So you might as well just create hex shaped images like this and forget getting too fancy.
So there's one trick that's kind of cool using PNG images (with transparency) for your tiles. You can have parts of the image that actually extend out of the tile. So a tree can extend off the top of the tile in a pseudo-isometric style. Or a castle, or a mountain, or anything.
PSA: don't open this on Safari on a Mac book Pro, it'll either take a while to load or it'll freeze your computer if you have other heavy processes running. Chrome works fine.
I ran into the problem too that CSS-counters are strings and not integers. Can anyone explain the reasoning? It's understandable that the displayed value is a string but the (sub)counters themselves could as well been done as integers imo.
Can anybody explain to me why I would use the CSS grid to accomplish this and not negative margins on inline divs? The latter is how I immediately guessed that this would be implemented. What would be the advantages of the grid method?