> All in One 586: October 2025

Ads

Friday, October 31, 2025

Mostly Clear today!



With a high of F and a low of 20F. Currently, it's 30F and Clear outside.

Current wind speeds: 3 from the North

Pollen: 0

Sunrise: October 31, 2025 at 07:18PM

Sunset: November 1, 2025 at 05:51AM

UV index: 0

Humidity: 70%

via https://ift.tt/O1ESn3y

November 1, 2025 at 10:02AM

Thursday, October 30, 2025

Partly Cloudy today!



With a high of F and a low of 27F. Currently, it's 35F and Clear outside.

Current wind speeds: 3 from the Southeast

Pollen: 0

Sunrise: October 30, 2025 at 07:17PM

Sunset: October 31, 2025 at 05:52AM

UV index: 0

Humidity: 55%

via https://ift.tt/JNKTeCr

October 31, 2025 at 10:02AM

Wednesday, October 29, 2025

Mostly Clear today!



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

Current wind speeds: 12 from the Southwest

Pollen: 0

Sunrise: October 29, 2025 at 07:16PM

Sunset: October 30, 2025 at 05:53AM

UV index: 0

Humidity: 41%

via https://ift.tt/erJiaug

October 30, 2025 at 10:02AM

Getting Creative With Small Screens

Over the past few months, I’ve explored how we can get creative using well-supported CSS properties. Each article is intended to nudge web design away from uniformity, toward designs that are more distinctive and memorable. One bit of feedback from Phillip Bagleg deserves a follow up:

Andy’s guides are all very interesting, but mostly impractical in real life. Very little guidance on how magazine style design, works when thrown into a responsive environment.

Fair point well made, Phillip. So, let’s bust the myth that editorial-style web design is impractical on small screens.

A series of three desktop-sized screenshots from Patty Meltt's website.

My brief: Patty Meltt is an up-and-coming country music sensation, and she needed a website to launch her new album and tour. She wanted it to be distinctive-looking and memorable, so she called Stuff & Nonsense. Patty’s not real, but the challenges of designing and developing sites like hers are.

The problem with endless columns

On mobile, people can lose their sense of context and can’t easily tell where a section begins or ends. Good small-screen design can help orient them using a variety of techniques.

When screen space is tight, most designers collapse their layouts into a single long column. That’s fine for readability, but it can negatively impact the user experience when hierarchy disappears; rhythm becomes monotonous, and content scrolls endlessly until it blurs. Then, nothing stands out, and pages turn from being designed experiences into content feeds.

Like a magazine, layout delivers visual cues in a desktop environment, letting people know where they are and suggesting where to go next. This rhythm and structure can be as much a part of visual storytelling as colour and typography.

But those cues frequently disappear on small screens. Since we can’t rely on complex columns, how can we design visual cues that help readers feel oriented within the content flow and stay engaged? One answer is to stop thinking in terms of one long column of content altogether. Instead, treat each section as a distinct composition, a designed moment that guides readers through the story.

Designing moments instead of columns

Even within a narrow column, you can add variety and reduce monotony by thinking of content as a series of meaningfully designed moments, each with distinctive behaviours and styles. We might use alternative compositions and sizes, arrange elements using different patterns, or use horizontal and vertical scrolling to create experiences and tell stories, even when space is limited. And fortunately, we have the tools we need to do that at our disposal:

These moments might move horizontally, breaking the monotony of vertical scrolling, giving a section its own rhythm, and keeping related content together.

Make use of horizontal scrolling

My desktop design for Patty’s discography includes her album covers arranged in a modular grid. Layouts like these are easy to achieve using my modular grid generator.

Six album covers arranged in a three-by-two grid next to a column of text on the left. Four columns total.

But that arrangement isn’t necessarily going to work for small screens, where a practical solution is to transform the modular grid into a horizontal scrolling element. Scrolling horizontally is a familiar behaviour and a way to give grouped content its own stage, the way a magazine spread might.

I started by defining the modular grid’s parent — in this case, the imaginatively named modular-wrap — as a container:

.modular-wrap {
  container-type: inline-size;
  width: 100%;
}

Then, I added grid styles to create the modular layout:

.modular {
  display: grid;
  gap: 1.5rem;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  overflow-x: visible;
  width: 100%;
}

It would be tempting to collapse those grid modules on small screens into a single column, but that would simply stack one album on top of another.

Album covers arranged in a single column.
Collapsing grid modules on small screens into a single column

So instead, I used a container query to arrange the album covers horizontally and enable someone to scroll across them:

@container (max-width: 30rem) {
  #example-1 .modular {
    display: grid;
    gap: 1.5rem;
    grid-auto-columns: minmax(70%, 1fr);
    grid-auto-flow: column;
    grid-template-columns: none;
    grid-template-rows: 1fr;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}
A paragraph of text above a series of albums covers arranged in a single row, overflowing the screen for scrolling.
Album covers are arranged horizontally rather than vertically. See this example in my lab.

Now, Patty’s album covers are arranged horizontally rather than vertically, which forms a cohesive component while preventing people from losing their place within the overall flow of content.

Push elements off-canvas

Last time, I explained how to use shape-outside and create the illusion of text flowing around both sides of an image. You’ll often see this effect in magazines, but hardly ever online.

Patty Meltt staring straight into the camera in between two columns of text.
The illusion of text flowing around both sides of an image

Desktop displays have plenty of space available, but what about smaller ones? Well, I could remove shape-outside altogether, but if I did, I’d also lose much of this design’s personality and its effect on visual storytelling. Instead, I can retain shape-outside and place it inside a horizontally scrolling component where some of its content is off-canvas and outside the viewport.

My content is split between two divisions: the first with half the image floating right, and the second with the other half floating left. The two images join to create the illusion of a single image at the centre of the design:

<div class="content">
  <div>
  <img src="img-left.webp" alt="">
  <p><!-- ... --></p>
  </div>

<div>
  <img src="img-right.webp" alt="">
  <p><!-- ... --></p>
  </div>
</div>

I knew this implementation would require a container query because I needed a parent element whose width determines when the layout should switch from static to scrolling. So, I added a section outside that content so that I could reference its width for determining when its contents should change:

<section>
  <div class="content">
    <!-- ... -->
  </div>
</section>
section {
  container-type: inline-size;
  overflow-x: auto;
  position: relative;
  width: 100%;
}

My technique involves spreading content across two equal-width divisions, and these grid column properties will apply to every screen size:

.content {
  display: grid;
  gap: 0;
  grid-template-columns: 1fr 1fr;
  width: 100%;
}

Then, when the section’s width is below 48rem, I altered the width of my two columns:

@container (max-width: 48rem) {
  .content {
    grid-template-columns: 85vw 85vw;
  }
}

Setting the width of each column to 85% — a little under viewport width — makes some of the right-hand column’s content visible, which hints that there’s more to see and encourages someone to scroll across to look at it.

Showing the left and right halves of a layout that supports horizontal scrolling.
Some of the right-hand column’s content is visible. See this example in my lab.

The same principle works at a larger scale, too. Instead of making small adjustments, we can turn an entire section into a miniature magazine spread that scrolls like a story in print.

Build scrollable mini-spreads

When designing for a responsive environment, there’s no reason to lose the expressiveness of a magazine-inspired layout. Instead of flattening everything into one long column, sections can behave like self-contained mini magazine spreads.

A desktop layout showing a column of text in between two columns of differently-sized and shaped images.
Sections can behave like self-contained mini magazine spreads.

My final shape-outside example flowed text between two photomontages. Parts of those images escaped their containers, creating depth and a layout with a distinctly editorial feel. My content contained the two images and several paragraphs:

<div class="content">
  <img src="left.webp" alt="">
  <img src="right.webp" alt="">
  <p><!-- ... --></p>
  <p><!-- ... --></p>
  <p><!-- ... --></p>
</div>

Two images float either left or right, each with shape-outside applied so text flows between them:

.content img:nth-of-type(1) {
  float: left;
  width: 45%;
  shape-outside: url("left.webp");
}

.spread-wrap .content img:nth-of-type(2) {
  float: right;
  width: 35%;
  shape-outside: url("right.webp");
}

That behaves beautifully at large screen sizes, but on smaller ones it feels cramped. To preserve the design’s essence, I used a container query to transform its layout into something different altogether.

First, I needed another parent element whose width would determine when the layout should change. So, I added a section outside so that I could reference its width and gave it a little padding and a border to help differentiate it from nearby content:

<section>
  <div class="content">
    <!-- ... -->
  </div>
</section>
section {
  border: 1px solid var(--border-stroke-color);
  box-sizing: border-box;
  container-type: inline-size;
  overflow-x: auto;
  padding: 1.5rem;
  width: 100%;
}

When the section’s width is below 48rem, I introduced a horizontal Flexbox layout:

@container (max-width: 48rem) {
  .content {
    align-items: center;
    display: flex;
    flex-wrap: nowrap;
    gap: 1.5rem;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }
}

And because this layout depends on a container query, I used container query units (cqi) for the width of my flexible columns:

.content > * {
  flex: 0 0 85cqi;
  min-width: 85cqi;
  scroll-snap-align: start;
}
Showing a three-column layout split between three screenshots to demonstrate horizontally scrolling through the layout.
On small screens, the layout flows from image to paragraphs to image. See this example in my lab.

Now, on small screens, the layout flows from image to paragraphs to image, with each element snapping into place as someone swipes sideways. This approach rearranges elements and, in doing so, slows someone’s reading speed by making each swipe an intentional action.

To prevent my images from distorting when flexed, I applied auto-height combined with object-fit:

.content img {
  display: block;
  flex-shrink: 0;
  float: none;
  height: auto;
  max-width: 100%;
  object-fit: contain;
}

Before calling on the Flexbox order property to place the second image at the end of my small screen sequence:

.content img:nth-of-type(2) {
  order: 100;
}

Mini-spreads like this add movement and rhythm, but orientation offers another way to shift perspective without scrolling. A simple rotation can become a cue for an entirely new composition.

Make orientation-responsive layouts

When someone rotates their phone, that shift in orientation can become a cue for a new layout. Instead of stretching a single-column design wider, we can recompose it entirely, making a landscape orientation feel like a fresh new spread.

A desktop layout showing a picture of Patty Meltt sitting and playing an acoustic guitar as text flows around the shape of the image on the right.
Turning a phone sideways is an opportunity to recompose a layout.

Turning a phone sideways is an opportunity to recompose a layout, not just reflow it. When Patty’s fans rotate their phones to landscape, I don’t want the same stacked layout to simply stretch wider. Instead, I want to use that additional width to provide a different experience. This could be as easy as adding extra columns to a composition in a media query that’s applied when the device’s orientation is detected in landscape:

@media (orientation: landscape) {
  .content {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
}

For the long-form content on Patty Meltt’s biography page, text flows around a polygon clip-path placed over a large faux background image. This image is inline, floated, and has its width set to 100%:

<div class="content">
  <img src="patty.webp" alt="">
  <!-- ... -->
</div>
.content > img {
  float: left;
  width: 100%;
  max-width: 100%;
}

Then, I added shape-outside using the polygon coordinates and added a shape-margin:

.content > img {
  shape-outside: polygon(...);
  shape-margin: 1.5rem;
}

I only want the text to flow around the polygon and for the image to appear in the background when a device is held in landscape, so I wrapped that rule in a query which detects the screen orientation:

@media (orientation: landscape) {
  .content > img {
    float: left;
    width: 100%;
    max-width: 100%;
    shape-outside: polygon(...);
    shape-margin: 1.5rem;
  }
}
Image of Patty Meltt sitting and playing an acoustic guitar above a column of text.
See this example in my lab.

Those properties won’t apply when the viewport is in portrait mode.

Design stories that adapt, not layouts that collapse

Small screens don’t make design more difficult; they make it more deliberate, requiring designers to consider how to preserve a design’s personality when space is limited.

Phillip was right to ask how editorial-style design can work in a responsive environment. It does, but not by shrinking a print layout. It works when we think differently about how content flexes, shifts, and scrolls, and when a design responds not just to a device, but to how someone holds it.

The goal isn’t to mimic miniature magazines on mobile, but to capture their energy, rhythm, and sense of discovery that print does so well. Design is storytelling, and just because there’s less space to tell one, it shouldn’t mean it should make any less impact.


Getting Creative With Small Screens originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



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

Tuesday, October 28, 2025

Clear today!



With a high of F and a low of 21F. Currently, it's 30F and Clear outside.

Current wind speeds: 10 from the Northwest

Pollen: 0

Sunrise: October 28, 2025 at 07:15PM

Sunset: October 29, 2025 at 05:54AM

UV index: 0

Humidity: 53%

via https://ift.tt/wKrQ71z

October 29, 2025 at 10:02AM

Monday, October 27, 2025

Rain/Wind Early today!



With a high of F and a low of 31F. Currently, it's 40F and Showers in the Vicinity outside.

Current wind speeds: 19 from the North

Pollen: 0

Sunrise: October 27, 2025 at 07:14PM

Sunset: October 28, 2025 at 05:56AM

UV index: 0

Humidity: 90%

via https://ift.tt/7PcEGf8

October 28, 2025 at 10:02AM

Pure CSS Tabs With Details, Grid, and Subgrid

Sunday, October 26, 2025

Mostly Cloudy today!



With a high of F and a low of 41F. Currently, it's 47F and Mostly Cloudy outside.

Current wind speeds: 14 from the Southeast

Pollen: 0

Sunrise: October 26, 2025 at 07:13PM

Sunset: October 27, 2025 at 05:57AM

UV index: 0

Humidity: 92%

via https://ift.tt/7iSPw3j

October 27, 2025 at 10:02AM

Saturday, October 25, 2025

Mostly Cloudy today!



With a high of F and a low of 39F. Currently, it's 45F and Mostly Cloudy outside.

Current wind speeds: 10 from the Southeast

Pollen: 0

Sunrise: October 25, 2025 at 07:12PM

Sunset: October 26, 2025 at 05:58AM

UV index: 0

Humidity: 91%

via https://ift.tt/vDMqTne

October 26, 2025 at 10:02AM

Friday, October 24, 2025

Partly Cloudy today!



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

Current wind speeds: 3 from the Southeast

Pollen: 0

Sunrise: October 24, 2025 at 07:11PM

Sunset: October 25, 2025 at 05:59AM

UV index: 0

Humidity: 84%

via https://ift.tt/pVIOWsY

October 25, 2025 at 10:02AM

CSS Animations That Leverage the Parent-Child Relationship

Modern CSS has great ways to position and move a group of elements relative to each other, such as anchor positioning. That said, there are instances where it may be better to take up the old ways for a little animation, saving time and effort.

We’ve always been able to affect an element’s structure, like resizing and rotating it. And when we change an element’s intrinsic sizing, its children are affected, too. This is something we can use to our advantage.

Let’s say a few circles need to move towards and across one another. Something like this:

Our markup might be as simple as a <main> element that contains four child .circle elements:

<main>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
  <div class="circle"></div>
</main>

As far as rotating things, there are two options. We can (1) animate the <main> parent container, or (2) animate each .circle individually.

Tackling that first option is probably best because animating each .circle requires defining and setting several animations rather than a single animation. Before we do that, we ought to make sure that each .circle is contained in the <main> element and then absolutely position each one inside of it:

main {
  contain: layout;
}

.circle {
  position: absolute;

  &:nth-of-type(1){
    background-color: rgb(0, 76, 255);
  }
  &:nth-of-type(2){
    background-color: rgb(255, 60, 0);
    right: 0;
  }
  &:nth-of-type(3){
    background-color: rgb(0, 128, 111);
    bottom: 0;
  }
  &:nth-of-type(4){
    background-color: rgb(255, 238, 0);
    right: 0;
    bottom: 0;
  }
}

If we rotate the <main> element that contains the circles, then we might create a specific .animate class just for the rotation:

/* Applied on <main> (the parent element) */
.animate {
  width: 0;
  transform: rotate(90deg);
  transition: width 1s, transform 1.3s;
}

…and then set it on the <main> element with JavaScript when the button is clicked:

const MAIN = document.querySelector("main");
function play() {
  MAIN.className = "";
  MAIN.offsetWidth;
  MAIN.className = "animate";
}

It looks like we’re animating four circles, but what we’re really doing is rotating the parent container and changing its width, which rotates and squishes all the circles in it as well:

Each .circle is fixed to a respective corner of the <main> parent with absolute positioning. When the animation is triggered in the parent element — i.e. <main> gets the .animate class when the button is clicked — the <main> width shrinks and it rotates 90deg. That shrinking pulls each .circle closer to the <main> element’s center, and the rotation causes the circles to switch places while passing through one another.

This approach makes for an easier animation to craft and manage for simple effects. You can even layer on the animations for each individual element for more variations, such as two squares that cross each other during the animation.

/* Applied on <main> (the parent element) */
.animate {
  transform:  skewY(30deg) rotateY(180deg);
  transition: 1s transform .2s;
  
  .square {
    transform: skewY(30deg); 
    transition: inherit;
  }
}

See that? The parent <main> element makes a 30deg skew and flip along the Y-axis, while the two child .square elements counter that distortion with the same skew. The result is that you see the child squares flip positions while moving away from each other.

If we want the squares to form a separation without the flip, here’s a way to do that:

/* Applied on <main> (the parent element) */
.animate {
  transform:  skewY(30deg);
  transition: 1s transform .2s;
  
  .square {
    transform: skewY(-30deg); 
    transition: inherit;
  }
}

This time, the <main> element is skewed 30deg, while the .square children cancel that with a -30deg skew.

Setting skew() on a parent element helps rearrange the children beyond what typical rectangular geometry allows. Any change in the parent can be complemented, countered, or cancelled by the children depending on what effect you’re looking for.

Here’s an example where scaling is involved. Notice how the <main> element’s skewY() is negated by its children and scale()s at a different value to offset it a bit.

/* Applied on <main> (the parent element) */
.animate {
  transform: rotate(-180deg) scale(.5) skewY(45deg) ;
  transition: .6s .2s; 
  transition-property: transform, border-radius;
  
  .squares {
    transform: skewY(-45deg) scaleX(1.5); 
    border-radius: 10px;
    transition: inherit;
  }
}

The parent element (<main>) rotates counter-clockwise (rotate(-180deg)), scales down (scale(.5)), and skews vertically (skewY(45deg)). The two children (.square) cancel the parent’s distortion by using the negative value of the parent’s skew angle (skewY(-45deg)), and scale up horizontally (scaleX(1.5)) to change from a square to a horizontal bar shape.

There are a lot of these combinations you can come up with. I’ve made a few more below where, instead of triggering the animation with a JavaScript interaction, I’ve used a <details> element that triggers the animation when it is in an [open] state once the <summary> element is clicked. And each <summary> contains an .icon child demonstrating a different animation when the <details> toggles between open and closed.

Click on a <details> to toggle it open and closed to see the animations in action.

That’s all I wanted to share — it’s easy to forget that we get some affordances for writing efficient animations if we consider how transforming a parent element intrinsically affects the size, position, and orientation. That way, for example, there’s no need to write complex animations for each individual child element, but rather leverage what the parent can do, then adjust the behavior at the child level, as needed.


CSS Animations That Leverage the Parent-Child Relationship originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



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

Thursday, October 23, 2025

Partly Cloudy today!



With a high of F and a low of 35F. Currently, it's 44F and Partly Cloudy outside.

Current wind speeds: 3 from the Northeast

Pollen: 0

Sunrise: October 23, 2025 at 07:10PM

Sunset: October 24, 2025 at 06:00AM

UV index: 0

Humidity: 70%

via https://ift.tt/LD60OHv

October 24, 2025 at 10:02AM

Wednesday, October 22, 2025

Partly Cloudy today!



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

Current wind speeds: 8 from the Southeast

Pollen: 0

Sunrise: October 22, 2025 at 07:09PM

Sunset: October 23, 2025 at 06:02AM

UV index: 0

Humidity: 45%

via https://ift.tt/CSGlDWo

October 23, 2025 at 10:02AM

An Introduction to JavaScript Expressions

Editor’s note: Mat Marquis and Andy Bell have released JavaScript for Everyone, an online course offered exclusively at Piccalilli. This post is an excerpt from the course taken specifically from a chapter all about JavaScript expressions. We’re publishing it here because we believe in this material and want to encourage folks like yourself to sign up for the course. So, please enjoy this break from our regular broadcasting to get a small taste of what you can expect from enrolling in the full JavaScript for Everyone course.

Hey, I’m Mat, but “Wilto” works too — I’m here to teach you JavaScript.

Well, not here-here; technically, I’m over at JavaScript for Everyone to teach you JavaScript. What we have here is a lesson from the JavaScript for Everyone module on lexical grammar and analysis — the process of parsing the characters that make up a script file and converting it into a sequence of discrete “input elements” (lexical tokens, line ending characters, comments, and whitespace), and how the JavaScript engine interprets those input elements.


An expression is code that, when evaluated, resolves to a value. 2 + 2 is a timeless example.

2 + 2
// result: 4

As mental models go, you could do worse than “anywhere in a script that a value is expected you can use an expression, no matter how simple or complex that expression may be:”

function numberChecker( checkedNumber ) {
  if( typeof checkedNumber === "number" ) {
    console.log( "Yep, that's a number." );
  }
}

numberChecker( 3 );
// result: Yep, that's a number.

numberChecker( 10 + 20 );
// result: Yep, that's a number.

numberChecker( Math.floor( Math.random() * 20 ) / Math.floor( Math.random() * 10 ) );
// result: Yep, that's a number.

Granted, JavaScript doesn’t tend to leave much room for absolute statements. The exceptions are rare, but it isn’t the case absolutely, positively, one hundred percent of the time:

console.log( -2**1 );
// result: Uncaught SyntaxError: Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence

Still, I’m willing to throw myself upon the sword of “um, actually” on this one. That way of looking at the relationship between expressions and their resulting values is heart-and-soul of the language stuff, and it’ll get you far.

Primary Expressions

There’s sort of a plot twist, here: while the above example reads to our human eyes as an example of a number, then an expression, then a complex expression, it turns out to be expressions all the way down. 3 is itself an expression — a primary expression. In the same way the first rule of Tautology Club is Tautology Club’s first rule, the number literal 3 is itself an expression that resolves in a very predictable value (psst, it’s three).

console.log( 3 );
// result: 3

Alright, so maybe that one didn’t necessarily need the illustrative snippet of code, but the point is: the additive expression 2 + 2 is, in fact, the primary expression 2 plus the primary expression 2.

Granted, the “it is what it is” nature of a primary expression is such that you won’t have much (any?) occasion to point at your display and declare “that is a primary expression,” but it does afford a little insight into how JavaScript “thinks” about values: a variable is also a primary expression, and you can mentally substitute an expression for the value it results in — in this case, the value that variable references. That’s not the only purpose of an expression (which we’ll get into in a bit) but it’s a useful shorthand for understanding expressions at their most basic level.

There’s a specific kind of primary expression that you’ll end up using a lot: the grouping operator. You may remember it from the math classes I just barely passed in high school:

console.log( 2 + 2 * 3 );
// result: 8 

console.log( ( 2 + 2 ) * 3 );
// result: 12

The grouping operator (singular, I know, it kills me too) is a matched pair of parentheses used to evaluate a portion of an expression as a single unit. You can use it to override the mathematical order of operations, as seen above, but that’s not likely to be your most common use case—more often than not you’ll use grouping operators to more finely control conditional logic and improve readability:

const minValue = 0;
const maxValue = 100;
const theValue = 50;

if( ( theValue > minValue ) && ( theValue < maxValue ) ) {
  // If ( the value of `theValue` is greater than that of `minValue` ) AND less than `maxValue`):
  console.log( "Within range." );
}
// result: Within range.

Personally, I make a point of almost never excusing my dear Aunt Sally. Even when I’m working with math specifically, I frequently use parentheses just for the sake of being able to scan things quickly:

console.log( 2 + ( 2 * 3 ) );
// result: 8

This use is relatively rare, but the grouping operator can also be used to remove ambiguity in situations where you might need to specify that a given syntax is intended to be interpreted as an expression. One of them is, well, right there in your developer console.

The syntax used to initialize an object — a matched pair of curly braces — is the same as the syntax used to group statements into a block statement. Within the global scope, a pair of curly braces will be interpreted as a block statement containing a syntax that makes no sense given that context, not an object literal. That’s why punching an object literal into your developer console will result in an error:

{ "theValue" : true }
// result: `Uncaught SyntaxError: unexpected token: ':'

It’s very unlikely you’ll ever run into this specific issue in your day-to-day JavaScript work, seeing as there’s usually a clear division between contexts where an expression or a statement are expected:

{
  const theObject = { "theValue" : true };
}

You won’t often be creating an object literal without intending to do something with it, which means it will always be in the context where an expression is expected. It is the reason you’ll see standalone object literals wrapped in a a grouping operator throughout this course — a syntax that explicitly says “expect an expression here”:

({ "value" : true });

However, that’s not to say you’ll never need a grouping operator for disambiguation purposes. Again, not to get ahead of ourselves, but an Independently-Invoked Function Expression (IIFE), an anonymous function expression used to manage scope, relies on a grouping operator to ensure the function keyword is treated as a function expression rather than a declaration:

(function(){
  // ...
})();

Expressions With Side Effects

Expressions always give us back a value, in no uncertain terms. There are also expressions with side effects — expressions that result in a value and do something. For example, assigning a value to an identifier is an assignment expression. If you paste this snippet into your developer console, you’ll notice it prints 3:

theIdentifier = 3;
// result: 3

The resulting value of the expression theIdentifier = 3 is the primary expression 3; classic expression stuff. That’s not what’s useful about this expression, though — the useful part is that this expression makes JavaScript aware of theIdentifier and its value (in a way we probably shouldn’t, but that’s a topic for another lesson). That variable binding is an expression and it results in a value, but that’s not really why we’re using it.

Likewise, a function call is an expression; it gets evaluated and results in a value:

function theFunction() {
  return 3;
};

console.log( theFunction() + theFunction() );
// result: 6

We’ll get into it more once we’re in the weeds on functions themselves, but the result of calling a function that returns an expression is — you guessed it — functionally identical to working with the value that results from that expression. So far as JavaScript is concerned, a call to theFunction effectively is the simple expression 3, with the side effect of executing any code contained within the function body:

function theFunction() {
  console.log( "Called." );
  return 3;
};

console.log( theFunction() + theFunction() );
/* Result:
Called.
Called.
6
*/

Here theFunction is evaluated twice, each time calling console.log then resulting in the simple expression 3 . Those resulting values are added together, and the result of that arithmetic expression is logged as 6.

Granted, a function call may not always result in an explicit value. I haven’t been including them in our interactive snippets here, but that’s the reason you’ll see two things in the output when you call console.log in your developer console: the logged string and undefined.

A browser open to an empty page, with the console opened. The first line is ‘console.log("Called.")’. The second line reads “Called.” The third line reads “undefined” is present, at a much lower contrast.

JavaScript’s built-in console.log method doesn’t return a value. When the function is called it performs its work — the logging itself. Then, because it doesn’t have a meaningful value to return, it results in undefined. There’s nothing to do with that value, but your developer console informs you of the result of that evaluation before discarding it.

Comma Operator

Speaking of throwing results away, this brings us to a uniquely weird syntax: the comma operator. A comma operator evaluates its left operand, discards the resulting value, then evaluates and results in the value of the right operand.

Based only on what you’ve learned so far in this lesson, if your first reaction is “I don’t know why I’d want an expression to do that,” odds are you’re reading it right. Let’s look at it in the context of an arithmetic expression:

console.log( ( 1, 5 + 20 ) );
// result: 25

The primary expression 1 is evaluated and the resulting value is discarded, then the additive expression 5 + 20 is evaluated, and that’s resulting value. Five plus twenty, with a few extra characters thrown in for style points and a 1 cast into the void, perhaps intended to serve as a threat to the other numbers.

And hey, notice the extra pair of parentheses there? Another example of a grouping operator used for disambiguation purposes. Without it, that comma would be interpreted as separating arguments to the console.log method — 1 and 5 + 20 — both of which would be logged to the console:

console.log( 1, 5 + 20 );
// result: 1 25

Now, including a value in an expression in a way where it could never be used for anything would be a pretty wild choice, granted. That’s why I bring up the comma operator in the context of expressions with side effects: both sides of the , operator are evaluated, even if the immediately resulting value is discarded.

Take a look at this validateResult function, which does something fairly common, mechanically speaking; depending on the value passed to it as an argument, it executes one of two functions, and ultimately returns one of two values.

For the sake of simplicity, we’re just checking to see if the value being evaluated is strictly true — if so, call the whenValid function and return the string value "Nice!". If not, call the whenInvalid function and return the string "Sorry, no good.":

function validateResult( theValue ) {
  function whenValid() {
    console.log( "Valid result." );
  };
  function whenInvalid() {
    console.warn( "Invalid result." );
  };

  if( theValue === true ) {
    whenValid();
    return "Nice!";
  } else {
    whenInvalid();
    return "Sorry, no good.";
  }
};

const resultMessage = validateResult( true );
// result: Valid result.

console.log( resultMessage );
// result: "Nice!"

Nothing wrong with this. The whenValid / whenInvalid functions are called when the validateResult function is called, and the resultMessage constant is initialized with the returned string value. We’re touching on a lot of future lessons here already, so don’t sweat the details too much.

Some room for optimizations, of course — there almost always is. I’m not a fan of having multiple instances of return, which in a sufficiently large and potentially-tangled codebase can lead to increased “wait, where is that coming from” frustrations. Let’s sort that out first:

function validateResult( theValue ) {
  function whenValid() {
    console.log( "Valid result." );
  };
  function whenInvalid() {
    console.warn( "Invalid result." );
  };

  if( theValue === true ) {
    whenValid();
  } else {
    whenInvalid();
  }
  return theValue === true ? "Nice!" : "Sorry, no good.";
};

const resultMessage = validateResult( true );
// result: Valid result.

resultMessage;
// result: "Nice!"

That’s a little better, but we’re still repeating ourselves with two separate checks for theValue. If our conditional logic were to be changed someday, it wouldn’t be ideal that we have to do it in two places.

The first — the if/else — exists only to call one function or the other. We now know function calls to be expressions, and what we want from those expressions are their side effects, not their resulting values (which, absent a explicit return value, would just be undefined anyway).

Because we need them evaluated and don’t care if their resulting values are discarded, we can use comma operators (and grouping operators) to sit them alongside the two simple expressions — the strings that make up the result messaging — that we do want values from:

function validateResult( theValue ) {
  function whenValid() {
    console.log( "Valid result." );
  };
  function whenInvalid() {
    console.warn( "Invalid result." );
  };
  return theValue === true ? ( whenValid(), "Nice!" ) : ( whenInvalid(), "Sorry, no good." );
};

const resultMessage = validateResult( true );
// result: Valid result.

resultMessage;
// result: "Nice!"

Lean and mean thanks to clever use of comma operators. Granted, there’s a case to be made that this is a little too clever, in that it could make this code a little more difficult to understand at a glance for anyone that might have to maintain this code after you (or, if you have a memory like mine, for your near-future self). The siren song of “I could do it with less characters” has driven more than one JavaScript developer toward the rocks of, uh, slightly more difficult maintainability. I’m in no position to talk, though. I chewed through my ropes years ago.


Between this lesson on expressions and the lesson on statements that follows it, well, that would be the whole ballgame — the entirety of JavaScript summed up, in a manner of speaking — were it not for a not-so-secret third thing. Did you know that most declarations are neither statement nor expression, despite seeming very much like statements?

Variable declarations performed with let or const, function declarations, class declarations — none of these are statements:

if( true ) let theVariable;
// Result: Uncaught SyntaxError: lexical declarations can't appear in single-statement context

if is a statement that expects a statement, but what it encounters here is one of the non-statement declarations, resulting in a syntax error. Granted, you might never run into this specific example at all if you — like me — are the sort to always follow an if with a block statement, even if you’re only expecting a single statement.

I did say “one of the non-statement declarations,” though. There is, in fact, a single exception to this rule — a variable declaration using var is a statement:

if( true ) var theVariable;

That’s just a hint at the kind of weirdness you’ll find buried deep in the JavaScript machinery. 5 is an expression, sure. 0.1 * 0.1 is 0.010000000000000002, yes, absolutely. Numeric values used to access elements in an array are implicitly coerced to strings? Well, sure — they’re objects, and their indexes are their keys, and keys are strings (or Symbols). What happens if you use call() to give this a string literal value? There’s only one way to find out — two ways to find out, if you factor in strict mode.

That’s where JavaScript for Everyone is designed take you: inside JavaScript’s head. My goal is to teach you the deep magic — the how and the why of JavaScript. If you’re new to the language, you’ll walk away from this course with a foundational understanding of the language worth hundreds of hours of trial-and-error. If you’re a junior JavaScript developer, you’ll finish this course with a depth of knowledge to rival any senior.

I hope to see you there.


JavaScript for Everyone is now available and the launch price runs until midnight, October 28. Save £60 off the full price of £249 (~$289) and get it for £189 (~$220)!


An Introduction to JavaScript Expressions originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



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

Tuesday, October 21, 2025

Clear today!



With a high of F and a low of 33F. Currently, it's 42F and Clear outside.

Current wind speeds: 7 from the Southwest

Pollen: 0

Sunrise: October 21, 2025 at 07:08PM

Sunset: October 22, 2025 at 06:03AM

UV index: 0

Humidity: 41%

via https://ift.tt/iRYkNdZ

October 22, 2025 at 10:02AM

Monday, October 20, 2025

Clear today!



With a high of F and a low of 30F. Currently, it's 42F and Clear outside.

Current wind speeds: 12 from the Northwest

Pollen: 0

Sunrise: October 20, 2025 at 07:07PM

Sunset: October 21, 2025 at 06:05AM

UV index: 0

Humidity: 34%

via https://ift.tt/Dk8x0u3

October 21, 2025 at 10:02AM

Building a Honeypot Field That Works

Honeypots are fields that developers use to prevent spam submissions.

They still work in 2025.

So you don’t need reCAPTCHA or other annoying mechanisms.

But you got to set a couple of tricks in place so spambots can’t detect your honeypot field.

Use This

I’ve created a Honeypot component that does everything I mention below. So you can simply import and use them like this:

<script>
  import { Honeypot } from '@splendidlabz/svelte'
</script>

<Honeypot name="honeypot-name" />

Or, if you use Astro, you can do this:

---
import { Honeypot } from '@splendidlabz/svelte'
---

<Honeypot name="honeypot-name" />

But since you’re reading this, I’m sure you kinda want to know what’s the necessary steps.

Preventing Bots From Detecting Honeypots

Here are two things that you must not do:

  1. Do not use <input type=hidden>.
  2. Do not hide the honeypot with inline CSS.

Bots today are already smart enough to know that these are traps — and they will skip them.

Here’s what you need to do instead:

  1. Use a text field.
  2. Hide the field with CSS that is not inline.

A simple example that would work is this:

<input class="honeypot" type="text" name="honeypot" />

<style>
  .honeypot {
    display: none;
  }
</style>

For now, placing the <style> tag near the honeypot seems to work. But you might not want to do that in the future (more below).

Unnecessary Enhancements

You may have seen these other enhancements being used in various honeypot articles out there:

  • aria-hidden to prevent screen readers from using the field
  • autocomplete=off and tabindex="-1" to prevent the field from being selected
<input ... aria-hidden autocomplete="off" tabindex="-1" />

These aren’t necessary because display: none itself already does the things these properties are supposed to do.

Future-Proof Enhancements

Bots get smarter everyday, so I won’t discount the possibility that they can catch what we’ve created above. So, here are a few things we can do today to future-proof a honeypot:

  1. Use a legit-sounding name attribute values like website or mobile instead of obvious honeypot names like spam or honeypot.
  2. Use legit-sounding CSS class names like .form-helper instead of obvious ones like .honeypot.
  3. Put the CSS in another file so they’re further away and harder to link between the CSS and honeypot field.

The basic idea is to trick spam bot to enter into this “legit” field.

<input class="form-helper" ... name="occupation" />

<!-- Put this into your CSS file, not directly in the HTML -->
<style>
  .form-helper {
    display: none;
  }
</style>

There’s a very high chance that bots won’t be able to differentiate the honeypot field from other legit fields.

Even More Enhancements

The following enhancements need to happen on the <form> instead of a honeypot field.

The basic idea is to detect if the entry is potentially made by a human. There are many ways of doing that — and all of them require JavaScript:

  1. Detect a mousemove event somewhere.
  2. Detect a keyboard event somewhere.
  3. Ensure the the form doesn’t get filled up super duper quickly (‘cuz people don’t work that fast).

Now, the simplest way of using these (I always advocate for the simplest way I know), is to use the Form component I’ve created in Splendid Labz:

<script>
  import { Form, Honeypot } from '@splendidlabz/svelte'
</script>

<Form>
  <Honeypot name="honeypot" />
</Form>

If you use Astro, you need to enable JavaScript with a client directive:

---
import { Form, Honeypot } from '@splendidlabz/svelte'
---

<Form client:idle>
  <Honeypot name="honeypot" />
</Form>

If you use vanilla JavaScript or other frameworks, you can use the preventSpam utility that does the triple checks for you:

import { preventSpam } from '@splendidlabz/utils/dom'

let form = document.querySelector('form')
form = preventSpam(form, { honeypotField: 'honeypot' })

form.addEventListener('submit', event => {
  event.preventDefault()
  if (form.containsSpam) return
  else form.submit()
})

And, if you don’t wanna use any of the above, the idea is to use JavaScript to detect if the user performed any sort of interaction on the page:

export function preventSpam(
  form,
  { honeypotField = 'honeypot', honeypotDuration = 2000 } = {}
) {
  const startTime = Date.now()
  let hasInteraction = false

  // Check for user interaction
  function checkForInteraction() {
    hasInteraction = true
  }

  // Listen for a couple of events to check interaction
  const events = ['keydown', 'mousemove', 'touchstart', 'click']
  events.forEach(event => {
    form.addEventListener(event, checkForInteraction, { once: true })
  })

  // Check for spam via all the available methods
  form.containsSpam = function () {
    const fillTime = Date.now() - startTime
    const isTooFast = fillTime < honeypotDuration
    const honeypotInput = form.querySelector(`[name="${honeypotField}"]`)
    const hasHoneypotValue = honeypotInput?.value?.trim()
    const noInteraction = !hasInteraction

    // Clean up event listeners after use
    events.forEach(event =>
      form.removeEventListener(event, checkForInteraction)
    )

    return isTooFast || !!hasHoneypotValue || noInteraction
  }
}

Better Forms

I’m putting together a solution that will make HTML form elements much easier to use. It includes the standard elements you know, but with easy-to-use syntax and are highly accessible.

Stuff like:

  • Form
  • Honeypot
  • Text input
  • Textarea
  • Radios
  • Checkboxes
  • Switches
  • Button groups
  • etc.

Here’s a landing page if you’re interested in this. I’d be happy to share something with you as soon as I can.

Wrapping Up

There are a couple of tricks that make honeypots work today. The best way, likely, is to trick spam bots into thinking your honeypot is a real field. If you don’t want to trick bots, you can use other bot-detection mechanisms that we’ve defined above.

Hope you have learned a lot and manage to get something useful from this!


Building a Honeypot Field That Works originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



from CSS-Tricks https://css-tricks.com/building-a-honeypot-field-that-works/
via IFTTT

Sunday, October 19, 2025

Clear today!



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

Current wind speeds: 14 from the Southwest

Pollen: 0

Sunrise: October 19, 2025 at 07:05PM

Sunset: October 20, 2025 at 06:06AM

UV index: 0

Humidity: 36%

via https://ift.tt/gdslH4y

October 20, 2025 at 10:02AM

Sequential linear() Animation With N Elements

Let’s suppose you have N elements with the same animation that should animate sequentially. The first one, then the second one, and so on until we reach the last one, then we loop back to the beginning. I am sure you know what I am talking about, and you also know that it’s tricky to get such an effect. You need to define complex keyframes, calculate delays, make it work for a specific number of items, etc.

Tell you what: with modern CSS, we can easily achieve this using a few lines of code, and it works for any number of items!

The following demo is currently limited to Chrome and Edge, but will work in other browsers as the sibling-index() and sibling-count() functions gain broader support. You can track Firefox support in Ticket #1953973 and WebKit’s position in Issue #471.

In the above demo, the elements are animated sequentially and the keyframes are as simple as a single to frame changing an element’s background color and scale:

@keyframes x {
  to {
    background: #F8CA00;
    scale: .8;
  }
}

You can add or remove as many items as you want and everything will keep running smoothly. Cool, right? That effect is made possible with this strange and complex-looking code:

.container > * {
  --_s: calc(100%*(sibling-index() - 1)/sibling-count());
  --_e: calc(100%*(sibling-index())/sibling-count());

  animation: 
    x calc(var(--d)*sibling-count()) infinite 
    linear(0, 0 var(--_s), 1, 0 var(--_e), 0);
}

It’s a bit scary and unreadable, but I will dissect it with you to understand the logic behind it.

The CSS linear() function

When working with animations, we can define timing functions (also called easing functions). We can use predefined keyword values — such as linear, ease, ease-in, etc. — or steps() to define discrete animations. There’s also cubic-bezier().

But we have a newer, more powerful function we can add to that list: linear().

From the specification:

A linear easing function is an easing function that interpolates linearly between its control points. Each control point is a pair of numbers, associating an input progress value to an output progress value.

animation-timing-function: linear creates a linear interpolation between two points — the start and end of the animation — while the linear() function allows us to define as many points as we want and have a “linear” interpolation between two consecutive points.

It’s a bit confusing at first glance, but once we start working with it, things becomes clearer. Let’s start with the first value, which is nothing but an equivalent of the linear value.

linear(0 0%, 1 100%)

We have two points, and each point is defined with two values (the “output” progress and “input” progress). The “output” progress is the animation (i.e., what is defined within the keyframes) and the “input” progress is the time.

Let’s consider the following code:

.box {
  animation: move 2s linear(0 0%, 1 100%);
}

@keyframes move {
  0%   {translate: 0px }
  100% {translate: 80px}
}

In this case, we want 0 of the animation (translate: 0px) at t=0% (in other words, 0% of 2s, so 0s) and 1 of the animation (translate: 80px) at t=100% (which is 100% of 2s, so 2s). Between these points, we do a linear interpolation.

Instead of percentages, we can use numbers, which means that the following is also valid:

linear(0 0, 1 1)

But I recommend you stick to the percentage notation to avoid getting confused with the first value which is a number as well. The 0% and 100% are implicit, so we can remove them and simply use the following:

linear(0, 1)

Let’s add a third point:

linear(0, 1, 0)

As you can see, I am not defining any “input” progress (the percentage values that represent the time) because they are not mandatory; however, introducing them is the first thing to do to understand what the function is doing.

The first value is always at 0% and the last value is always at 100%.

linear(0 0%, 1, 0 100%)

The value will be 50% for the middle point. When a control point is missing its “input” progress, we take the mid-value between two adjacent points. If you are familiar with gradients, you will notice the same logic applies to color stops.

linear(0 0%, 1 50%, 0 100%)

Easier to read, right? Can you explain what it does? Take a few minutes to think about it before continuing.

Got it? I am sure you did!

It breaks down like this:

  1. We start with translate: 0px at t=0s (0% of 2s).
  2. Then we move to translate: 80px at t=1s (50% of 2s).
  3. Then we get back to translate: 0px at t=2s (100% of 2s).

Most of the timing functions allow us to only move forward, but with linear() we can move in both directions as many times as we want. That’s what makes this function so powerful. With a “simple” keyframes you can have a “complex” animation.

I could have used the following keyframes to do the same thing:

@keyframes move {
  0%, 100% { translate: 0px }
  50% { translate: 80px }
}

However, I won’t be able to update the percentage values on the fly if I want a different animation. There is no way to control keyframes using CSS so I need to define new keyframes each time I need a new animation. But with linear(), I only need one keyframes.

In the demo below, all the elements are using the same keyframes and yet have completely different animations!

Add a delay with linear()

Now that we know more about linear(), let’s move to the main trick of our effect. Don’t forget that the idea is to create a sequential animation with a certain number (N) of elements. Each element needs to animate, then “wait” until all the others are done with their animation to start again. That waiting time can be seen as a delay.

The intuitive way to do this is the following:

@keyframes move {
  0%, 50% { translate: 0px }
  100% { translate: 80px }
}

We specify the same value at 0% and 50%; hence nothing will happen between 0% and 50%. We have our delay, but as I said previously, we won’t be able to control those percentages using CSS. Instead, we can express the same thing using linear():

linear(0 0%, 0 50%, 1 100%)

The first two control points have the same “output” progress. The first one is at 0% of the time, and the second one at 50% of the time, so nothing will “visually” happen in the first half of the animation. We created a delay without having to update the keyframes!

@keyframes move {
  0% { translate: 0px }
  100% { translate: 80px }
}

Let’s add another point to get back to the initial state:

linear(0 0%, 0 50%, 1 75%, 0 100%)

Or simply:

linear(0, 0 50%, 1, 0)

Cool, right? We’re able to create a complex animation with a simple set of keyframes. Not only that, but we can easily adjust the configuration by tweaking the linear() function. This is what we will do for each element to get our sequential animation!

The full animation

Let’s get back to our first animation and use the previous linear() value we did before. We will start with two elements.

Nothing surprising yet. Both elements have the exact same animation, so they animate the same way at the same time. Now, let’s update the linear() function for the first element to have the opposite effect: an animation in the first half, then a delay in the second half.

linear(0, 1, 0 50%, 0)

This literally inverts the previous value:

Tada! We have established a sequential animation with two elements! Are you starting to see the idea? The goal is to do the same with any number (N) of elements. Of course, we are not going to assign a different linear() value for each element — we will do it programmatically.

First, let’s draw a figure to understand what we did for two elements.

Two square graphs fside by side showing the lines of the first two items. It's the same upward pointing spike, only shifting along the x-axis as you compare the graphs.

When one element is waiting, the other one is animating. We can identify two ranges. Let’s imagine the same with three elements.

Three square graphs from right to left showing the lines of the first three items. It's the same upward pointing spike, only shifting along the x-axis as you compare the graphs.

This time, we need three ranges. Each element animates in one range and waits in two ranges. Do you see the pattern? For N elements, we need N ranges, and the linear() function will have the following syntax:

linear(0, 0 S, 1, 0 E, 0)

The start and the end are equal to 0, which is the initial state of the animation, then we have an animation between S and E. An element will wait from 0% to S, animate from S to E, then wait again from E to 100%. The animation time equals to 100%/N, which means E - S = 100%/N.

The first element starts its animation at the first range (0 * 100%/N), the second element at the second range (1 * 100%/N), the third element at the third range (2 * 100%/N), and so on. S is equal to:

S = (i - 1) * 100%/N

…where i is the index of the element.

Now, you may ask, how do we get the value of N and i? The answer is as simple as using the sibling-count()and sibling-index() functions! Again, these are currently supported in Chromium browsers, but we can expect them to roll out in other browsers down the road.

S = calc(100%*(sibling-index() - 1)/sibling-count())

And:

E = S + 100%/N
E = calc(100%*sibling-index()/sibling-count())

We write all this with some good CSS and we are done!

.box {
  --d: .5s; /* animation duration */
  --_s: calc(100%*(sibling-index() - 1)/sibling-count());
  --_e: calc(100%*(sibling-index())/sibling-count());

  animation: x calc(var(--d)*sibling-count()) infinite linear(0, 0 var(--_s), 1, 0 var(--_e), 0);
}
@keyframes x {
  to {
    background: #F8CA00;
    scale: .8;
  }
}

I used a variable (--d) to control the duration, but it’s not mandatory. I wanted to be able to control the amount of time each element takes to animate. That’s why I multiply it later by N.

Now all that’s left is to define your animation. Add as many elements as you want, and watch the result. No more complex keyframes and magic values!

Note: For unknown reasons (probably a bug) you need to register the variables with @property.

More variations

We can extend the basic idea to create more variations. For example, instead of having to wait for an element to completely end its animation, the next one can already start its own.

This time, I am defining N + 1 ranges, and each element animates in two ranges. The first element will animate in the first and second range, while the second element will animate in the second and third range; hence an overlap of both animations in the second range, etc.

I will not spend too much time explaining this case because it’s one example among many we create, so I let you dissect the code as a small exercise. And here is another one for you to study as well.

Conclusion

The linear() function was mainly introduced to create complex easing such as bounce and elastic, but combined with other modern features, it unlocks a lot of possibilities. Through this article, we got a small overview of its potential. I said “small” because we can go further and create even more complex animations, so stay tuned for more articles to come!


Sequential linear() Animation With N Elements originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



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

Partly Cloudy today!

With a high of F and a low of 24F. Currently, it's 25F and Clear outside. Current wind speeds: 6 from the Southwest Pollen: 0 Su...