Archive

Author Archive

YouTube Unveils a New Red: A Bold and Subtle Brand Evolution

February 17th, 2025 No comments

YouTube has refreshed its iconic red with a new red-to-magenta gradient to celebrate its 20th anniversary, enhancing visual appeal and color consistency across devices. The subtle update improves accessibility, encourages user interaction, and reflects the platform’s ongoing evolution.

Categories: Designing, Others Tags:

The What If Machine: Bringing the “Iffy” Future of CSS into the Present

February 17th, 2025 No comments

Geoff’s post about the CSS Working Group’s decision to work on inline conditionals inspired some drama in the comments section. Some developers are excited, but it angers others, who fear it will make the future of CSS, well, if-fy. Is this a slippery slope into a hellscape overrun with rogue developers who abuse CSS by implementing excessive logic in what was meant to be a styling language? Nah. Even if some jerk did that, no mainstream blog would ever publish the ramblings of that hypothetical nutcase who goes around putting crazy logic into CSS for the sake of it. Therefore, we know the future of CSS is safe.

You say the whole world’s ending — honey, it already did

My thesis for today’s article offers further reassurance that inline conditionals are probably not the harbinger of the end of civilization: I reckon we can achieve the same functionality right now with style queries, which are gaining pretty good browser support.

If I’m right, Lea’s proposal is more like syntactic sugar which would sometimes be convenient and allow cleaner markup. It’s amusing that any panic-mongering about inline conditionals ruining CSS might be equivalent to catastrophizing adding a ternary operator for a language that already supports if statements.

Indeed, Lea says of her proposed syntax, “Just like ternaries in JS, it may also be more ergonomic for cases where only a small part of the value varies.” She also mentions that CSS has always been conditional. Not that conditionality was ever verboten in CSS, but CSS isn’t always very good at it.

Sold! I want a conditional oompa loompa now!

Me too. And many other people, as proven by Lea’s curated list of amazingly complex hacks that people have discovered for simulating inline conditionals with current CSS. Some of these hacks are complicated enough that I’m still unsure if I understand them, but they certainly have cool names. Lea concludes: “If you’re aware of any other techniques, let me know so I can add them.”

Hmm… surely I was missing something regarding the problems these hacks solve. I noted that Lea has a doctorate whereas I’m an idiot. So I scrolled back up and reread, but I couldn’t stop thinking: Are these people doing all this work to avoid putting an extra div around their widgets and using style queries?

It’s fair if people want to avoid superfluous elements in the DOM, but Lea’s list of hacks shows that the alternatives are super complex, so it’s worth a shot to see how far style queries with wrapper divs can take us.

Motivating examples

Lea’s motivating examples revolve around setting a “variant” property on a callout, noting we can almost achieve what she wants with style queries, but this hypothetical syntax is sadly invalid:

.callout { 
  @container (style(--variant: success)) {
    border-color: var(--color-success-30);
    background-color: var(--color-success-95);

    &::before {
      content: var(--icon-success);
      color: var(--color-success-05);
    }
  }
}

She wants to set styles on both the container itself and its descendants based on --variant. Now, in this specific example, I could get away with hacking the ::after pseudo-element with z-index to give the illusion that it’s the container. Then I could style the borders and background of that. Unfortunately, this solution is as fragile as my ego, and in this other motivating example, Lea wants to set flex-flow of the container based on the variant. In that situation, my pseudo-element solution is not good enough.

Remember, the acceptance of Lea’s proposal into the CSS spec came as her birthday gift from the universe, so it’s not fair to try to replace her gift with one of those cheap fake containers I bought on Temu. She deserves an authentic container.

Let’s try again.

Busting out the gangsta wrapper

One of the comments on Lea’s proposal mentions type grinding but calls it “a very (I repeat, very) convoluted but working” approach to solving the problem that inline conditionals are intended to solve. That’s not quite fair. Type grinding took me a bit to get my head around, but I think it is more approachable with fewer drawbacks than other hacks. Still, when you look at the samples, this kind of code in production would get annoying. Therefore, let’s bite the bullet and try to build an alternate version of Lea’s flexbox variant sample. My version doesn’t use type grinding or any hack, but “plain old” (not so old) style queries together with wrapper divs, to work around the problem that we can’t use style queries to style the container itself.

CodePen Embed Fallback

The wrapper battles type grinding

Comparing the code from Lea’s sample and my version can help us understand the differences in complexity.

Here are the two versions of the CSS:

And here are the two versions of the markup:

Markup Code Comparison

So, simpler CSS and slightly more markup. Maybe we are onto something.

What I like about style queries is that Lea’s proposal uses the style() function, so if and when her proposal makes it into browsers then migrating style queries to inline conditionals and removing the wrappers seems doable. This wouldn’t be a 2025 article if I didn’t mention that migrating this kind of code could be a viable use case for AI. And by the time we get inline conditionals, maybe AI won’t suck.

But we’re getting ahead of ourselves. Have you ever tried to adopt some whizz-bang JavaScript framework that looks elegant in the “to-do list” sample? If so, you will know that solutions that appear compelling in simplistic examples can challenge your will to live in a realistic example. So, let’s see how using style queries in the above manner works out in a more realistic example.

Seeking validation

Combine my above sample with this MDN example of HTML5 Validation and Seth Jeffery’s cool demo of morphing pure CSS icons, then feed it all into the “What If” Machine to get the demo below.

CodePen Embed Fallback

All the changes you see to the callout if you make the form valid are based on one custom property. This property is never directly used in CSS property values for the callout but controls the style queries that set the callout’s border color, icon, background color, and content. We set the --variant property at the .callout-wrapper level. I am setting it using CSS, like this:

@property --variant {
  syntax: "error | success";
  initial-value: error;
  inherits: true;
}

body:has(:invalid) .callout-wrapper {
  --variant: error;
}

body:not(:has(:invalid)) .callout-wrapper {
  --variant: success;
}

However, the variable could be set by JavaScript or an inline style in the HTML, like Lea’s samples. Form validation is just my way of making the demo more interactive to show that the callout can change dynamically based on --variant.

Wrapping up

It’s off-brand for me to write an article advocating against hacks that bend CSS to our will, and I’m all for “tricking” the language into doing what we want. But using wrappers with style queries might be the simplest thing that works till we get support for inline conditionals. If we want to feel more like we are living in the future, we could use the above approach as a basis for a polyfill for inline conditionals, or some preprocessor magic using something like a Parcel plugin or a PostCSS plugin — but my trigger finger will always itch for the Delete key on such compromises. Lea acknowledges, “If you can do something with style queries, by all means, use style queries — they are almost certainly a better solution.”

I have convinced myself with the experiments in this article that style queries remain a cromulent option even in Lea’s motivating examples — but I still look forward to inline conditionals. In the meantime, at least style queries are easy to understand compared to the other known workarounds. Ironically, I agree with the comments questioning the need for the inline conditionals feature, not because it will ruin CSS but because I believe we can already achieve Lea’s examples with current modern CSS and without hacks. So, we may not need inline conditionals, but they could allow us to write more readable, succinct code. Let me know in the comment section if you can think of examples where we would hit a brick wall of complexity using style queries instead of inline conditionals.


The What If Machine: Bringing the “Iffy” Future of CSS into the Present originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags:

The Dark Side of UX: Lessons from OnlyFans’ Addictive Design

February 17th, 2025 No comments

OnlyFans leverages a meticulously crafted UX to create a powerful sense of intimacy and exclusivity, captivating users while sparking ethical debates about manipulation and exploitation.

Categories: Designing, Others Tags:

Goodbye Bitly… New Preview Page is a Major Step Back

February 14th, 2025 No comments

Bitly’s new preview page, which adds an extra click and includes ads, undermines the simplicity and efficiency that made the service popular. Users now face the choice of dealing with interruptions or paying for a premium plan, making Bitly less appealing as a URL shortening tool.

Categories: Designing, Others Tags:

Mapping India: How Google Designed a User-Centric Navigation Experience

February 14th, 2025 No comments

Google Maps in India is a great example of designing with real people in mind, tackling everything from landmark-based directions to language barriers and patchy internet. By listening to users and testing in the chaos of real-life streets, Google created a navigation tool that millions now rely on every day.

Categories: Designing, Others Tags:

Scroll Driven Animations Notebook

February 13th, 2025 No comments

Adam’s such a mad scientist with CSS. He’s been putting together a series of “notebooks” that make it easy for him to demo code. He’s got one for gradient text, one for a comparison slider, another for accordions, and the list goes on.

One of his latest is a notebook of scroll-driven animations. They’re all impressive as heck, as you’d expect from Adam. But it’s the simplicity of the first few examples that I love most. Here I am recreating two of the effects in a CodePen, which you’ll want to view in the latest version of Chrome for support.

CodePen Embed Fallback

This is a perfect example of how a scroll-driven animation is simply a normal CSS animation, just tied to scrolling instead of the document’s default timeline, which starts on render. We’re talking about the same set of keyframes:

@keyframes slide-in-from-left {
  from {
    transform: translateX(-100%);
  }
}

All we have to do to trigger scrolling is call the animation and assign it to the timeline:

li {
  animation: var(--animation) linear both;
  animation-timeline: view();
}

Notice how there’s no duration set on the animation. There’s no need to since we’re dealing with a scroll-based timeline instead of the document’s timeline. We’re using the view() function instead of the scroll() function, which acts sort of like JavsScript’s Intersection Observer where scrolling is based on where the element comes into view and intersects the scrollable area.

It’s easy to drop your jaw and ooo and ahh all over Adam’s demos, especially as they get more advanced. But just remember that we’re still working with plain ol’ CSS animations. The difference is the timeline they’re on.


Scroll Driven Animations Notebook originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags:

Typecasting and Viewport Transitions in CSS With tan(atan2())

February 12th, 2025 No comments
Animation Zone between 400px and 1200px

We’ve been able to get the length of the viewport in CSS since… checks notes… 2013! Surprisingly, that was more than a decade ago. Getting the viewport width is as easy these days as easy as writing 100vw, but what does that translate to, say, in pixels? What about the other properties, like those that take a percentage, an angle, or an integer?

Think about changing an element’s opacity, rotating it, or setting an animation progress based on the screen size. We would first need the viewport as an integer — which isn’t currently possible in CSS, right?

What I am about to say isn’t a groundbreaking discovery, it was first described amazingly by Jane Ori in 2023. In short, we can use a weird hack (or feature) involving the tan() and atan2() trigonometric functions to typecast a length (such as the viewport) to an integer. This opens many new layout possibilities, but my first experience was while writing an Almanac entry in which I just wanted to make an image’s opacity responsive.

Resize the CodePen and the image will get more transparent as the screen size gets smaller, of course with some boundaries, so it doesn’t become invisible:

CodePen Embed Fallback

This is the simplest we can do, but there is a lot more. Take, for example, this demo I did trying to combine many viewport-related effects. Resize the demo and the page feels alive: objects move, the background changes and the text smoothly wraps in place.

CodePen Embed Fallback

I think it’s really cool, but I am no designer, so that’s the best my brain could come up with. Still, it may be too much for an introduction to this typecasting hack, so as a middle-ground, I’ll focus only on the title transition to showcase how all of it works:

CodePen Embed Fallback

Setting things up

The idea behind this is to convert 100vw to radians (a way to write angles) using atan2(), and then back to its original value using tan(), with the perk of coming out as an integer. It should be achieved like this:

:root {
  --int-width: tan(atan2(100vw, 1px));
}

But! Browsers aren’t too keep on this method, so a lot more wrapping is needed to make it work across all browsers. The following may seem like magic (or nonsense), so I recommend reading Jane’s post to better understand it, but this way it will work in all browsers:

@property --100vw {
  syntax: "<length>";
  initial-value: 0px;
  inherits: false;
}

:root {
  --100vw: 100vw;
  --int-width: calc(10000 * tan(atan2(var(--100vw), 10000px)));
}

Don’t worry too much about it. What’s important is our precious --int-width variable, which holds the viewport size as an integer!

CodePen Embed Fallback

Wideness: One number to rule them all

Right now we have the viewport as an integer, but that’s just the first step. That integer isn’t super useful by itself. We oughta convert it to something else next since:

  • different properties have different units, and
  • we want each property to go from a start value to an end value.

Think about an image’s opacity going from 0 to 1, an object rotating from 0deg to 360deg, or an element’s offset-distance going from 0% to 100%. We want to interpolate between these values as --int-width gets bigger, but right now it’s just an integer that usually ranges between 0 to 1600, which is inflexible and can’t be easily converted to any of the end values.

The best solution is to turn --int-width into a number that goes from 0 to 1. So, as the screen gets bigger, we can multiply it by the desired end value. Lacking a better name, I call this “0-to-1” value --wideness. If we have --wideness, all the last examples become possible:

/* If `--wideness is 0.5 */

.element {
  opacity: var(--wideness); /* is 0.5 */
  translate: rotate(calc(wideness(400px, 1200px) * 360deg)); /* is 180deg */
  offset-distance: calc(var(--wideness) * 100%); /* is 50% */
}

So --wideness is a value between 0 to 1 that represents how wide the screen is: 0 represents when the screen is narrow, and 1 represents when it’s wide. But we still have to set what those values mean in the viewport. For example, we may want 0 to be 400px and 1 to be 1200px, our viewport transitions will run between these values. Anything below and above is clamped to 0 and 1, respectively.

In CSS, we can write that as follows:

:root {
  /* Both bounds are unitless */
  --lower-bound: 400; 
  --upper-bound: 1200;

  --wideness: calc(
    (clamp(var(--lower-bound), var(--int-width), var(--upper-bound)) - var(--lower-bound)) / (var(--upper-bound) - var(--lower-bound))
  );
}

Besides easy conversions, the --wideness variable lets us define the lower and upper limits in which the transition should run. And what’s even better, we can set the transition zone at a middle spot so that the user can see it in its full glory. Otherwise, the screen would need to be 0px so that --wideness reaches 0 and who knows how wide to reach 1.

CodePen Embed Fallback

We got the --wideness. What’s next?

For starters, the title’s markup is divided into spans since there is no CSS-way to select specific words in a sentence:

<h1><span>Resize</span> and <span>enjoy!</span></h1>

And since we will be doing the line wrapping ourselves, it’s important to unset some defaults:

h1 {
  position: absolute; /* Keeps the text at the center */
  white-space: nowrap; /* Disables line wrapping */
}

The transition should work without the base styling, but it’s just too plain-looking. They are below if you want to copy them onto your stylesheet:

CodePen Embed Fallback

And just as a recap, our current hack looks like this:

@property --100vw {
  syntax: "<length>";
  initial-value: 0px;
  inherits: false;
}

:root {
  --100vw: 100vw;
  --int-width: calc(10000 * tan(atan2(var(--100vw), 10000px)));
  --lower-bound: 400;
  --upper-bound: 1200;

  --wideness: calc(
    (clamp(var(--lower-bound), var(--int-width), var(--upper-bound)) - var(--lower-bound)) / (var(--upper-bound) - var(--lower-bound))
  );
}

OK, enough with the set-up. It’s time to use our new values and make the viewport transition. We first gotta identify how the title should be rearranged for smaller screens: as you saw in the initial demo, the first span goes up and right, while the second span does the opposite and goes down and left. So, the end position for both spans translates to the following values:

h1 {
  span:nth-child(1) {
    display: inline-block; /* So transformations work */
    position: relative;
    bottom: 1.2lh;
    left: 50%;
    transform: translate(-50%);
  }

  span:nth-child(2) {
    display: inline-block; /* So transformations work */
    position: relative;
    bottom: -1.2lh;
    left: -50%;
    transform: translate(50%);
  }
}

Before going forward, both formulas are basically the same, but with different signs. We can rewrite them at once bringing one new variable: --direction. It will be either 1 or -1 and define which direction to run the transition:

h1 {
  span {
    display: inline-block;
    position: relative;
    bottom: calc(1.2lh * var(--direction));
    left: calc(50% * var(--direction));
    transform: translate(calc(-50% * var(--direction)));
    }

  span:nth-child(1) {
    --direction: 1;
  }

  span:nth-child(2) {
    --direction: -1;
  }
}
CodePen Embed Fallback

The next step would be bringing --wideness into the formula so that the values change as the screen resizes. However, we can’t just multiply everything by --wideness. Why? Let’s see what happens if we do:

span {
  display: inline-block;
  position: relative;
  bottom: calc(var(--wideness) * 1.2lh * var(--direction));
  left: calc(var(--wideness) * 50% * var(--direction));
  transform: translate(calc(var(--wideness) * -50% * var(--direction)));
}

As you’ll see, everything is backwards! The words wrap when the screen is too wide, and unwrap when the screen is too narrow:

CodePen Embed Fallback

Unlike our first examples, in which the transition ends as --wideness increases from 0 to 1, we want to complete the transition as --wideness decreases from 1 to 0, i.e. while the screen gets smaller the properties need to reach their end value. This isn’t a big deal, as we can rewrite our formula as a subtraction, in which the subtracting number gets bigger as --wideness increases:

span {
  display: inline-block;
  position: relative;
  bottom: calc((1.2lh - var(--wideness) * 1.2lh) * var(--direction));
  left: calc((50% - var(--wideness) * 50%) * var(--direction));
  transform: translate(calc((-50% - var(--wideness) * -50%) * var(--direction)));
}

And now everything moves in the right direction while resizing the screen!

CodePen Embed Fallback

However, you will notice how words move in a straight line and some words overlap while resizing. We can’t allow this since a user with a specific screen size may get stuck at that point in the transition. Viewport transitions are cool, but not at the expense of ruining the experience for certain screen sizes.

Instead of moving in a straight line, words should move in a curve such that they pass around the central word. Don’t worry, making a curve here is easier than it looks: just move the spans twice as fast in the x-axis as they do in the y-axis. This can be achieved by multiplying --wideness by 2, although we have to cap it at 1 so it doesn’t overshoot past the final value.

span {
 display: inline-block;
 position: relative;
 bottom: calc((1.2lh - var(--wideness) * 1.2lh) * var(--direction));
 left: calc((50% - min(var(--wideness) * 2, 1) * 50%) * var(--direction));
 transform: translate(calc((-50% - min(var(--wideness) * 2, 1) * -50%) * var(--direction)));
}

Look at that beautiful curve, just avoiding the central text:

CodePen Embed Fallback

This is just the beginning!

It’s surprising how powerful having the viewport as an integer can be, and what’s even crazier, the last example is one of the most basic transitions you could make with this typecasting hack. Once you do the initial setup, I can imagine a lot more possible transitions, and --widenesss is so useful, it’s like having a new CSS feature right now.

I expect to see more about “Viewport Transitions” in the future because they do make websites feel more “alive” than adaptive.


Typecasting and Viewport Transitions in CSS With tan(atan2()) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags:

Amazon App Ditches the Logo (For Now)—Is This the Future of App Design?

February 11th, 2025 No comments

Some Amazon app users are noticing a new design, with the navigation bar and the company logo now missing. Instead, a large search bar greet users, raising questions about whether this change is part of a broader redesign or a test for a future logo-less interface.

Categories: Designing, Others Tags:

Organizing Design System Component Patterns With CSS Cascade Layers

February 10th, 2025 No comments

I’m trying to come up with ways to make components more customizable, more efficient, and easier to use and understand, and I want to describe a pattern I’ve been leaning into using CSS Cascade Layers.

I enjoy organizing code and find cascade layers a fantastic way to organize code explicitly as the cascade looks at it. The neat part is, that as much as it helps with “top-level” organization, cascade layers can be nested, which allows us to author more precise styles based on the cascade.

The only downside here is your imagination, nothing stops us from over-engineering CSS. And to be clear, you may very well consider what I’m about to show you as a form of over-engineering. I think I’ve found a balance though, keeping things simple yet organized, and I’d like to share my findings.

The anatomy of a CSS component pattern

Let’s explore a pattern for writing components in CSS using a button as an example. Buttons are one of the more popular components found in just about every component library. There’s good reason for that popularity because buttons can be used for a variety of use cases, including:

  • performing actions, like opening a drawer,
  • navigating to different sections of the UI, and
  • holding some form of state, such as focus or hover.

And buttons come in several different flavors of markup, like

Categories: Designing, Others Tags:

Make Any File a Template Using This Hidden macOS Tool

February 10th, 2025 No comments
macOS contextual window for a CSS file with the "Stationary pad" checkbox option highlighted.

From MacRumors:

Stationery Pad is a handy way to nix a step in your workflow if you regularly use document templates on your Mac. The long-standing Finder feature essentially tells a file’s parent application to open a copy of it by default, ensuring that the original file remains unedited.

This works for any kind of file, including HTML, CSS, JavaScriprt, or what have you. You can get there with CMD+i or right-click and select “Get info.”


Make Any File a Template Using This Hidden macOS Tool originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

Categories: Designing, Others Tags: