> All in One 586

Ads

Friday, May 8, 2026

Mostly Clear today!



With a high of F and a low of 47F. Currently, it's 53F and Clear outside.

Current wind speeds: 4 from the Southeast

Pollen: 0

Sunrise: May 8, 2026 at 05:45PM

Sunset: May 9, 2026 at 07:51AM

UV index: 0

Humidity: 51%

via https://ift.tt/3OCtbMq

May 9, 2026 at 10:02AM

Using CSS corner-shape For Folded Corners

I came across Kitty Giraudel’s folded corners technique. It leverages CSS clip-path, and I thought that that was such a cool way to do it. clip-path has been trending lately, most likely because web browsers support the shape() function now.

However, I’ve been on a bit of a corner-shape kick lately (have a look at my introduction to corner-shape as well as these scroll-driven corner-shape animations), so I figured that corner-shape could be used to create folded corners as well, and this is what I came up with:

White paper with the top-right corner folded in.

So open Chrome, which supports corner-shape, and let’s dig in (if you’re looking at this in other browsers, it basically falls back to a rounded corner).

Step 1: Set some CSS variables

Elements have four corners, but when we use border-radius, each corner is split into two coordinates. The x-axis coordinate moves along the x-axis, away from its associated corner, while the y-axis coordinate does the same thing along the y-axis. It’s from these coordinates that border-radius draws the curvature of the rounded corners.

Diagramming the shape showing border-radius applied to the bottom-left corner. The rounded corner is 50% on the y-axis and 50% on the x-axis.

First, store the coordinates as CSS variables. We’ll need the values that they hold more than once, so this simplifies things, makes the fold animatable, and maintains some degree of realism.

:root {
  /* x-axis coordinate */
  --x-coord: 9rem;

  /* y-axis coordinate */
  --y-coord: 5rem;
}

Step 2: Establishing the fold

Given what we now know about border-radius, it should be obvious what border-top-right-radius does. As for corner-top-right-shape: bevel, that ensures that a straight line is drawn between the coordinates instead of rounded corners (corner-top-right-shape: round). That’s right, border-radius includes corner-shape: round by default (behind the scenes, of course).

/* Square */
div {
  /* Place coordinates */
  border-top-right-radius: var(--x-coord) var(--y-coord);

  /* Draw line between coordinates */
  corner-top-right-shape: bevel;
}
White paper with a diagonal cut in the top-right corner.

Step 3: Creating the flip side

Now that we’ve established the fold, it’s time to create the flip side. Start by selecting ::before, then declare content: "" to create the element without content. The background can be inherited from the square, and the dimensions should leverage the coordinates that we saved. As you can see, I’ve also added a box-shadow where the blur radius scales with --x-coord and --y-coord, but you’re welcome to adapt the formula as you see fit.

/* Square */
div {
  /* Place coordinates */
  border-top-right-radius: var(--x-coord) var(--y-coord);

  /* Draw line between coordinates */
  corner-top-right-shape: bevel;

  /* Flip side */
  &::before {
    /* Generate empty element */
    content: "";

    /* Inherit background */
    background: inherit;

    /* Same as coordinates */
    width: var(--x-coord);
    height: var(--y-coord);

    /* Scale blur radius with --x-coord and --y-coord */
    box-shadow: 0 0 calc((var(--x-coord) + var(--y-coord)) / 3) #00000050;
  }
}
White paper with s white rectangle in the top-left corner and a diagonal cut in the top-right corner.

Step 4: Positioning the flip side (::before)

Next, we need to shift ::before to the (top-)right corner. We’re avoiding anchor positioning, because there’s no need for modern features if more supported features work well using the same amount of code. So, declare position: relative on the square and position: absolute on ::before. This makes ::before position relative to the square, and is a trick that only works for parent-child relationships. Actually, this shortcoming is why anchor positioning was invented, but we just don’t need it in this case.

In addition, declare inset: 0 0 auto auto on ::before to align it to the top-right corner of the square, and overflow: clip on the square to clip the half of ::before that overflows it.

/* Square */
div {
  /* Place coordinates */
  border-top-right-radius: var(--x-coord) var(--y-coord);

  /* Draw line between coordinates */
  corner-top-right-shape: bevel;

  /* Clip any overflow */
  overflow: clip;

  /* For alignment */
  position: relative;

  /* Flip side */
  &::before {
    /* Generate empty element */
    content: "";

    /* Inherit background */
    background: inherit;

    /* Same as coordinates */
    width: var(--x-coord);
    height: var(--y-coord);

    /* Scale blur radius with --x-coord and --y-coord */
    box-shadow: 0 0 calc((var(--x-coord) + var(--y-coord)) / 3) #00000050;

    /* For alignment */
    position: absolute;

    /* Align to top-right */
    inset: 0 0 auto auto;
  }
}
White paper with the top-right corner folded in.

You can stop here if you want, but there’s room for improvement…

Step 5: Sculpting the flip side

To make the outcome look a bit more realistic, we’ll use corner-bottom-left-shape: bevel to make one more straight cut, this time to ::before. There are, most likely, many ways to tackle this depending on how sharply we want to crease the fold, how elevated we want the flip side to be, and the angle from which we want to view the square, but I don’t think it matters as long as the effect looks decent, so we’re aiming for a sharp crease, the flip side sticking up, and an aerial view. If you’d rather something different, keep in mind that the shadow also impacts the outcome, and that you’d be facing a trickier implementation.

The only degree of complexity that I suggest is this:

/* Ensure realistic fold */
@container style(--x-coord < --y-coord) {
  border-bottom-left-radius: 100% calc(100% - var(--x-coord));
}

@container style(--x-coord >= --y-coord) {
  border-bottom-left-radius: calc(100% - var(--y-coord)) 100%;
}

These are container style queries using the range syntax, where if the value of --x-coord is less than the value of --y-coord, we subtract the value of --x-coord from 100% and use it as the y-axis coordinate for the relevant border radius (border-bottom-left-radius, in this case). The other axis is set to 100%. Adversely, if the value of --x-coord is more than (or equal to) the value of --y-coord, we subtract the value of --y-coord from 100% and use it as the x-axis coordinate. Once again, the other axis is set to 100%.

The result is that the crease, shadow, and now perspective of the fold is calculated using only --x-coord and --y-coord to look realistic (or realistic enough, anyway). Using the slideVars toggles in the top-right corner of the demo, you can see for yourself by testing various combinations of coordinates:

If you want to implement a failsafe to ensure that the coordinates don’t exceed the dimensions of the square, breaking the effect, you can use min(). The modified coordinate variables below set --y-coord to an impossible 999999999rem, but caps it at the height of the square (although I can’t imagine that you’d actually need this, to be completely honest):

--x-coord: min(--square-width, 9rem);
--y-coord: min(--square-height, 999999999rem);
White paper with the top-right corner folded in.

All in all, we have not only a folded corner effect but a utility that builds the effect based on only two coordinates.

The full code:

:root {
  /* x-axis coordinate */
  --x-coord: 9rem;

  /* y-axis coordinate */
  --y-coord: 5rem;

  /* Square */
  div {
    /* Place coordinates */
    border-top-right-radius: var(--x-coord) var(--y-coord);

    /* Draw line between coordinates */
    corner-top-right-shape: bevel;

    /* Clip any overflow */
    overflow: clip;

    /* For alignment */
    position: relative;

    /* Flip side */
    &::before {
      /* Generate empty element */
      content: "";

      /* Inherit background */
      background: inherit;

      /* Same as coordinates */
      width: var(--x-coord);
      height: var(--y-coord);

      /* Scale blur radius with --x-coord and --y-coord */
      box-shadow: 0 0 calc((var(--x-coord) + var(--y-coord)) / 3) #00000050;

      /* For alignment */
      position: absolute;

      /* Align to top-right */
      inset: 0 0 auto auto;

      /* Draw line between coordinates */
      corner-bottom-left-shape: bevel;

      /* Ensure realistic fold */
      @container style(--x-coord < --y-coord) {
        border-bottom-left-radius: 100% calc(100% - var(--x-coord));
      }

      @container style(--x-coord >= --y-coord) {
        border-bottom-left-radius: calc(100% - var(--y-coord)) 100%;
      }
    }
  }
}

Note: We could swap container style queries for if() functions, which are shorter but less readable.

Folded corners using clip-path vs. corner-shape

Kitty’s Giraudel’s folded corners work in all browsers, and because clip-path is used, which is a more versatile shaping feature, there are more ways to customize the shape. It’s also the more correct approach, for whatever that’s worth. However, my corner-shape approach is cleaner and likely wouldn’t require any further customization anyway, but lacks Safari and Firefox support for now. So unless you need folded corners today, I’d bookmark both:


Using CSS corner-shape For Folded Corners originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.



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

Thursday, May 7, 2026

Partly Cloudy today!



With a high of 71F and a low of 42F. Currently, it's 55F and Partly Cloudy outside.

Current wind speeds: 7 from the Northeast

Pollen: 0

Sunrise: May 7, 2026 at 05:46PM

Sunset: May 8, 2026 at 07:51AM

UV index: 0

Humidity: 48%

via https://ift.tt/umChAle

May 8, 2026 at 10:02AM

A Scrollytelling Gift for Mum on Mother’s Day 2026

My mum loved logic because she was born at a time when nothing made sense. She was born in 1945, the year World War II ended, so she dodged a literal bullet because we are Jewish. But from the first day of her life, she found that famine, racism, and misfortune kept trying to take her away. In 2011, cancer took her away from me forever. But on a lighter note, this Mother’s Day I’m bringing her back to life the only way I know how: UI mad science!

I will explain how my mum inspired this 2026 Mother’s Day scrollytelling experiment — but also, how she inspired my approach to dev and life. Along the way, I’ll discuss some of the tech involved in this virtual Mother’s Day gift. I normally write either inspirational or technical posts — but for Mother’s Day, you’re getting a twofer.

Try the interactive Mother’s Day card

Here’s the CodePen, which uses scroll-snap events and scroll-state queries, so it will only work in Chromium-based browsers at the moment.

Alternatively, here’s a video demo with commentary by my eight-year-old. It was bittersweet to realise that this is the closest he has come to interacting with his nana, because she passed before he was born.

Why I made this

Mum was born in a hospital in Kazakhstan, where civilian patients shared wards with discharged soldiers suffering PTSD. They wandered in and out of the maternity rooms, terrifying the patients and making labour even harder for my grandmother.

When Mum was born, she wasn’t breathing. The staff immersed her in cold water, then hot, then cold water again — a so-called remedy at the time based on no science. This was the beginning of a larger pattern in her life: She kept surviving not because of the help she received, but despite chaos disguised as help.

So, as an adult, Mum learned to survive by finding patterns and sense in the unfathomable. She accomplished this by combining her three passions:

  • In photography, she framed moments when the chaos of her surroundings temporarily harmonized into beauty.
  • In teaching, she used those images to help tell a story that broke the chaos into logical steps people could follow.
  • In computer programming, she encapsulated those illustrated teachable moments within interactive experiences. Unlike in real life, if a programmed interaction goes wrong, you can trace why and solve the problem.

In other words, she educated me by using the skill set I now think of as web development—before the web existed.

Preview of the interactive card. A woman with short brown hair at the right of the frame looking left into the lens of a large camera and standing in the middle of a lush flower garden. In loving memory of Anna Meyer, 1945-2011.

Gamifying the experience of knowing my mum

I drew inspiration from Roland Franke’s deconstructed radial slice transition using scroll-snap events. Roland’s Pen showcases eye-catching, scroll-triggered transitions between landscapes as a figure sits in the foreground watching. This made me think of the patience my mum put into observing the world—but then she’d encapsulate everything in short, interactive stories I could digest as a young child.

I’m symbolizing that experience in my Mother’s Day game with the scroll-triggered time-lapse animation of day to night, stylized with CSS shapes. Using a single scroll gesture, we grasp the gist of an entire day. That experience is like the way my mum could explain a big topic to me in a way that felt like play.

My mum taught me that video games don’t have to be about blowing things up. She once used QuickBASIC to build a photography game long before Pokémon Snap existed. I remember passing a shop in the 90s with Armor Alley playing in demo mode in the window. I was obviously fascinated, but my mum said, “I don’t like it. The helicopter started it,” then she went home and built her photography game for me to play instead.

She once told me a story about photography from her childhood in the Soviet Union. She remembered taking a photo of a government building just because it looked cool, but a soldier saw her and confiscated the roll of film from her camera. Maybe the lesson was that exposing the reality of something can be just as much of a threat as shooting things in the militaristic sense of the word.

The violence common in games is a metaphor for the uncertainty and randomness we face in life, but my mum’s photography game taught me that violence isn’t the only way of coping with that problem, even in a game.

How the scrollytelling Mother’s Day card works

My mum inspired the randomness of the UFOs in this experiment with her ability to use a camera to capture the fleeting moments of sense in a chaotic world.

The combination of deterministic scroll-triggered animations with the randomness of the UFOs and text physics is possible using alien technology I’ve not seen used much in the wild: scroll-snap events. This emergent module — available only in Chrome and Opera at the time of writing — provides a simple JavaScript API so that when we style the page to snap between the day and night scenes, we can trigger behavior that isn’t possible in CSS alone, like the random flight paths of the UFOs, and the Pretext-inspired effect of the UFOs repelling letters as the spaceships fly through the text.

Sidenote: Randomness in CSS is coming, but it only works in Safari for now.

Here’s the CSS to enable scroll snapping:

/* The scroll container */
body {
  overflow-y: auto;
  scroll-snap-type: y mandatory;
}

/* Each snap target */
.snap-panel {
  scroll-snap-align: start;
  scroll-snap-stop: always;
}

…and the JavaScript to handle scroll snap events:

// scrollsnapchanging fires while the user is scrolling —
// snapTargetBlock is the panel they are heading toward.
snapScroller.addEventListener('scrollsnapchanging', ({ snapTargetBlock }) => {
  markPanelStates({ active: selectedPanel, incoming: snapTargetBlock });
  if (snapTargetBlock === dayPanel)   onScrollingTowardDay();
  if (snapTargetBlock === nightPanel) onScrollingTowardNight();
});

// scrollsnapchange fires once a panel has snapped into place —
// snapTargetBlock is the panel now fully in view.
snapScroller.addEventListener('scrollsnapchange', ({ snapTargetBlock }) => {
  selectedPanel = snapTargetBlock;
  markPanelStates({ active: selectedPanel, incoming: null });
  if (snapTargetBlock === dayPanel)   onLandedOnDay();
  if (snapTargetBlock === nightPanel) onLandedOnNight();
});

You can see that handling these events lets us create context-aware transitions between the two scenes, depending on the state of the game logic when the user slides between day and night and back again. My mum would always give me another chance to get things right.

In case you found this while Googling for a conventional Mother’s Day gift idea…

Parting words: In the unlikely event that your mum is not into avant-garde homemade virtual gifts that showcase emergent browser features, get her a Kindle or something. I bought my mum a Kindle when she was alive. We’d read the same novel separately on our Kindles, then we’d compare notes over the phone on the days I couldn’t visit.

Happy Mother’s Day, everyone!


A Scrollytelling Gift for Mum on Mother’s Day 2026 originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.



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

Wednesday, May 6, 2026

Clear today!



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

Current wind speeds: 7 from the Southwest

Pollen: 0

Sunrise: May 6, 2026 at 05:47PM

Sunset: May 7, 2026 at 07:50AM

UV index: 0

Humidity: 93%

via https://ift.tt/SN63Cuv

May 7, 2026 at 10:02AM

Google’s Prompt API

Mat Marquis on Google pulling the web standards equivalent of U2 album marketing:

As a Chrome user, you’ll have received Gemini Nano in the form of a 4GB transfer recently; no permission asked or required. If you remove it, Chrome will re-download it. For reasons I can only guess at, Gemini Nano is presumably now considered to be part of Chrome itself, despite being a standalone product that is included alongside but not integrated into the browser — the way a copy of Bonzi Buddy included in a browser update might be considered a part of said browser.

It’s not exactly new news, as we’ve had published explainers on it for over a year now, as well as an intent to prototype for just as long.

Mozilla has already voiced its concerns/opposition:

According to Chrome’s documentation, to use the prompt API you must ‘acknowledge’ Google’s Generative AI Prohibited Uses Policy. Elements of this policy go beyond law. For example:

Do not engage … generating or distributing content that facilitates … Sexually explicit content
Do not engage in misinformation, misrepresentation, or misleading activities. This includes … Facilitating misleading claims related to governmental or democratic processes

This seems like a bad direction for an API on the web platform, and sets a worrying precedent for more APIs that have UA-specific rules around usage.

I have nothing to add, only that this is the sort of thing that seems worth knowing. Mat’s take-home isn’t exactly comforting because, remember, this has already shipped:

I’d like to say that something to the tune of “their whole argument hinges on ‘positive developer sentiment,’ so let’s show them that there isn’t any” — but there isn’t any; they cited places where there isn’t any. That’s not how it works for them. Google participates in the web standards process the way a bear participates in the “camping” process.

[…]

Remember this the next time Google announces an “exciting new standard” that they’re heroically championing — for you, for users, for good of the web — in language that has just a hint of inevitability about it.

The browser ecosystem has historically provided us with plenty of concerns. Alex Russell’s writing is a treasure trove of the current limits of browser choice. And things are especially murky when we need to be reminded that not all browser APIs are Web APIs.

Maybe helpful, maybe not:

Chrome browser settings with system tab open showing disabled on-device AI option.

More coverage, if you’d like:


Google’s Prompt API originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.



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

Making Zigzag CSS Layouts With a Grid + Transform Trick

Most grid layouts sit in neat rows, perfectly aligned, like soldiers in formation. But sometimes you want something with more rhythm — a layout where items cascade diagonally, like water flowing down a waterfall.

This is the zigzag layout. And building it requires a small trick that reveals something fascinating about how CSS transforms actually work.

The Strategy

Before writing a single line of CSS, let’s think about approach.

The first idea that comes to mind: set up a flex container with flex-direction: column and flex-wrap: wrap, so items flow down and then wrap into a second column. Usually we think of the flex-wrap property in terms of rows, but the nice thing about flexbox is that it works in either orientation.

Two problems make this approach awkward:

  1. You need a fixed height. You have to tell the container “you are 500px tall” for wrapping to kick in. That’s brittle.
  2. The tab order breaks. Items flow down the first column (i.e., 1, 2, 3), then jump to the second column (i.e., 4, 5, 6). That’s not a waterfall. That’s two buckets.

To be fair, the CSS Grid approach we’re about to build has its own hardcoded value. We’ll get to that. But it sidesteps the Tab order problem entirely, and that’s a meaningful win.

The Grid Plan

Here’s what I want to do instead:

  1. Create a two-column grid with items sitting side by side, nothing fancy.
  2. Select every item in the second column, the even ones.
  3. Shift them down by half of their own height to establish the staggered layout.

That shift is where the magic happens. Let’s build it.

The Grid

We start with a wrapper and five items. Nothing in the file yet, just a blank slate.

<div class="wrapper">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>
*,
*::before,
*::after {
  box-sizing: border-box;
}

.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  max-width: 800px;
  margin: 0 auto;
}

.item {
  height: 100px;
  border: 2px solid;
}

We’re applying box-sizing: border-box globally because without it, the items aren’t actually 100px tall — they’re slightly taller once the border gets added. This will matter in a moment.

The Shift

Now the fun part. Let’s grab every even item and translate it down:

.item:nth-child(even of .item) {
  transform: translateY(50%);
}

A quick note on the selector. You might reach for .item:nth-of-type(even) here, and in this demo it would produce the same result since all the children are the same element type. But nth-of-type selects by tag name, not by class. So if you ever mix different element types inside the wrapper, it’ll match in ways you don’t expect. :nth-child(even of .item) is more precise because it explicitly filters by class, and it’s well-supported in modern browsers.

The zigzag emerges immediately. But let’s pause here, because something subtle is happening and it’s worth understanding.

Transform Percentages Are Different

Percentages in transforms work completely differently than they do anywhere else in CSS.

In flow layout, positioned layout, or really any layout mode, a percentage refers to the parent’s available space. If you write width: 50% on an element inside a wrapper, you’re saying: The container is this wide. Make me half of that.

Transforms don’t work this way. In a transform, percentages refer to the element itself. So translateY(50%) doesn’t mean “move down by half of the available space.” It means “move down by half of your own height.” If the element is 200px tall, it moves down by 100px.

This is actually the same coordinate-system behavior you see with the individual translate(), scale(), and rotate() CSS properties. All of them are applied in the element’s own coordinate space, post-layout. The browser finishes laying everything out first, including positions, sizes — basically the whole box model — and then applies the transform relative to the element itself. That’s why scale(2) grows outward from the element’s center, not from the top-left of the page.

This is exactly why the trick works. Each even item shifts down relative to its own size, not the container’s. The zigzag stays proportional no matter how tall the items are.

The result looks close. But it’s not quite right.

The Gap Problem

We can expose the imperfection by cranking the gap up to something absurd — say, 100px. When we do, the even items clearly aren’t sitting where they should. They need to travel a little further to account for the vertical space between rows.

Here’s the fix. First, let’s store the gap in a CSS custom property so we can reference it in multiple places:

.wrapper {
  --gap: 16px;

  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap);
  max-width: 800px;
  margin: 0 auto;
}

.item:nth-child(even of .item) {
  transform: translateY(calc(50% + var(--gap) / 2));
}

We translate by 50% of the element’s height plus half of the gap. We divide the gap by 2 because we only need to cover half the distance between rows — the full value would push it too far.

Set the gap to 16px, it looks great. Set it to 100px, it still looks great. The math holds regardless of the value.

The Overflow Surprise

We’ve solved the core puzzle. But there’s a hidden problem waiting to surface.

Let’s add a border to the wrapper to see its boundaries:

.wrapper {
  border: 2px solid red;
}

With five items, everything looks fine. The wrapper contains all of its children. No overflow. No issues.

Now add a sixth item:

<div class="wrapper">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

The sixth item is even. It gets translated down. And it spills right out of the container.

Why? Because transforms don’t affect layout. As far as the browser’s layout engine is concerned, that sixth item is still sitting in its original, untranslated position. The wrapper sizes itself based on that original position. The transform shifts pixels visually, but the parent has no idea anything moved.

We surprised the browser.

The Fix: Reserve the Space

The simplest solution is to add padding-bottom (or padding-block-end) to the wrapper, enough to accommodate the overshoot. The padding needs to match the translation: half the item height plus half the gap.

Since padding percentages reference the parent’s width (not the child’s height), we can’t use the same 50% trick here. Instead, we store the item height as a variable:

.wrapper {
  --gap: 16px;
  --item-height: 100px;
  
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gap);
  margin: 0 auto;
  max-width: 800px;
  padding-bottom: calc(var(--item-height) / 2 + var(--gap) / 2);
}

.item {
  border: 2px solid;
  height: var(--item-height);
}

Now, I’ll be up front: --item-height: 100px is a hard-coded value. That’s the same kind of brittleness I flagged in the flexbox approach, where you need a fixed container height for wrapping to work. Both approaches ask you to know a dimension ahead of time. The difference here is that you’re locking down the item height rather than the container height, and the rest of the layout — column structure, gap math, source order — stays flexible. It’s a trade-off, not a deal-breaker, but it’s worth being honest about.

The wrapper now reserves exactly enough space at the bottom. No overflow. No surprises.

A Note on Accessibility

This approach keeps items in their natural source order, and that matters more than it might seem at first glance.

Screen readers are unaffected. Transforms are purely visual. The DOM order stays 1-6, and that’s exactly how assistive technology will announce them. No reordering surprises, unlike the flexbox column-wrap approach where the visual order and DOM order can diverge.

Focus order stays intact, too. When someone tabs through the items, focus follows the source order, not where the items appear visually. In our zigzag, the visual flow and source order both cascade left-right, top-down, so they naturally agree. If your layout ever gets complex enough that visual and source order start to diverge, that’s when you’d need to think more carefully about focus management.

Respect motion preferences. The zigzag itself is static — we’re not animating the transform. But if you ever decide to animate items into their staggered positions (say, on page load), wrap that animation in a prefers-reduced-motion check:

/* animates when user has no motion preference */
@media (prefers-reduced-motion: no-preference) {
  .item {
    animation: slide-in 0.3s ease-out both;
  }
}

In this case, we’ve set it up so that users who have no preference on motion are the only ones who get the animation. Typically, though, you might do the inverse of that. The layout still works either way.

The Final Demo

Once again:

Conclusion

The zigzag layout is really just three ideas stacked on top of each other:

  1. A two-column grid gives us the foundation.
  2. translateY(50%) creates the stagger and works because transform percentages reference the element itself, not the parent.
  3. padding-bottom reserves space for the translated items because transforms move pixels without telling the layout engine.

Change the gap. Change the item height. Add more items. The zigzag holds.


Making Zigzag CSS Layouts With a Grid + Transform Trick originally handwritten and published with love on CSS-Tricks. You should really get the newsletter as well.



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

Mostly Clear today!

With a high of F and a low of 47F. Currently, it's 53F and Clear outside. Current wind speeds: 4 from the Southeast Pollen: 0 Su...