> All in One 586: June 2025

Ads

Friday, June 6, 2025

Partly Cloudy today!



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

Current wind speeds: 7 from the Northwest

Pollen: 0

Sunrise: June 6, 2025 at 05:25PM

Sunset: June 7, 2025 at 08:15AM

UV index: 0

Humidity: 100%

via https://ift.tt/LHXR4U1

June 7, 2025 at 10:02AM

Better CSS Shapes Using shape() — Part 3: Curves

If you’re following along, this is the third post in a series about the new CSS shape() function. We’ve learned how to draw lines and arcs and, in this third part, I will introduce the curve command — the missing command you need to know to have full control over the shape() function. In reality, there are more commands, but you will rarely need them and you can easily learn about them later by checking the documentation.

Better CSS Shapes Using shape()

  1. Lines and Arcs
  2. More on Arcs
  3. Curves (you are here!)

The curve command

This command adds a Bézier curve between two points by specifying control points. We can either have one control point and create a Quadratic curve or two control points and create a Cubic curve.

Bézier, Quadratic, Cubic, control points? What?!

For many of you, that definition is simply unclear, or even useless! You can spend a few minutes reading about Bézier curves but is it really worth it? Probably not, unless your job is to create shapes all the day and you have a solid background in geometry.

We already have cubic-bezier() as an easing function for animations but, honestly, who really understands how it works? We either rely on a generator to get the code or we read a “boring” explanation that we forget in two minutes. (I have one right here by the way!)

Don’t worry, this article will not be boring as I will mostly focus on practical examples and more precisely the use case of rounding the corners of irregular shapes. Here is a figure to illustrate a few examples of Bézier curves.

Comparing two curved lines, one with one control point and one with two control points.

The blue dots are the starting and ending points (let’s call them A and B) and the black dots are the control points. And notice how the curve is tangent to the dashed lines illustrated in red.

In this article, I will consider only one control point. The syntax will follow this pattern:

clip-path: shape(
  from Xa Ya, 
  curve to Xb Yb with Xc Yc
);

arc command vs. curve command

We already saw in Part 1 and Part 2 that the arc command is useful establishing rounded edges and corners, but it will not cover all the cases. That’s why you will need the curve command. The tricky part is to know when to use each one and the answer is “it depends.” There is no generic rule but my advice is to first see if it’s possible (and easy) using arc. If not, then you have to use curve.

For some shapes, we can have the same result using both commands and this is a good starting point for us to understand the curve command and compare it with arc.

Take the following example:

This is the code for the first shape:

.shape {
  clip-path: shape(from 0 0,
    arc to 100% 100% of 100% cw,
    line to 0 100%)
}

And for the second one, we have this:

.shape {
  clip-path: shape(from 0 0,
    curve to 100% 100% with 100% 0,
    line to 0 100%)
}

The arc command needs a radius (100% in this case), but the curve command needs a control point (which is 100% 0 in this example).

Two rounded shapes that appear to have similar curves, one using an arc and another using a curve.

Now, if you look closely, you will notice that both results aren’t exactly the same. The first shape using the arc command is creating a quarter of a circle, whereas the shape using the curve command is slightly different. If you place both of them above each other, you can clearly see the difference.

This is interesting because it means we can round some corners using either an arc or a curve, but with slightly different results. Which one is better, you ask? I would say it depends on your visual preference and the shape you are creating.

In Part 1, we created rounded tabs using the arc command, but we can also create them with curve.

Can you spot the difference? It’s barely visible but it’s there.

Notice how I am using the by directive the same way I am doing with arc, but this time we have the control point, which is also relative. This part can be confusing, so pay close attention to this next bit.

Consider the following:

shape(from Xa Ya, curve by Xb Yb with Xc Yc)

It means that both (Xb,Yb) and (Xc,Yc) are relative coordinates calculated from the coordinate of the starting point. The equivalent of the above using a to directive is this:

shape(from Xa Ya, curve to (Xa + Xb) (Ya + Yb) with (Xa + Xc) (Yb + Yc))

We can change the reference of the control point by adding a from directive. We can either use start (the default value), end, or origin.

shape(from Xa Ya, curve by Xb Yb with Xc Yc from end)

The above means that the control point will now consider the ending point instead of the starting point. The result is similar to:

shape(from Xa Ya, curve to (Xa + Xb) (Ya + Yb) with (Xa + Xb + Xc) (Ya + Yb + Yc))

If you use origin, the reference will be the origin, hence the coordinate of the control point becomes absolute instead of relative.

The from directive may add some complexity to the code and the calculation, so don’t bother yourself with it. Simply know it exists in case you face it, but keep using the default value.

I think it’s time for your first homework! Similar to the rounded tab exercise, try to create the inverted radius shape we covered in the Part 1 using curve instead of arc. Here are both versions for you to reference, but try to do it without peeking first, if you can.

Let’s draw more shapes!

Now that we have a good overview of the curve command, let’s consider more complex shapes where arc won’t help us round the corners and the only solution is to draw curves instead. Considering that each shape is unique, so I will focus on the technique rather than the code itself.

Slanted edge

Let’s start with a rectangular shape with a slanted edge.

A slanted rectangle shape in two stages, first with sharp edges, then with curved edges.

Getting the shape on the left is quite simple, but the shape on the right is a bit tricky. We can round two corners with a simple border-radius, but for the slanted edge, we will use shape() and two curve commands.

The first step is to write the code of the shape without rounded corners (the left one) which is pretty straightforward since we’re only working with the line command:

.shape {
  --s: 90px;  /* slant size */

  clip-path: 
    shape(from 0 0,
    line to calc(100% - var(--s)) 0,
    line to 100% 100%,
    line to 0 100%
    );
}

Then we take each corner and try to round it by modifying the code. Here is a figure to illustrate the technique I am going to use for each corner.

Diagrammiong a rounded rectangular shape in three stages, first with sharp edges, then with points indicating where the curve control points are, then the completed shape.

We define a distance, R, that controls the radius. From each side of the corner point, I move by that distance to create two new points, which are illustrated above in red. Then, I draw my curve using the new points as starting and ending points. The corner point will be the control point.

The code becomes:

.shape {
  --s: 90px;  /* slant size */

  clip-path: 
    shape(from 0 0,
    Line  to Xa Ya,
    curve to Xb Yb with calc(100% - var(--s)) 0,
    line to 100% 100%,
    line to 0 100%
    );
}

Notice how the curve is using the coordinates of the corner point in the with directive, and we have two new points, A and B.

Until now, the technique is not that complex. For each corner point, you replace the line command with line + curve commands where the curve command reuses the old point in its with directive.

If we apply the same logic to the other corner, we get the following:

.shape {
  --s: 90px;  /* slant size */

  clip-path: 
    shape(from 0 0,
    line  to Xa Ya, 
    curve to Xb Yb with calc(100% - var(--s)) 0,
    line  to Xc Yc,
    curve to Xd Yd with 100% 100%,
    line to 0 100%
    );
}

Now we need to calculate the coordinates of the new points. And here comes the tricky part because it’s not always simple and it may require some complex calculation. Even if I detail this case, the logic won’t be the same for the other shapes we’re making, so I will skip the math part and give you the final code:

.box {
  --h: 200px; /* element height */
  --s: 90px;  /* slant size */
  --r: 20px;  /* radius */
  
  height: var(--h);
  border-radius: var(--r) 0 0 var(--r);
  --_a: atan2(var(--s), var(--h));
  clip-path: 
    shape(from 0 0,
    line  to calc(100% - var(--s) - var(--r)) 0,
    curve by calc(var(--r) * (1 + sin(var(--_a)))) 
              calc(var(--r) * cos(var(--_a)))
    with var(--r) 0,
    line  to calc(100% - var(--r) * sin(var(--_a))) 
              calc(100% - var(--r) * cos(var(--_a))),
    curve to calc(100% - var(--r)) 100%  with 100% 100%,
    line to 0 100%
    );
}

I know the code looks a bit scary, but the good news is that the code is also really easy to control using CSS variables. So, even if the math is not easy to grasp, you don’t have to deal with it. It should be noted that I need to know the height to be able to calculate the coordinates which means the solution isn’t perfect because the height is a fixed value.

Arrow-shaped box

Here’s a similar shape, but this time we have three corners to round using the curve command.

The final code is still complex but I followed the same steps. I started with this:

.shape {
  --s: 90px; 

  clip-path: 
    shape(from 0 0,
    /* corner #1 */
    line to calc(100% - var(--s)) 0,
    /* corner #2 */
    line to 100% 50%,
    /* corner #3 */
    line to calc(100% - var(--s)) 100%,

    line to 0 100%
    );
}

Then, I modified it into this:

.shape {
  --s: 90px; 

  clip-path: 
    shape(from 0 0,
    /* corner #1 */
    line  to Xa Ya
    curve to Xb Yb with calc(100% - var(--s)) 0,
    /* corner #2 */
    line  to Xa Ya
    curve to Xb Yb with 100% 50%,
    /* corner #3 */
    line  to Xa Yb
    curve to Xb Yb with calc(100% - var(--s)) 100%,

    line to 0 100%
    );
}

Lastly, I use a pen and paper to do all the calculations.

You might think this technique is useless if you are not good with math and geometry, right? Not really, because you can still grab the code and use it easily since it’s optimized using CSS variables. Plus, you aren’t obligated to be super accurate and precise. You can rely on the above technique and use trial and error to approximate the coordinates. It will probably take you less time than doing all the math.

Rounded polygons

I know you are waiting for this, right? Thanks to the new shape() and the curve command, we can now have rounded polygon shapes!

Three rounded polygon shapes, first a pentagon, second a triangle, and third an octagon.

Here is my implementation using Sass where you can control the radius, number of sides and the rotation of the shape:

If we omit the complex geometry part, the loop is quite simple as it relies on the same technique with a line + curve per corner.

$n: 9; /* number of sides*/
$r: .2; /* control the radius [0 1] */
$a: 15deg; /* control the rotation */

.poly {
  aspect-ratio: 1;
  $m: ();
  @for $i from 0 through ($n - 1) {
    $m: append($m, line  to Xai Yai, comma);
    $m: append($m, curve to Xbi Ybi with Xci Yci, comma);
  } 
  clip-path: shape(#{$m});
}

Here is another implementation where I define the variables in CSS instead of Sass:

Having the variables in CSS is pretty handy especially if you want to have some animations. Here is an example of a cool hover effect applied to hexagon shapes:

I have also updated my online generator to add the radius parameter. If you are not familiar with Sass, you can easily copy the CSS code from there. You will also find the border-only and cut-out versions!

A rounded shape with six sides next to a cutout of a rounded shape with six edges.

Conclusion

Are we done with the curve command? Probably not, but we have a good overview of its potential and all the complex shapes we can build with it. As for the code, I know that we have reached a level that is not easy for everyone. I could have extended the explanation by explicitly breaking down the math, but then this article would be overly complex and make it seem like using shape() is harder than it is.

This said, most of the shapes I code are available within my online collection that I constantly update and optimize so you can easily grab the code of any shape!

If you want a good follow-up to this article, I wrote an article for Frontend Masters where you can create blob shapes using the curve command.

Three blob shapes in a single row, each colored with a gradient that goes left to right from dark orange to light orange.

Better CSS Shapes Using shape()

  1. Lines and Arcs
  2. More on Arcs
  3. Curves (you are here!)

Better CSS Shapes Using shape() — Part 3: Curves originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



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

Thursday, June 5, 2025

Cloudy today!



With a high of F and a low of 49F. Currently, it's 56F and Cloudy outside.

Current wind speeds: 9 from the Northeast

Pollen: 0

Sunrise: June 5, 2025 at 05:25PM

Sunset: June 6, 2025 at 08:14AM

UV index: 0

Humidity: 89%

via https://ift.tt/jAuBFS5

June 6, 2025 at 10:02AM

The State of CSS 2025 Survey is out!

The State of CSS 2025 Survey dropped a few days ago, and besides waiting for the results, it’s exciting to see a lot of the new things shipped to CSS over the past year reflected in the questions. To be specific, the next survey covers the following features:

Again, a lot!

However, I think the most important questions (regarding CSS) are asked at the end of each section. I am talking about the “What are your top CSS pain points related to ______?” questions. These sections are optional, but help user agents and the CSS Working Group know what they should focus on next.

By nature of comments, those respondents with strong opinions are most likely to fill them in, skewing data towards issues that maybe the majority doesn’t have. So, even if you don’t have a hard-set view on a CSS pain point, I encourage you to fill them — even with your mild annoyances.


The State of CSS 2025 Survey is out! originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



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

Exploring the CSS contrast-color() Function… a Second Time

In many countries, web accessibility is a human right and the law, and there can be heavy fines for non-compliance. Naturally, this means that text and icons and such must have optimal color contrast in accordance with the benchmarks set by the Web Content Accessibility Guidelines (WCAG). Now, there are quite a few color contrast checkers out there (Figma even has one built-in now), but the upcoming contrast-color() function doesn’t check color contrast, it outright resolves to either black or white (whichever one contrasts the most with your chosen color).

Right off the bat, you should know that we’ve sorta looked at this feature before. Back then, however, it was called color-contrast() instead of contrast-color() and had a much more convoluted way of going about things. It was only released in Safari Technology Preview 122 back in 2021, and that’s still the case at the time I’m writing this (now at version 220).

You’d use it like this:

button {
  --background-color: darkblue;
  background-color: var(--background-color);
  color: contrast-color(var(--background-color));
}

Here, contrast-color() has determined that white contrasts with darkblue better than black does, which is why contrast-color() resolves to white. Pretty simple, really, but there are a few shortcomings, which includes a lack of browser support (again, it’s only in Safari Technology Preview at the moment).

We can use contrast-color() conditionally, though:

@supports (color: contrast-color(red)) {
  /* contrast-color() supported */
}

@supports not (color: contrast-color(red)) {
  /* contrast-color() not supported */
}

The shortcomings of contrast-color()

First, let me just say that improvements are already being considered, so here I’ll explain the shortcomings as well as any improvements that I’ve heard about.

Undoubtedly, the number one shortcoming is that contrast-color() only resolves to either black or white. If you don’t want black or white, well… that sucks. However, the draft spec itself alludes to more control over the resolved color in the future.

But there’s one other thing that’s surprisingly easy to overlook. What happens when neither black nor white is actually accessible against the chosen color? That’s right, it’s possible for contrast-color() to just… not provide a contrasting color. Ideally, I think we’d want contrast-color() to resolve to the closest accessible variant of a preferred color. Until then, contrast-color() isn’t really usable.

Another shortcoming of contrast-color() is that it only accepts arguments of the <color> data type, so it’s just not going to work with images or anything like that. I did, however, manage to make it “work” with a gradient (basically, two instances of contrast-color() for two color stops/one linear gradient):

<button>
  <span>A button</span>
</button>
button {
  background: linear-gradient(to right, red, blue);

  span {
    background: linear-gradient(to right, contrast-color(red), contrast-color(blue));
    color: transparent;
    background-clip: text;
  }
}

The reason this looks so horrid is that, as mentioned before, contrast-color() only resolves to black or white, so in the middle of the gradient we essentially have 50% grey on purple. This problem would also get solved by contrast-color() resolving to a wider spectrum of colors.

But what about the font size? As you might know already, the criteria for color contrast depends on the font size, so how does that work? Well, at the moment it doesn’t, but I think it’s safe to assume that it’ll eventually take the font-size into account when determining the resolved color. Which brings us to APCA.

APCA (Accessible Perceptual Contrast Algorithm) is a new algorithm for measuring color contrast reliably. Andrew Somers, creator of APCA, conducted studies (alongside many other independent studies) and learned that 23% of WCAG 2 “Fails” are actually accessible. In addition, an insane 47% of “Passes” are inaccessible.

Not only should APCA do a better job, but the APCA Readability Criterion (ARC) is far more nuanced, taking into account a much wider spectrum of font sizes and weights (hooray for me, as I’m very partial to 600 as a standard font weight). While the criterion is expectedly complex and unnecessarily confusing, the APCA Contrast Calculator does a decent-enough job of explaining how it all works visually, for now.

contrast-color() doesn’t use APCA, but the draft spec does allude to offering more algorithms in the future. This wording is odd as it suggests that we’ll be able to choose between the APCA and WCAG algorithms. Then again, we have to remember that the laws of some countries will require WCAG 2 compliance while others require WCAG 3 compliance (when it becomes a standard).

That’s right, we’re a long way off of APCA becoming a part of WCAG 3, let alone contrast-color(). In fact, it might not even be a part of it initially (or at all), and there are many more hurdles after that, but hopefully this sheds some light on the whole thing. For now, contrast-color() is using WCAG 2 only.

Using contrast-color()

Here’s a simple example (the same one from earlier) of a darkblue-colored button with accessibly-colored text chosen by contrast-color(). I’ve put this darkblue color into a CSS variable so that we can define it once but reference it as many times as is necessary (which is just twice for now).

button {
  --background-color: darkblue;
  background-color: var(--background-color);
  /* Resolves to white */
  color: contrast-color(var(--background-color));
}

And the same thing but with lightblue:

button {
  --background-color: lightblue;
  background-color: var(--background-color);
  /* Resolves to black */
  color: contrast-color(var(--background-color));
}

First of all, we can absolutely switch this up and use contrast-color() on the background-color property instead (or in-place of any <color>, in fact, like on a border):

button {
  --color: darkblue;
  color: var(--color);
  /* Resolves to white */
  background-color: contrast-color(var(--color));
}

Any valid <color> will work (named, HEX, RGB, HSL, HWB, etc.):

button {
  /* HSL this time */
  --background-color: hsl(0 0% 0%);
  background-color: var(--background-color);
  /* Resolves to white */
  color: contrast-color(var(--background-color));
}

Need to change the base color on the fly (e.g., on hover)? Easy:

button {
  --background-color: hsl(0 0% 0%);
  background-color: var(--background-color);
  /* Starts off white, becomes black on hover */
  color: contrast-color(var(--background-color));

  &:hover {
    /* 50% lighter */
    --background-color: hsl(0 0% 50%);
  }
}

Similarly, we could use contrast-color() with the light-dark() function to ensure accessible color contrast across light and dark modes:

:root {
  /* Dark mode if checked */
  &:has(input[type="checkbox"]:checked) {
    color-scheme: dark;
  }

  /* Light mode if not checked */
  &:not(:has(input[type="checkbox"]:checked)) {
    color-scheme: light;
  }

  body {
    /* Different background for each mode */
    background: light-dark(hsl(0 0% 50%), hsl(0 0% 0%));
    /* Different contrasted color for each mode */
    color: light-dark(contrast-color(hsl(0 0% 50%)), contrast-color(hsl(0 0% 0%));
  }
}

The interesting thing about APCA is that it accounts for the discrepancies between light mode and dark mode contrast, whereas the current WCAG algorithm often evaluates dark mode contrast inaccurately. This one nuance of many is why we need not only a new color contrast algorithm but also the contrast-color() CSS function to handle all of these nuances (font size, font weight, etc.) for us.

This doesn’t mean that contrast-color() has to ensure accessibility at the expense of our “designed” colors, though. Instead, we can use contrast-color() within the prefers-contrast: more media query only:

button {
  --background-color: hsl(270 100% 50%);
  background-color: var(--background-color);
  /* Almost white (WCAG AA: Fail) */
  color: hsl(270 100% 90%);

  @media (prefers-contrast: more) {
    /* Resolves to white (WCAG AA: Pass) */
    color: contrast-color(var(--background-color));
  }
}

Personally, I’m not keen on prefers-contrast: more as a progressive enhancement. Great color contrast benefits everyone, and besides, we can’t be sure that those who need more contrast are actually set up for it. Perhaps they’re using a brand new computer, or they just don’t know how to customize accessibility settings.

Closing thoughts

So, contrast-color() obviously isn’t useful in its current form as it only resolves to black or white, which might not be accessible. However, if it were improved to resolve to a wider spectrum of colors, that’d be awesome. Even better, if it were to upgrade colors to a certain standard (e.g., WCAG AA) if they don’t already meet it, but let them be if they do. Sort of like a failsafe approach? This means that web browsers would have to take the font size, font weight, element, and so on into account.

To throw another option out there, there’s also the approach that Windows takes for its High Contrast Mode. This mode triggers web browsers to overwrite colors using the forced-colors: active media query, which we can also use to make further customizations. However, this effect is quite extreme (even though we can opt out of it using the forced-colors-adjust CSS property and use our own colors instead) and macOS’s version of the feature doesn’t extend to the web.

I think that forced colors is an incredible idea as long as users can set their contrast preferences when they set up their computer or browser (the browser would be more enforceable), and there are a wider range of contrast options. And then if you, as a designer or developer, don’t like the enforced colors, then you have the option to meet accessibility standards so that they don’t get enforced. In my opinion, this approach is the most user-friendly and the most developer-friendly (assuming that you care about accessibility). For complete flexibility, there could be a CSS property for opting out, or something. Just color contrast by default, but you can keep the colors you’ve chosen as long as they’re accessible.

What do you think? Is contrast-color() the right approach, or should the user agent bear some or all of the responsibility? Or perhaps you’re happy for color contrast to be considered manually?


Exploring the CSS contrast-color() Function… a Second Time originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.



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

Wednesday, June 4, 2025

Showers Late today!



With a high of F and a low of 50F. Currently, it's 54F and Cloudy outside.

Current wind speeds: 13 from the Southeast

Pollen: 0

Sunrise: June 4, 2025 at 05:26PM

Sunset: June 5, 2025 at 08:14AM

UV index: 0

Humidity: 79%

via https://ift.tt/lFnNC5m

June 5, 2025 at 10:02AM

Tuesday, June 3, 2025

Cloudy today!



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

Current wind speeds: 4 from the East

Pollen: 0

Sunrise: June 3, 2025 at 05:26PM

Sunset: June 4, 2025 at 08:13AM

UV index: 0

Humidity: 81%

via https://ift.tt/FlHsqy7

June 4, 2025 at 10:02AM

Getting Creative With HTML Dialog

Like ’em or loath ’em, whether you’re showing an alert, a message, or a newsletter signup, dialogue boxes draw attention to a particular piece of content without sending someone to a different page. In the past, dialogues relied on a mix of divisions, ARIA, and JavaScript. But the HTML dialog element has made them more accessible and style-able in countless ways.

So, how can you take dialogue box design beyond the generic look of frameworks and templates? How can you style them to reflect a brand’s visual identity and help to tell its stories? Here’s how I do it in CSS using ::backdrop, backdrop-filter, and animations.

Homepage design for Mike Worth website, showing a cartoon gorilla in a dark cave in an Indiana Jones style.
Design by Andy Clarke, Stuff & Nonsense. Mike Worth’s website will launch in June 2025, but you can see examples from this article on CodePen.

I mentioned before that Emmy-award-winning game composer Mike Worth hired me to create a highly graphical design. Mike loves ’90s animation, and he challenged me to find ways to incorporate its retro style without making a pastiche. However, I also needed to achieve that retro feel while maintaining accessibility, performance, responsiveness, and semantics.

A brief overview of dialog and ::backdrop

Let’s run through a quick refresher.

Note: While I mostly refer to “dialogue boxes” throughout, the HTML element is spelt dialog.

dialog is an HTML element designed for implementing modal and non-modal dialogue boxes in products and website interfaces. It comes with built-in functionality, including closing a box using the keyboard Esc key, focus trapping to keep it inside the box, show and hide methods, and a ::backdrop pseudo-element for styling a box’s overlay.

The HTML markup is just what you might expect:

<dialog>
  <h2>Keep me informed</h2>
  <!-- ... -->
  <button>Close</button>
</dialog>

This type of dialogue box is hidden by default, but adding the open attribute makes it visible when the page loads:

<dialog open>
  <h2>Keep me informed</h2>
  <!-- ... -->
  <button>Close</button>
</dialog>

I can’t imagine too many applications for non-modals which are open by default, so ordinarily I need a button which opens a dialogue box:

<dialog>
  <!-- ... -->
</dialog>
<button>Keep me informed</button>

Plus a little bit of JavaScript, which opens the modal:

const dialog = document.querySelector("dialog");
const showButton = document.querySelector("dialog + button");
showButton.addEventListener("click", () => {
  dialog.showModal();
});

Closing a dialogue box also requires JavaScript:

const closeButton = document.querySelector("dialog button");
closeButton.addEventListener("click", () => {
  dialog.close();
});

Unless the box contains a form using method="dialog", which allows it to close automatically on submit without JavaScript:

<dialog>
  <form method="dialog">
    <button>Submit</button>
  </form>
</dialog>

The dialog element was developed to be accessible out of the box. It traps focus, supports the Esc key, and behaves like a proper modal. But to help screen readers announce dialogue boxes properly, you’ll want to add an aria-labelledby attribute. This tells assistive technology where to find the dialogue box’s title so it can be read aloud when the modal opens.

<dialog aria-labelledby="dialog-title">
  <h2 id="dialog-title">Keep me informed</h2>
  <!-- ... -->
</dialog>

Most tutorials I’ve seen include very little styling for dialog and ::backdrop, which might explain why so many dialogue boxes have little more than border radii and a box-shadow applied.

Two examples of plain-looking dialogs with white backgrounds and box shadows.
Out-of-the-box dialogue designs

I believe that every element in a design — no matter how small or infrequently seen — is an opportunity to present a brand and tell a story about its products or services. I know there are moments during someone’s journey through a design where paying special attention to design can make their experience more memorable.

Dialogue boxes are just one of those moments, and Mike Worth’s design offers plenty of opportunities to reflect his brand or connect directly to someone’s place in Mike’s story. That might be by styling a newsletter sign-up dialogue to match the scrolls in his news section.

Dialog in the design is an open scroll with script lettering and an email signup form.
Mike Worth concept design, designed by Andy Clarke, Stuff & Nonsense.

Or making the form modal on his error pages look like a comic-book speech balloon.

Example of a dialog in the shape of a shat bubble with an email signup form inside.
Mike Worth concept design, designed by Andy Clarke, Stuff & Nonsense.

dialog in action

Mike’s drop-down navigation menu looks like an ancient stone tablet.

A menu of links set against a cartoon stone tablet illustration.
Mike Worth, designed by Andy Clarke, Stuff & Nonsense.

I wanted to extend this look to his dialogue boxes with a three-dimensional tablet and a jungle leaf-filled backdrop.

A dialog against a cartoon stone tablet illustration with an email signup for inside.
Mike Worth, designed by Andy Clarke, Stuff & Nonsense.

This dialog contains a newsletter sign-up form with an email input and a submit button:

<dialog>
  <h2>Keep me informed</h2>
  <form>
    <label for="email" data-visibility="hidden">Email address</label>
    <input type="email" id="email" required>
    <button>Submit</button>
  </form>
  <button>x</button>
</dialog>

I started by applying dimensions to the dialog and adding the SVG stone tablet background image:

dialog {
  width: 420px;
  height: 480px;
  background-color: transparent;
  background-image: url("dialog.svg");
  background-repeat: no-repeat;
  background-size: contain;
}

Then, I added the leafy green background image to the dialogue box’s generated backdrop using the ::backdrop pseudo element selector:

dialog::backdrop {
  background-image: url("backdrop.svg");
  background-size: cover;
}
Dialog sitting on top of a safari jungle pattern as the backdrop. The dialog is styled with a cartoon stone tablet illustration with an email signup form inside.
Mike Worth, designed by Andy Clarke, Stuff & Nonsense.

I needed to make it clear to anyone filling in Mike’s form that their email address is in a valid format. So I combined :has and :valid CSS pseudo-class selectors to change the color of the submit button from grey to green:

dialog:has(input:valid) button {
  background-color: #7e8943;
  color: #fff;
}

I also wanted this interaction to reflect Mike’s fun personality. So, I also changed the dialog background image and applied a rubberband animation to the box when someone inputs a valid email address:

dialog:has(input:valid) {
  background-image: url("dialog-valid.svg");
  animation: rubberBand 0.82s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

@keyframes rubberBand {
  from { transform: scale3d(1, 1, 1); }
  30% { transform: scale3d(1.25, 0.75, 1); }
  40% { transform: scale3d(0.75, 1.25, 1); }
  50% { transform: scale3d(1.15, 0.85, 1); }
  65% { transform: scale3d(0.95, 1.05, 1); }
  75% { transform: scale3d(1.05, 0.95, 1); }
  to { transform: scale3d(1, 1, 1); }
}

Tip: Daniel Eden’s Animate.css library is a fabulous source of “Just-add-water CSS animations” like the rubberband I used for this dialogue box.

Changing how an element looks when it contains a valid input is a fabulous way to add interactions that are, at the same time, fun and valuable for the user.

Dialog sitting on top of a safari jungle pattern as the backdrop. The dialog is styled with a cartoon stone tablet illustration with an email signup form inside.
Mike Worth, designed by Andy Clarke, Stuff & Nonsense.

That combination of :has and :valid selectors can even be extended to the ::backdrop pseudo-class, to change the backdrop’s background image:

dialog:has(input:valid)::backdrop {
  background-image: url("backdrop-valid.svg");
}

Try it for yourself:

Conclusion

We often think of dialogue boxes as functional elements, as necessary interruptions, but nothing more. But when you treat them as opportunities for expression, even the smallest parts of a design can help shape a product or website’s personality.

The HTML dialog element, with its built-in behaviours and styling potential, opens up opportunities for branding and creative storytelling. There’s no reason a dialogue box can’t be as distinctive as the rest of your design.


Andy Clarke

Often referred to as one of the pioneers of web design, Andy Clarke has been instrumental in pushing the boundaries of web design and is known for his creative and visually stunning designs. His work has inspired countless designers to explore the full potential of product and website design.

Andy’s written several industry-leading books, including ‘Transcending CSS,’ ‘Hardboiled Web Design,’ and ‘Art Direction for the Web.’ He’s also worked with businesses of all sizes and industries to achieve their goals through design.

Visit Andy’s studio, Stuff & Nonsense, and check out his Contract Killer, the popular web design contract template trusted by thousands of web designers and developers.


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



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

Monday, June 2, 2025

Thunderstorms/Wind today!



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

Current wind speeds: 15 from the Northeast

Pollen: 0

Sunrise: June 2, 2025 at 05:26PM

Sunset: June 3, 2025 at 08:13AM

UV index: 0

Humidity: 70%

via https://ift.tt/Wwm4LKC

June 3, 2025 at 10:02AM

Sunday, June 1, 2025

Partly Cloudy today!



With a high of F and a low of 56F. Currently, it's 70F and Light Rain with Thunder outside.

Current wind speeds: 11 from the South

Pollen: 0

Sunrise: June 1, 2025 at 05:27PM

Sunset: June 2, 2025 at 08:12AM

UV index: 0

Humidity: 54%

via https://ift.tt/AIb8JRX

June 2, 2025 at 10:02AM

Partly Cloudy today!

With a high of F and a low of 48F. Currently, it's 53F and Mostly Cloudy outside. Current wind speeds: 7 from the Northwest Pollen...