so for the past little while i’ve been making a big update to my personal website. it’s a cube now! i wanted to make sure it worked in several browsers:
- firefox latest (easy) and ESR (ok)
- chrome (annoying)
- safari (had to make a few sacrifices)
- qutebrowser (uhhh)
you know, the big four.
i got there in the end, with mostly satisfactory results.
first try
the first incarnation of the cube was very elegant. the sides were moved with 3d transforms, and were brought to the front by applying a transformation to the whole cube. it didn’t even need javascript!
if you have a document that looks like this…
<nav>
<!-- page switcher -->
<input type=radio name=page id=r-one>
<input type=radio name=page id=r-two>
</nav>
<main>
<!-- pages -->
<section id=one> 1111111111111111 </section>
<section id=two> 2222222222222222 </section>
</main>
…you can make a page switcher like this!
/* hide sections by default */
> section { display: none; }
main
/* show one if r-one is checked, and same for two/r-two */
:has(#r-one:checked) #one { display: block; }
body:has(#r-two:checked) #two { display: block; } body
the last two rules say that if the body
contains a thing
called r-one
(or r-two
) which is checked, then
a descendant called one
(two
) should be
displayed. add four more sections and some a lot of css, and
you got yourself a cube babey.
but it wasn’t to last, because firefox ESR and qutebrowser don’t
support :has()
.
in a few years you might be able to do this though.
second try
ok, fine, i’ll use javascript.
one thing about putting the pages on a cube is that the front is
overlapping with the back on the page. obviously. but this means that
sometimes scrolling or selecting text ends up on the back face you can’t
see. so all sides except the front have
pointer-events: none
applied, so you can’t accidentally
click links you can’t see, or whatever.
this worked fine in latest firefox (well, so did the non-javascript one). but in chrome, the faces that started on the left and right sides wouldn’t respond to the mouse when brought to the front. just those two. presumably this had been happening the whole time, and i just hadn’t noticed yet.
third try (how the sausage is made)
ok so. rotating the whole cube at once is a bust. fine. i’ll manipulate each side individually then.
now i’m keeping a state for each pane, of which face it is on, and how it is rotated. annoying, because composition of transformations isn’t commutative, so currently i just have some big lookup tables to do that (sorry i am bad at computer and also at algebra).
oh no now the sides are moving separately, like i told them to, and the transition looks all nutso! so what it actually does is:
- give the whole cube a
transition
style - rotate the whole cube
- remove the
transition
style - de-rotate the cube (which, after c, is non-animated)
- move the individual sides into place
urgh
in a way, this was a good thing. i can now animate the z-axis transform separately to the other two, which i think looks nicer, and i can give the cube an idle “breathing” animation so hopefully people stop asking if the sides are supposed to be touching.
both of those would have been harder with the whole-cube rotation. silver lining or whatever.
other stuff
it’s not done. there’s still more stuff i want to do. but it’s done enough to use.
there’s a flat mode when
prefers-reduced-motion
is active. i don’t want anyone barfing over my spinning cube.dark theme! all the art turns into neon signs (kinda). this was myno’s idea and i think it rules
in flat mode, the hidden panes need to have
display: none
applied, otherwise VoiceOver can still see them. apparently it doesn’t count as a pointer event.in safari and qutebrowser, the breathing animation is disabled. otherwise, in safari, the top and bottom faces disappear sometimes, and in qutebrowser everything just goes to hell
qutebrowser’s
AbortController
doesn’t seem to work so i had to do some minor contortions around thatqutebrowser’s media queries in
<link>
tags are slightly broken, but it’s not really a big deal