> All in One 586

Ads

Thursday, April 23, 2026

Mostly Clear today!



With a high of F and a low of 31F. Currently, it's 44F and Clear outside.

Current wind speeds: 7 from the Northeast

Pollen: 0

Sunrise: April 23, 2026 at 06:03PM

Sunset: April 24, 2026 at 07:37AM

UV index: 0

Humidity: 33%

via https://ift.tt/nu91sFt

April 24, 2026 at 10:02AM

Recreating Apple’s Vision Pro Animation in CSS

Apple’s product animations, particularly the scrolly teardowns (technical term), have always been inspiring. But these bleeding-edge animations have always used JavaScript and other technologies. Plus, they aren’t always responsive (or, at least, Apple switches to a static image at a certain width).

I’ve been wowed by CSS’s more recent scrolling animation capabilities and wondered if I could rebuild one of these animations in just CSS and make it responsive. (In fact, CSS sure has come a long way since the last attempt in this publication.) The one I’ll be attempting is from the Vision Pro site and to see it you’ll need to scroll down until you hit a black background, a little more than halfway down the page. If you’re too lazy errr… efficient to go look yourself, and/or they decide to change the animation after this article goes live, you can watch this video:

Note: While Apple’s version works in all major browsers, the CSS-only version, at the time of this writing, will not work in Firefox.

Apple’s Animation

The first thing we have to do is identify what’s going on in the original animation. There are two major stages.

Stage 1: “Exploding” Hardware

Three electronic components rise in sequence from the Vision Pro device at the bottom of the page. Each of the three components is a set of two images that go both in front of and behind other components like a sub roll around a hot dog bun around a bread stick. (Yes, that’s a weird analogy, but you get it, don’t you?)

The first, outermost component (the sub roll) comprises the frontmost and the hindmost images allowing it to appear as if it’s both in front of and behind the other components.

The next component (the hot dog bun) wraps the third component (the bread stick) similarly. This provides depth, visual interest, and a 3D effect, as transparent areas in each image allow the images behind it to show through.

Stage 2: Flip-Up to Eyepieces

The final piece of the Vision Pro animation flips the device up in a smooth motion to show the eyepieces. Apple does this portion with a video, using JavaScript to advance the video as the user scrolls.

Let’s recreate these, one stage at a time.

“Exploding” Hardware

Since Apple already created the six images for the components, we can borrow them. Initially, I started with a stack of img tags in a div and used position: fixed to keep the images at the bottom of the page and position: absolute to have them overlap each other. However, when I did this, I ran into two issues: (1) It wasn’t responsive — shrinking the width of the viewport made the images go off screen, and (2) the Vision Pro couldn’t scroll into view or scroll out of view as it does on the Apple site.

After banging my head against this for a bit, I went back and looked at how Apple constructed it. They had made each image a background image that was at background-position: bottom center, and used background-size: cover to keep it a consistent aspect ratio. I still needed them to be able to overlap though, but I didn’t want to pull them out of flow the way position: absolute does so I set display: grid on their parent element and assigned them all to the same grid area.

.visionpro { /* the overarching div that holds all the images */
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
}
.part { /* each of the images has a part class */
  grid-area: 1 / 1 / 2 / 2;
}

As my logic professor used to say in the early aughts, “Now we’re cooking with gas!” (I don’t really know how that applies here, but it seemed appropriate. Somewhat illogical, I know.)

I then began animating the components. I started with a scroll timeline that would have allowed me to pin the animation timeline to scrolling the entire html element, but realized that if the Vision Pro (meaning the elements holding all of the images) was going to scroll both into and out of the viewport, then I should switch to a view timeline so that scrolling the element into view would start the animation rather than trying to estimate a keyframe percentage to start on where the elements would be in view (a rather brittle and non-responsive way to handle it).

Scrolling the Vision Pro into view, pausing while it’s animating, and then scrolling it out of view is a textbook use of position: sticky. So I created a container div that fully encapsulated the Vision Pro div and set it to position: relative. I pushed the container div down past the viewport with a top margin, and set top on the vision pro div to 0. You could then scroll up till the position: sticky held the vision pro in place, the animation executed and then, when the container had been entirely scrolled through, it would carry the Vision Pro div up and out of the viewport.

Now, to tackle the component moves. When I first used a translate to move the images up, I had hoped to use the natural order of the elements to keep everything nicely stacked in my bread-based turducken. Alas, the browser’s sneaky optimization engine placed my sub roll entirely on top of my hot dog bun, which was entirely on top of my breadstick. Luckily, using z-index allowed me to separate the layers and get the overlap that is part of why Apple’s version looks so awesome.

Another problem I ran into was that, at sizes smaller than the 960-pixel width of the images, I couldn’t reliably and responsively move the components up. They needed to be far enough away that they didn’t interfere with Stage 2, but not so far away that they went fully out of the viewport. (Where’s a bear family and a blonde girl when you need them?) Thankfully, as it so often does, algebra saved my tuchus. Since I have the dimensions of the full-size image (960px by 608px), and the full width of the image is equal to the width of the viewport, I could write an equation like below to get the height and use that in my translation calculations for how far to move each component.

--stage2-height: calc(min(100vw, 960px) * 608 / 960);

However, this calculation breaks down when the viewport is shorter than 608px and wider than 960px because the width of the image is no longer equal to 100vw. I initially wrote a similar equation to calculate the width:

--stage2-width: calc(min(100vh, 608px) * 960 / 608);

But it also only works if the height is 608px or less, and they both won’t work while the other one applies. This would be a simple fix using an “if” statement. While CSS does have an if() function as I’m writing this, it doesn’t work in Safari. While I know this whole thing won’t work in Firefox, I didn’t want to knock out a whole other browser if I could help it. So, I fixed it with a media query:

:root {
  --stage2-height: calc(min(100vw, 960px) * 608 / 960);
  --stage2-width: calc(min(100vh, 608px) * 960 / 608);
}

@media screen and (max-height: 608px) {
  :root {
    --stage2-height: calc(var(--vid-width) * 608 / 960);
  }
}

I patted myself on the back for my mathematical genius and problem-solving skills until I realized (as you smarty pants people have probably already figured out) that if the height is less than 608px, then it’s equal to 100vh. (Yes, vh is a complicated unit, particularly on iOS, but for this proof of concept I’m ignoring its downsides).

So, really all I needed was:

:root {
  --stage2-height: calc(min(100vw, 960px) * 608 / 960);
}

@media screen and (max-height: 608px) {
  :root {
    --stage2-height: 100vh;
  }
}

But whatever my mathematical tangents (Ha! Terrible math pun!), this allowed me to base my vertical translations on the height of the Stage 2 graphics, e.g.:

translate: 0 calc(var(--stage2-height) * -1 - 25vh);

…and thus get them out of the way for the Stage 2 animation. That said, it wasn’t perfect, and at viewports narrower than 410px, I still had to make an adjustment to the heights using a media query.

Flip-Up to Eyepieces

Unfortunately, there’s no way to either start a video with just CSS or modify the frame rate with just CSS. However, we can create a set of keyframes that changes the background image over time, such as:

/* ... */

50% {
  background-image: url(imgs/video/00037.jpg);
  z-index: -1;
}

51% {
  background-image: url(imgs/video/00039.jpg);
  z-index: -1;
}

52% {
  background-image: url(imgs/video/00041.jpg);
  z-index: -1;
}

/* ... */

(Since there’s, like, 60-some images involved in this one, I’m not giving you the full set of keyframes, but you can go look at the cssvideo keyframes in the complete CodePen for the full Monty.)

The downside of this, however, is that instead of one video file, we’re downloading 60+ files for the same effect. You’ll notice that the file numbers skip a number between each iteration. This was me halving the number of frames so that we didn’t have 120+ images to download. (You might be able to speed things up with a sprite, but since this is more proof-of-concept than a production-ready solution, I didn’t have the patience to stitch 60+ images together).

The animation was a bit choppy on the initial scroll, even when running the demo locally.

So I added:

<link rel="preload" as="image" href="imgs/video/00011.jpg">

…for every image, including the component images. That helped a lot because the server didn’t have to parse the CSS before downloading all the images.

Using the same view timeline as we do for Stage 1, we run an animation moving it into place and the cssvideo animation and the eyepieces appear to “flip up.”

animation: vpsf-move forwards, cssvideo forwards;
animation-timeline: --apple-vp, --apple-vp;

Fine Tuning

While a view timeline was great, the animation didn’t always begin or end exactly when I wanted it to. Enter animation-range. While there’s a lot of options what I used on all of the .parts was

animation-range: contain cover;

This made sure that the Vision Pro element was inside the viewport before it started (contain) and that it didn’t fully finish the animation until it was out of view (cover). This worked well for the parts because I wanted them fully in view before the components started rising and since their endpoint isn’t important they can keep moving until they’re off screen.

However, for Stage 2, I wanted to be sure the flip up animation had ended before it went off screen so for this one I used:

animation-range: cover 10% contain;

Both cover and 10% refer to the start of the animation, using the cover keyword, but pushing its start 10% later. The contain ensures that the animation ends before it starts going off screen.

Here’s everything together:

And here’s a video in case your browser doesn’t support it yet:

Conclusion

CSS sure has come a long way and while I definitely used some cutting edge features there were also a lot of relatively recent additions that made this possible too.

With scroll timelines, we can attach an animation to the scroll either of an entire element or just when an element is in view. The animation-range property let us fine-tune when the animation happened. position: sticky lets us easily hold something on screen while we animate it even as its scrolling. Grid layout allowed overlap elements without pulling them out of flow. Even calc(), viewport units, custom properties, and media queries all had their roles in making this possible. And that doesn’t even count the HTML innovations like preload. Incredible!

Maybe we should add a W to WWW: The World Wide Wondrous Web. Okay, okay you can stop groaning, but I’m not wrong…


Recreating Apple’s Vision Pro Animation in CSS originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.



from CSS-Tricks https://ift.tt/u6Xh9YL
via IFTTT

Wednesday, April 22, 2026

Partly Cloudy/Wind today!



With a high of F and a low of 41F. Currently, it's 66F and Fair outside.

Current wind speeds: 14 from the Southwest

Pollen: 0

Sunrise: April 22, 2026 at 06:04PM

Sunset: April 23, 2026 at 07:36AM

UV index: 0

Humidity: 10%

via https://ift.tt/SXsmr89

April 23, 2026 at 10:02AM

Enhancing Astro With a Markdown Component

There are two ways to enhance Markdown in an Astro project:

  1. Through MDX
  2. Through a Markdown Component

This article focuses on the Markdown Component.

Why Use a Markdown Component

I use a Markdown Component for two main reasons:

  1. It reduces the amount of markup I need to write.
  2. It converts typographic symbols like ' to opening or closing quotes (' or ').

So, I can skip several HTML tags — like <p>, <strong>, <em>, <ul>, <ol>, <li>, and <a>. I can also skip heading tags if I don’t need to add classes to them.

<div class="card">
  <!-- prettier-ignore -->
  <Markdown>
    ## Card Title
    This is a paragraph with **strong** and *italic* text.
    This is the second paragraph with a [link](https://link-somewhere.com)

    - List
    - Of
    - Items
  </Markdown>
</div>

Notice the prettier-ignore comment? It tells prettier not to format the contents within the <Markdown> block so Prettier won’t mess up my Markdown content.

The HTML output will be as follows:

<div class="card">
  <h2> Card Title </h2>
  <p>This is a paragraph with <strong>strong</strong> and <em>italic</em> text.</p>
  <p>This is the second paragraph with a <a href="https://link-somewhere.com">link</a></p>

  <ul>
    <li> List </li>
    <li> Of </li>
    <li> Items </li>
  </ul>
</div>

Installing the Markdown Component

Fun Fact: Astro came with a <Markdown> component in its early release, but this <Markdown> component was migrated to a separate plugin in Version 1, and completely removed in version 3.

I was upset about it. But I decided to build a Markdown component for myself since I liked using one. You can the documentation here.

Using the Markdown component is simple: Just import and use it in the way I showed you above.

---
import { Markdown } from '@splendidlabz/astro'
---

<Markdown>
  ...
</Markdown>

Respects Indentation Automatically

You can write your Markdown naturally, as if you’re writing content normally. This Markdown component detects the indentation and outputs the correct values (without wrapping them in <pre> and <code> tags).

<div>
  <div>
    <!-- prettier-ignore -->
    <Markdown>
      This is a paragraph

      This is a second paragraph
    </Markdown>
  </div>
</div>

Here’s the output:

<div>
  <div>
    <p>This is a paragraph</p>
    <p>This is a second paragraph</p>
  </div>
</div>

Inline Option

There’s an inline option that tells the <Markdown> component not to generate paragraph tags.

<h2 class="max-w-[12em]">
  <Markdown inline> Ain't this cool? </Markdown>
</h2>

Here’s the output:

<h2 class="max-w-[12em]">
  Ain't this cool?
</h2>

Gotchas and Caveats

Prettier messes up the <!-- prettier-ignore --> block if you have unicode characters like emojis and em dashes anywhere before the block.

Here’s the original code:

<!-- prettier-ignore -->
<Markdown>
  Markdown block that contains Unicode characters 🤗
</Markdown>

<!-- prettier-ignore -->
<Markdown>
  Second Markdown block.
</Markdown>

Here’s what it looks like after saving:

<!-- prettier-ignore -->
<Markdown>
  Markdown block that contains unicode characters 🤗
</Markdown>

<!-- prettier-ignore -->
<Markdown>
  Second Markdown block.
</Markdown>

Unfortunately, we can’t do much about emojis because the issue stems from Prettier’s formatter.

But, we can use en and em dashes by writing -- and ---, respectively.

Content Workaround

You can prevent Prettier from breaking all those <!-- prettier-ignore --> comments by not using them!

To do this, you just put your content inside a content property. No need to worry about whitespace as well — that’s taken care of for you.

<Markdown content=`
  This is a paragraph

  This is another paragraph
`/>

Personally, I think it doesn’t look at nice as slot version above…

But it lets you use markdown directly with any JS or json content you load!

---
const content = `
  This is a paragraph

  This is another paragraph
`
---

<Markdown {content} />

Taking it Further

I’ve been building with Astro for 3+ years, and I kept running into the same friction points on content-heavy sites: blog pages, tag pages, pagination, and folder structures that get messy over time.

So I built Practical Astro: Content Systems — 7 ready-to-use solutions for Astro content workflows (MDX is just one of them). You get both the code and the thinking behind it.

If you want a cleaner, calmer content workflow, check it out.

I also write about Astro Patterns and Using Tailwind + CSS together on my blog. Come by and say hi!


Enhancing Astro With a Markdown Component originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.



from CSS-Tricks https://ift.tt/IqQ17ZV
via IFTTT

Tuesday, April 21, 2026

Clear today!



With a high of F and a low of 50F. Currently, it's 65F and Clear outside.

Current wind speeds: 16 from the South

Pollen: 0

Sunrise: April 21, 2026 at 06:05PM

Sunset: April 22, 2026 at 07:35AM

UV index: 0

Humidity: 23%

via https://ift.tt/gh812O3

April 22, 2026 at 10:02AM

Monday, April 20, 2026

Mostly Clear today!



With a high of F and a low of 41F. Currently, it's 57F and Clear outside.

Current wind speeds: 4 from the Southeast

Pollen: 0

Sunrise: April 20, 2026 at 06:07PM

Sunset: April 21, 2026 at 07:34AM

UV index: 0

Humidity: 15%

via https://ift.tt/8nJpqYP

April 21, 2026 at 10:02AM

Markdown + Astro = ❤️

Markdown is a great invention that lets us write less markup. It also handles typographical matters like converting straight apostrophes (') to opening or closing quotes (' or ') for us.

Although Astro has built-in support for Markdown via .md files, I’d argue that your Markdown experience can be enhanced in two ways:

  1. MDX
  2. Markdown Component

I’ve cover these in depth in Practical Astro: Content Systems.

We’re going to focus on MDX today.

MDX

MDX is a superset of Markdown. It lets you use components in Markdown and simple JSX in addition to all other Markdown features.

For Astro, you can also use components from any frontend framework that you have installed. So you can do something like:

---
# Frontmatter...
---

import AstroComp from '@/components/AstroComp.astro'
import SvelteComp from '@/components/AstroComp.astro'

<AstroComp> ... </AstroComp>
<SvelteComp> ... </SvelteComp>

It can be a great substitute for content-heavy stuff because it lets you write markup like the following.

<div class="card">
  ### Card Title

  Content goes here

  - List
  - Of
  - Items

  Second paragraph
</div>

Astro will convert the MDX into the following HTML:

<div class="card">
  <h2>Card Title</h2>

  <p>Content goes here </p>

  <ul>
    <li> List </li>
    <li> Of </li>
    <li> Items </li>
  </ul>

  <p>Second paragraph</p>
</div>

Notice what I did above:

  • I used ## instead of a full h2 tag.
  • I used - instead of <ul> and <li> to denote lists.
  • I didn’t need any paragraph tags.

Writing the whole thing in HTML directly would have been somewhat of a pain.

Installing MDX

Astro folks have built an integration for MDX so it’s easy-peasy to add it to your project. Just follow these instructions.

Three Main Ways to Use MDX

These methods also work with standard Markdown files.

  1. Import it directly into an Astro file
  2. Through content collections
  3. Through a layout

Import it Directly

The first way is simply to import your MDX file and use it directly as a component.

---
import MDXComp from '../components/MDXComp.mdx'
---

<MDXComp />

Because of this, MDX can kinda function like a partial.

Through Content Collections

First, you feed your MDX into a content collection. Note that you have to add the mdx pattern to your glob here.

Import it directly

The first way is simply to import your MDX file and use it directly as a component.

// src/content.config.js
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';

const blog = defineCollection({
  loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/blog" }),
});

export const collections = { blog };

Then you retrieve the MDX file from the content collection.

---
import { getEntry, render } from 'astro:content'
const { slug } = Astro.props
const post = await getEntry('blog', slug)
const { Content } = await render(post)
---

<Content />

As you’re doing this, you can pass components into the MDX files so you don’t have to import them individually in every file.

For example, here’s how I would pass the Image component from Splendid Labz into each of my MDX files.

---
import { Image } from '@splendidlabz/astro'
// ...
const { Content } = await render(post)
const components = { Image }
---

<Content {components} />

In my MDX files, I can now use Image without importing it.

<Image src="..." alt="..." />

Use a Layout

Finally, you can add a layout frontmatter in the MDX file.

---
title: Blog Post Title
layout: @/layouts/MDX.astro
---

This layout frontmatter should point to an Astro file.

In that file:

  • You can extract frontmatter properties from Astro.props.content.
  • The MDX content can be rendered with <slot>.
---
import Base from './Base.astro'
const props = Astro.props.content
const { title } = props
---

<Base>
  <h1>{title}</h1>
  <slot />
</Base>

Caveats

Formatting and Linting Fails

ESLint and Prettier don’t format MDX files well, so you’ll end up manually indenting most of your markup.

This is fine for small amounts of markup. But if you have lots of them… then the Markdown Component will be a much better choice.

More on that in another upcoming post.

RSS Issues

The Astro RSS integration doesn’t support MDX files out of the box.

Thankfully, this can be handled easily with Astro containers. I’ll show you how to do this in Practical Astro.

Taking it Further

I’ve been building with Astro for 3+ years, and I kept running into the same friction points on content-heavy sites: blog pages, tag pages, pagination, and folder structures that get messy over time.

So I built Practical Astro: Content Systems, 7 ready-to-use solutions for Astro content workflows (MDX is just one of them). You get both the code and the thinking behind it.

If you want a cleaner, calmer content workflow, check it out.

I also write about Astro Patterns and Using Tailwind + CSS together on my blog. Come by and say hi!


Markdown + Astro = ❤️ originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.



from CSS-Tricks https://ift.tt/WOTlj0d
via IFTTT

Mostly Clear today!

With a high of F and a low of 31F. Currently, it's 44F and Clear outside. Current wind speeds: 7 from the Northeast Pollen: 0 Su...