
As an experiment to see if Modern CSS is up to the task, I recreated the Google Antigravity website with Modern CSS.
~
Recently, Google Antigravity was released along with its website. I havenât used the application myself yet, but I did check out its website. Besides being visually appealing, one of the first things that caught my eye is that the scrolling felt âoffâ. Opening DevTools confirmed that my hunch was right: the site is using scroll-jacking, which makes the scrolling feel uncanny. Hmmm.
Digging further in, I also noticed that the site does not use any of the latest Modern CSS features, even though the site is packed with patterns that can be done with CSS nowadays. Now, I do get why that team might have chosen not to lean on those recent CSS capabilities: the website is a marketing tool, so they want need it to look and behave â including all the visual flair â exactly the same in every browser. I get it. But it does feel like a missed opportunity from my âChrome DevRel for CSS/UIâ point-of-view.
~
So as one does, I nerd sniped myself into recreating the website using Modern CSS Features. The goal of my experiment wasnât to make an exact copy of the Antigravity website, but more to see if Modern CSS would hold up throughout building it.
A few nights of burning the midnight oil, and this is what I landed on:
To get to that result, I leaned on these Modern CSS features:
- CSS
@starting-style - A Houdini PaintWorklet for the ring particles.
- CSS Container Queries
- CSS Scroll Snapping
- CSS
@scope - CSS Anchor Positioning
- CSS Carousels
- CSS
overscroll-behavior: contain;on a non-scrollable scroll container - CSS
scroll-state(scrolled: âŚ)scroll queries to create a hidey bar - CSS Scroll-Triggered Animations (which is admittedly a bit buggy in Chrome Canary right now)
- CSS
sibling-index()(would have loved to userandom()but thatâs not available in Chrome) - CSS Scroll-Driven Animations
(Not included in this list are things I consider basic nowadays: Responsive Design, Cascade Layers, CSS Nesting, Custom Properties, âŚ)
The only bit of JavaScript used is the registration of the PaintWorklet, and a tad of JS to sync the cursorâs position to two custom properties for the CSS to use.
~
The recreation I built is embedded below, but you should check it out on CodePen as the website is responsive. You must use Chrome Canary to see the latest niceties in action.
If you are not using Google Chrome Canary (or not using Chrome at all) donât worry: the site was built with Progressive Enhancement in mind and everything â except @scope đ â is feature-detected with @supports. So even if your browser understands only a fragment of the Modern CSS that is used, everything can be viewed perfectly fine đ
The rest of the CSS is admittedly a bit messy, as I quickly threw this together during some late night coding sessions. Like, the grid approach could be done much better. I also didnât get to code up all parts of the site (such as the mega menu or the footer) as this is merely an after-hours POC.
TIP: Go disable the @layer named moderncss in the Style Sheet to force the site into its âbasicâ view. The easiest way to do so, is to change @layer moderncss to @slayer moderncss and youâre done.
~
All in all, I would say Modern CSS held up very nicely here. There are still a few rough edges I encountered:
- Using
@scopefrom a progressive angle is difficult, because you canât feature detect it. We really needat-rule()to become a thing. - When determining the gap for a flex or grid layout, I want to have different gaps for the rows and columns.
gap: inline-gap block-gapwould be handy. - Getting the pointerâs x and y position on an element should not require JavaScript â I filed w3c/csswg-drafts#6733 for that a long time ago
- When using scroll-snapping to, for example, snap to the center of a horizontal scroller, the first item canât really snap to that edge because it is the first item in that scroller so itâs stuck to the left edge. You can fix this by adding some virtual padding to the scroller, but that involves manually calculating the size of it.
- It feels like the CSS snapped state query is triggering while it is still scrolling â so a bit too early. I needed to delay the entry transitions a bit (magic number!) to sync things up.
- In the mobile view, I am using the checkbox hack to show the menu. Ideally this would need to be a button, but then I think I would need JS to update the
aria-pressedof it. AFAIK there is no âtoggleable buttonâ I can use for this. - In CSS Carousels, there is no wrapper around the two
::scroll-button()s, so I had to diverge from the original design. - Grid blowouts still catch me off guard and subgrid sometimes leaves me headscratching, but thatâs most likely just PEBKAC.
- There is no way to disable an entire cascade layer. You have to slayer it.
- I could use something like an
animation-speedto control how fast the strip of icons should slide in (also see w3c/csswg-drafts#5091 (comment)). Now I have to adjust the time in a media query, whereas â100px per 0.1sâ would automatically adjust things.
All the rest went pretty smooth đ
If you havenât done so already, go check out âGoogle Antigravity with Modern CSSâ on CodePen.
~
đĽ Like what you see? Want to stay in the loop? Here's how:
I can also be found on đ Twitter and đ Mastodon but only post there sporadically.