Ads
Thursday, October 31, 2024
Clear today!
With a high of F and a low of 31F. Currently, it's 41F and Clear outside.
Current wind speeds: 17 from the Southeast
Pollen: 0
Sunrise: October 31, 2024 at 07:19PM
Sunset: November 1, 2024 at 05:51AM
UV index: 0
Humidity: 63%
via https://ift.tt/lO53SLQ
November 1, 2024 at 10:02AM
Wednesday, October 30, 2024
Partly Cloudy today!
With a high of F and a low of 26F. Currently, it's 37F and Mostly Cloudy outside.
Current wind speeds: 5 from the Northwest
Pollen: 0
Sunrise: October 30, 2024 at 07:18PM
Sunset: October 31, 2024 at 05:52AM
UV index: 0
Humidity: 84%
via https://ift.tt/QJWjOvk
October 31, 2024 at 10:02AM
State of CSS 2024 Results
They’re out! Like many of you, I look forward to these coming out each year. I don’t put much stock in surveys but they can be insightful and give a snapshot of the CSS zeitgeist. There are a few little nuggets in this year’s results that I find interesting. But before I get there, you’ll want to also check out what others have already written about it.
- Josh Comeau digested his takeaways in a recent newsletter.
Oh, I guess that’s it — at least it’s the most formal write-up I’ve seen. There’s a little summary by Ahmad Shadeed at the end of the survey that generally rounds things up. I’ll drop in more links as I find ’em.
In no particular order…
Demographics
Josh has way more poignant thoughts on this than I do. He rightfully calls out discrepancies in gender pay and regional pay, where men are way more compensated than women (a nonsensical and frustratingly never-ending trend) and the United States boasts more $100,000 salaries than anywhere else. The countries with the highest salaries were also the most represented in survey responses, so perhaps the results are no surprise. We’re essentially looking at a snapshot of what it’s like to be a rich, white male developer in the West.
Besides pay, my eye caught the Age Group demographics. As an aging front-ender, I often wonder what we all do when we finally get to retirement age. I officially dropped from the most represented age group (30-39, 42%) a few years ago into the third most represented tier (40-49, 21%). Long gone are my days being with the cool kids (20-29, 27%).
And if the distribution is true to life, I’m riding fast into my sunset years and will be only slightly more represented than those getting into the profession. I don’t know if anyone else feels similarly anxious about aging in this industry — but if you’re one of the 484 folks who identify with the 50+ age group, I’d love to talk with you.
Before we plow ahead, I think it’s worth calling out how relatively “new” most people are to front-end development.
Wow! Forty-freaking-four percent of respondents have less than 10 years of experience. Yes, 10 years is a high threshold, but we’re still talking about a profession that popped up in recent memory.
For perspective, someone developing for 10 years came to the field around 2014. That’s just when we were getting Flexbox, and several years after the big bang of CSS 3 and HTML 5. That’s just under half of developers who never had to deal with the headaches of table layouts, clearfix hacks, image sprites, spacer images, and rasterized rounded corners. Ethan Marcotte’s seminal article on “Responsive Web Design” predates these folks by a whopping four years!
That’s just wild. And exciting. I’m a firm believer in the next generation of front-enders but always hope that they learn from our past mistakes and become masters at the basics.
Features
I’m not entirely sure what to make of this section. When there are so many CSS features, how do you determine which are most widely used? How do you pare it down to just 50 features? Like, are filter
effects really the most widely used CSS feature? So many questions, but the results are always interesting nonetheless.
What I find most interesting are the underused features. For example, hanging-punctuation
comes in dead last in usage (1.57%) but is the feature that most developers (52%) have on their reading list. (If you need some reading material on it, Chris initially published the Almanac entry for hanging-punctuation
back in 2013.)
I also see Anchor Positioning at the end of the long tail with reported usage at 4.8%. That’ll go up for sure now that we have at least one supporting browser engine (Chromium) but also given all of the tutorials that have sprung up in the past few months. Yes, we’ve contributed to that noise… but it’s good noise! I think Juan published what might be the most thorough and thoughtful guide on the topic yet.
I’m excited to see Cascade Layers falling smack dab in the middle of the pack at a fairly robust 18.7%. Cascade Layers are super approachable and elegantly designed that I have trouble believing anybody these days when they say that the CSS Cascade is difficult to manage. And even though @scope
is currently low on the list (4.8%, same as Anchor Positioning), I’d bet the crumpled gum wrapper in my pocket that the overall sentiment of working with the Cascade will improve dramatically. We’ll still see “CSS is Awesome” memes galore, but they’ll be more like old familiar dad jokes in good time.
(Aside: Did you see the proposed designs for a new CSS logo? You can vote on them as of yesterday, but earlier versions played off the “CSS is Awesome” mean quite beautifully.)
Interestingly enough, viewport units come in at Number 11 with 44.2% usage… which lands them at Number 2 for most experience that developers have with CSS layout. Does that suggest that layout features are less widely used than CSS filters? Again, so many questions.
Frameworks
How many of you were surprised that Tailwind blew past Bootstrap as Top Dog framework in CSS Land? Nobody, right?
More interesting to me is that “No CSS framework” clocks in at Number 13 out of 21 list frameworks. Sure, its 46 votes are dwarfed by the 138 for Material UI at Number 10… but the fact that we’re seeing “no framework” as a ranking option at all would have been unimaginable just three years ago.
The same goes for CSS pre/post-processing. Sass (67%) and PostCSS (38%) are the power players, but “None” comes in third at 19%, ahead of Less, Stylus, and Lightning CSS.
It’s a real testament to the great work the CSSWG is doing to make CSS better every day. We don’t thank the CSSWG enough — thank you, team! Y’all are heroes around these parts.
CSS Usage
Josh already has a good take on the fact that only 67% of folks say they test their work on mobile phones. It should be at least tied with the 99% who test on desktops, right? Right?! Who knows, maybe some responses consider things like “Responsive Design Mode” desktop features to be the equivalent of testing on real mobile devices. I find it hard to believe that only 67% of us test mobile.
Oh, and The Great Divide is still alive and well if the results are true and 53% write more JavsScript than CSS in their day-to-day.
Missing CSS Features
This is always a fun topic to ponder. Some of the most-wanted CSS features have been lurking around 10+ years. But let’s look at the top three form this year’s survey:
- Mixins
- Conditional Logic
- Masonry
We’re in luck team! There’s movement on all three of those fronts:
- A new CSS Functions and Mixins Module draft was published in late June after the CSSWG resolved to adopt the proposal back in February. (Read our notes.)
- The CSS Working Group (CSSWG) resolved to add an
if()
conditional to the CSS Values Module Level 5 specification. (Read our notes.) - There are competing proposals for how to forge ahead with a CSS-y approach to masonry layouts. One is based on the CSS Grid Layout Module Level 3 draft specifcation and the other is a fresh new module dedicated to masonry. Apple has planted its flag. So has Chrome. Let the cage-match continue!
Resources
This is where I get to toot our own horn a bit because CSS-Tricks continues to place first among y’all when it comes to the blogs you follow for CSS happenings.
I’m also stoked to see Smashing Magazine right there as well. It was fifth in 2023 and I’d like to think that rise is due to me joining the team last year. Correlation implies causation, amirite?
But look at Kevin Powell and Josh in the Top 10. That’s just awesome. It speaks volumes about their teaching talents and the hard work they put into “helping people fall in love with CSS” as Kevin might say it. I was able to help Kevin with a couple of his videos last year (here’s one) and can tell you the guy cares a heckuva lot about making CSS approachable and fun.
Honestly, the rankings are not what we live for. Now that I’ve been given a second wind to work on CSS-Tricks, all I want is to publish things that are valuable to your everyday work as front-enders. That’s traditionally happened as a stream of daily articles but is shifting to more tutorials and resources, whether it’s guides (we’ve published four new ones this year), taking notes on interesting developments, spotlighting good work with links, or expanding the ol’ Almanac to account for things like functions, at-rules, and pseudos (we have lots of work to do).
My 2024 Pick
No one asked my opinion but I’ll say it anyway: Personal blogging. I’m seeing more of us in the front-end community getting back behind the keyboards of their personal websites and I’ve never been subscribed to more RSS feeds than I am today. Some started blogging as a “worry stone” during the 2020 lockdown. Some abandoned socials when Twitter X imploded. Some got way into the IndieWeb. Webrings and guestbooks are even gaining new life. Sure, it can be tough keeping up, but what a good problem to have! Let’s make RSS king once and for all.
That’s a wrap!
Seriously, a huge thanks to Sacha Greif and the entire Devographics team for the commitment to putting this survey together every year. It’s always fun. And the visualizations are always to die for.
State of CSS 2024 Results originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
from CSS-Tricks https://ift.tt/dt4cpQJ
via IFTTT
Tuesday, October 29, 2024
Partly Cloudy today!
With a high of F and a low of 33F. Currently, it's 47F and Rain Shower outside.
Current wind speeds: 13 from the Northwest
Pollen: 0
Sunrise: October 29, 2024 at 07:17PM
Sunset: October 30, 2024 at 05:53AM
UV index: 0
Humidity: 51%
via https://ift.tt/os6I3d1
October 30, 2024 at 10:02AM
Tooltip Best Practices
In this article, I try to summarize the best practices mentioned by various accessibility experts and their work (like this, this, and this) into a single article that’s easy to read, understand, and apply.
Let’s begin.
What are tooltips?
Tooltips are used to provide simple text hints for UI controls. Think of them as tips for tools. They’re basically little bubbles of text content that pop up when you hover over an unnamed control (like the bell icon in Stripe).
If you prefer more of a formal definition, Sarah Highley provides us with a pretty good one:
A “tooltip” is a non-modal (or non-blocking) overlay containing text-only content that provides supplemental information about an existing UI control. It is hidden by default, and becomes available on hover or focus of the control it describes.
She further goes on to say:
That definition could even be narrowed down even further by saying tooltips must provide only descriptive text.
This narrowed definition is basically (in my experience) how every accessibility expert defines tooltips:
- A tooltip is a popover.
- Tooltips must not contain interactive content.
Heydon Pickering takes things even further, saying: If you’re thinking of adding interactive content (even an ok
button), you should be using dialog
instead.
In his words:
You’re thinking of dialogs. Use a dialog.
Two kinds of tooltips
Tooltips are basically only used for two things:
- Labeling an icon
- Providing a contextual description of an icon
Heydon separates these cleanly into two categories, “Primary Label” and “Auxiliary description” in his Inclusive Components article on tooltips and toggletips).
Labeling
If your tooltip is used to label an icon — using only one or two words — you should use the aria-labelledby
attribute to properly label it since it is attached to nothing else on the page that would help identify it.
<button aria-labelledby="notifications"> ... </button>
<div role="tooltip" id="notifications">Notifications</div>
You could provide contextual information, like stating the number of notifications, by giving a space-separated list of id
s to aria-labelledby
.
<button aria-labelledby="notifications-count notifications-label">
<!-- bell icon here -->
<span id="notifications-count">3</span>
</button>
<div role="tooltip" id="notifications-label">Notifications</div>
Providing contextual description
If your tooltip provides a contextual description of the icon, you should use aria-describedby
. But, when you do this, you also need to provide an accessible name for the icon.
In this case, Heydon recommends including the label as the text content of the button. This label would be hidden visually from sighted users but read for screen readers.
Then, you can add aria-describedby
to include the auxiliary description.
<button class="notifications" aria-describedby="notifications-desc">
<!-- icon for bell here -->
<span id="notifications-count">3</span>
<span class="visually-hidden">Notifications</span>
</button>
<div role="tooltip" id="notifications-desc">View and manage notifications settings</div>
Here, screen readers would say “3 notifications” first, followed by “view and manage notifications settings” after a brief pause.
Additional tooltip dos and don’ts
Here are a couple of additional points you should be aware of:
Do:
- Use
aria-labellebdy
oraria-describedby
attributes depending on the type of tooltip you’re building. - Use the
tooltip
role even if it doesn’t do much in screen readers today, because it may extend accessibility support for some software. - Open tooltips on
mouseover
orfocus
, and close them onmouseout
orblur
. - Allow a mouse user to move their mouse over the tooltip content without dismissing the tooltip.
- Allow a keyboard user to close the tooltip on the
Escape
button, per WCAG Success Criterion 1.4.13.
Don’t:
- Don’t use the
title
attribute. Much has been said about this so I shall not repeat them. - Don’t use the
aria-haspopup
attribute with thetooltip
role becausearia-haspopup
signifies interactive content whiletooltip
should contain non-interactive content. - Don’t include essential content inside tooltips because some screen readers may ignore
aria-labelledby
oraria-describedby
. (It’s rare, but possible.)
Tooltip limitations and alternatives
Tooltips are inaccessible to most touch devices because:
- users cannot hover over a button on a touch device, and
- users cannot focus on a button on a touch device.
The best alternative is not to use tooltips, and instead, find a way to include the label or descriptive text in the design.
If the “tooltip” contains a lot of content — including interactive content — you may want to display that information with a Toggletip (or just use a <dialog>
element).
Heydon explains toggletips nicely and concisely:
Toggletips exist to reveal information balloons. Often they take the form of little “i” icons.
These informational icons should be wrapped within a <button>
element. When opened, screen readers can announce the text contained in it through a live region with the status
role.
<span class="tooltip-container">
<button type="button" aria-label="more info">i</button>
<span role="status">This clarifies whatever needs clarifying</span>
</span>
Speaking anymore about toggletips detracts this article from tooltips so I’ll point you to Heydon’s “Tooltips and Toggletips” article if you’re interested in chasing this short rabbit hole.
That’s all you need to know about tooltips and their current best practices!
Further reading
- Clarifying the Relationship Between Popovers and Dialogs (Zell Liew)
- Tooltips and Toggletips (Inclusive Components)
- Tooltips in the time of WCAG 2.1 (Sarah Higley)
- Short note on
aria-label
,aria-labelledby
, andaria-describedby
(Léonie Watson) - Some Hands-On with the HTML Dialog Element (Chris Coyier)
Tooltip Best Practices originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
from CSS-Tricks https://ift.tt/Qz8R37x
via IFTTT
Monday, October 28, 2024
Partly Cloudy today!
With a high of F and a low of 48F. Currently, it's 56F and Clear outside.
Current wind speeds: 17 from the Southwest
Pollen: 0
Sunrise: October 28, 2024 at 07:15PM
Sunset: October 29, 2024 at 05:54AM
UV index: 0
Humidity: 34%
via https://ift.tt/W1BNIE4
October 29, 2024 at 10:02AM
Sunday, October 27, 2024
Mostly Cloudy today!
With a high of F and a low of 50F. Currently, it's 59F and Clear outside.
Current wind speeds: 14 from the South
Pollen: 0
Sunrise: October 27, 2024 at 07:14PM
Sunset: October 28, 2024 at 05:55AM
UV index: 0
Humidity: 46%
via https://ift.tt/b0uYjdI
October 28, 2024 at 10:02AM
Saturday, October 26, 2024
Clear today!
With a high of F and a low of 41F. Currently, it's 47F and Clear outside.
Current wind speeds: 9 from the South
Pollen: 0
Sunrise: October 26, 2024 at 07:13PM
Sunset: October 27, 2024 at 05:56AM
UV index: 0
Humidity: 65%
via https://ift.tt/b5x6JXm
October 27, 2024 at 10:02AM
Friday, October 25, 2024
Mostly Clear today!
With a high of F and a low of 36F. Currently, it's 42F and Clear outside.
Current wind speeds: 9 from the Southeast
Pollen: 0
Sunrise: October 25, 2024 at 07:12PM
Sunset: October 26, 2024 at 05:58AM
UV index: 0
Humidity: 64%
via https://ift.tt/FuBmv4I
October 26, 2024 at 10:02AM
Left Half and Right Half Layout – Many Different Ways
A whole bunch of years ago, we posted on this idea here on CSS-Tricks. We figured it was time to update that and do the subject justice.
Imagine a scenario where you need to split a layout in half. Content on the left and content on the right. Basically two equal height columns are needed inside of a container. Each side takes up exactly half of the container, creating a distinct break between one. Like many things in CSS, there are a number of ways to go about this and we’re going to go over many of them right now!
Update (Oct. 25, 2024): Added an example that uses CSS Anchor Positioning.
Using Background Gradient
One simple way we can create the appearance of a changing background is to use gradients. Half of the background is set to one color and the other half another color. Rather than fade from one color to another, a zero-space color stop is set in the middle.
.container {
background: linear-gradient(
to right,
#ff9e2c 0%,
#ff9e2c 50%,
#b6701e 50%,
#b6701e 100%
);
}
This works with a single container element. However, that also means that it will take working with floats or possibly some other layout method if content needs to fill both sides of the container.
Using Absolute Positioning
Another route might be to set up two containers inside of a parent container, position them absolutely, split them up in halves using percentages, then apply the backgrounds. The benefit here is that now we have two separate containers that can hold their own content.
Absolute positioning is sometimes a perfect solution, and sometimes untenable. The parent container here will need to have a set height, and setting heights is often bad news for content (content changes!). Not to mention absolute positioned elements are out of the document flow. So it would be hard to get this to work while, say, pushing down other content below it.
Using (fake) Tables
Yeah, yeah, tables are so old school (not to mention fraught with accessibility issues and layout inflexibility). Well, using the display: table-cell;
property can actually be a handy way to create this layout without writing table markup in HTML. In short, we turn our semantic parent container into a table, then the child containers into cells inside the table — all in CSS!
You could even change the display properties at breakpoints pretty easily here, making the sides stack on smaller screens. display: table;
(and friends) is supported as far back as IE 8 and even old Android, so it’s pretty safe!
Using Floats
We can use our good friend the float
to arrange the containers beside each other. The benefit here is that it avoids absolute positioning (which as we noted, can be messy).
In this example, we’re explicitly setting heights to get them to be even. But you don’t really get that ability with floats by default. You could use the background gradient trick we already covered so they just look even. Or look at fancy negative margin tricks and the like.
Also, remember you may need to clear the floats on the parent element to keep the document flow happy.
Using Inline-Block
If clearing elements after floats seems like a burden, then using display: inline-block
is another option. The trick here is to make sure that the elements for the individual sides have no breaks or whitespace in between them in the HTML. Otherwise, that space will be rendered as a literal space and the second half will break and fall.
Again there is nothing about inline-block that helps us equalize the heights of the sides, so you’ll have to be explicit about that.
There are also other potential ways to deal with that spacing problem described above.
Using Flexbox
Flexbox is a pretty fantastic way to do this, just note that it’s limited to IE 10 and up and you may need to get fancy with the prefixes and values to get the best support.
Using this method, we turn our parent container into a flexible box with the child containers taking up an equal share of the space. No need to set widths or heights! Flexbox just knows what to do, because the defaults are set up perfectly for this. For instance, flex-direction: row;
and align-items: stretch;
is what we’re after, but those are the defaults so we don’t have to set them. To make sure they are even though, setting flex: 1;
on the sides is a good plan. That forces them to take up equal shares of the space.
In this demo we’re making the side flex containers as well, just for fun, to handle the vertical and horizontal centering.
Using Grid Layout
For those living on the bleeding edge, the CSS Grid Layout technique is like the Flexbox and Table methods merged into one. In other words, a container is defined, then split into columns and cells which can be filled flexibly with child elements.
CSS Anchor Positioning
This started rolling out in 2024 and we’re still waiting for full browser support. But we can use CSS Anchor Positioning to “attach” one element to another — even if those two elements are completely unrelated in the markup.
The idea is that we have one element that’s registered as an “anchor” and another element that’s the “target” of that anchor. It’s like the target element is pinned to the anchor. And we get to control where we pin it!
.anchor {
anchor-name: --anchor;
}
.target {
anchor-position: --anchor;
position: absolute; /* required */
}
This sets up an .anchor
and establishes a relationship with a .target
element. From here, we can tell the target which side of the anchor it should pin to.
.anchor {
anchor-name: --anchor;
}
.target {
anchor-position: --anchor;
position: absolute; /* required */
left: anchor(right);
}
Isn’t it cool how many ways there are to do things in CSS?
Left Half and Right Half Layout – Many Different Ways originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
from CSS-Tricks https://ift.tt/iQwpGFa
via IFTTT
Come to the light-dark() Side
You’d be forgiven for thinking coding up both a dark and a light mode at once is a lot of work. You have to remember @media
queries based on prefers-color-scheme
as well as extra complications that arise when letting visitors choose whether they want light or dark mode separately from the OS setting. And let’s not forget the color palette itself! Switching from a “light” mode to a “dark” mode may involve new variations to get the right amount of contrast for an accessible experience.
It is indeed a lot of work. But I’m here to tell you it’s now a lot simpler with modern CSS!
Default HTML color scheme(s)
We all know the “naked” HTML theme even if we rarely see it as we’ve already applied a CSS reset or our favorite boilerplate CSS before we even open localhost. But here’s a news flash: HTML doesn’t only have the standard black-on-white theme, there is also a native white-on-black version.
If you want to create a dark mode interface, this is a great base to work with and saves you from having to account for annoying details, like dark inputs, buttons, and other interactive elements.
Switching color schemes automatically based on OS preference
Without any @media
queries — or any other CSS at all — if all we did was declare color-scheme: light dark
on the root element, the page will apply either the light or dark color scheme automatically by looking at the visitor’s operating system (OS) preferences. Most OSes have a built-in accessibility setting for your preferred color scheme — “light”, “dark”, or even “auto” — and browsers respect that setting.
html {
color-scheme: light dark;
}
We can even accomplish this without CSS directly in the HTML document in a <meta>
tag:
<meta name="color-scheme" content="light dark">
Whether you go with CSS or the HTML route, it doesn’t matter — they both work the same way: telling the browser to make both light and dark schemes available and apply the one that matches the visitor’s preferences. We don’t even need to litter our styles with prefers-color-scheme
instances simply to swap colors because the logic is built right in!
You can apply light
or dark
values to the color-scheme
property. At the same time, I’d say that setting color-scheme: light
is redundant, as this is the default color scheme with or without declaring it.
You can, of course, control the <meta>
tag or the CSS property with JavaScript.
There’s also the possibility of applying the color-scheme
property on specific elements instead of the entire page in one fell swoop. Then again, that means you are required to explicitly declare an element’s color
and background-color
properties; otherwise the element is transparent and inherits its text color from its parent element.
What values should you give it? Try:
Default text and background color variables
The “black” colors of these native themes aren’t always completely black but are often off-black, making the contrast a little easier on the eyes. It’s worth noting, too, that there’s variation in the blackness of “black” between browsers.
What is very useful is that this default not-pure-black and maybe-not-pure-white background-color
and text color
are available as <system-color>
variables. They also flip their color values automatically with color-scheme
!
They are: Canvas
and CanvasText
.
These two variables can be used anywhere in your CSS to call up the current default background color (Canvas
) or text color (CanvasText
) based on the current color scheme. If you’re familiar with the currentColor
value in CSS, it seems to function similarly. CanvasText
, meanwhile, remains the default text color
in that it can’t be changed the way currentColor
changes when you assign something to color
.
In the following examples, the only change is the color-scheme
property:
Not bad! There are many, many more of these system variables. They are case-insensitive, often written in camelCase
or PascalCase
for readability. MDN lists 19 <system-color>
variables and I’m dropping them in below for reference.
Open to view 19 system color names and descriptions
AccentColor
: The background color for accented user interface controlsAccentColorText
: The text color for accented user interface controlsActiveText
: The text color of active linksButtonBorder
: The base border color for controlsButtonFace
: The background color for controlsButtonText
: The text color for controlsCanvas
: The background color of an application’s content or documentsCanvasText
: The text color used in an application’s content or documentsField
: The background color for input fieldsFieldText
: The text color inside form input fieldsGrayText
: The text color for disabled items (e.g., a disabled control)Highlight
: The background color for selected itemsHighlightText
: The text color for selected itemsLinkText
: The text color used for non-active, non-visited linksMark
: The background color for text marked up in a<mark>
elementMarkText
: The text color for text marked up in a<mark>
elementSelectedItem
: The background color for selected items (e.g., a selected checkbox)SelectedItemText
: The text color for selected itemsVisitedText
: The text visited links
Cool, right? There are many of them! There are, unfortunately, also discrepancies as far as how these color keywords are used and rendered between different OSes and browsers. Even though “evergreen” browsers arguably support all of them, they don’t all actually match what they’re supposed to, and fail to flip with the CSS color-scheme
property as they should.
Egor Kloos (also known as dutchcelt) is keeping an eye on the current status of system colors, including which ones exist and the browsers that support them, something he does as part of a classless CSS framework cleverly called system.css.
Declaring colors for both modes together
OK good, so now you have a page that auto-magically flips dark and light colors according to system preferences. Whether you choose to use these system colors or not is up to you. I just like to point out that “dark” doesn’t always have to mean pure “black” just as “light” doesn’t have to mean pure “white.” There are lots more colors to pair together!
But what’s the best or simplest way to declare colors so they work in both light and dark mode?
In my subjective reverse-best order:
Third place: Declare color opacity
You could keep all the same background colors in dark and light modes, but declare them with an opacity (i.e. rgb(128 0 0 / 0.5)
or #80000080
). Then they’ll have the Canvas
color shine through.
It’s unusable in this way for text colors, and you may end up with somewhat muted colors. But it is a nice easy way to get some theming done fast. I did this for the code blocks on this old light and dark mode demo.
Second place: Use color-mix()
Like this:
color-mix(in oklab, Canvas 75%, RebeccaPurple);
Similar (but also different) to using opacity to mute a color is mixing colors in CSS. We can even mix the system color variables! For example, one of the colors can be either Canvas
or CanvasText
so that the background color always mixes with Canvas
and the text color always mixes with CanvasText
.
We now have the CSS color-mix()
function to help us with this. The first argument in the function defines the color space where the color mixing happens. For example, we can tell the function that we are working in the OKLAB color space, which is a rectangular color space like sRGB making it ideal to mix with sRGB color values for predictable results. You can certainly mix colors from different color spaces — the OKLAB/sRGB combination happens to work for me in this instance.
The second and third arguments are the colors you want to mix, and in what proportion. Proportions are optional but expressed in percentages. Without declaring a proportion, the mix is an even 50%-50% split. If you add percentages for both colors and they don’t match up to 100%, it does a little math for you to prevent breakages.
The color-mix()
approach is useful if you’re happy to keep the same hues and color saturations regardless of whether the mode is light or dark.
In this example, as you change the value of the hue slider, you’ll see color changes in the themed boxes, following the theme color but mixed with Canvas
and CanvasText
:
You may have noticed that I used OKLCH and HSL color spaces in that last example. You may also have noticed that the HSL-based theme color and the themed paragraph were a lot more “flashy” as you moved the hue slider.
I’ve declared colors using a polar color space, like HSL, for years, loving that you can easily take a hue and go up or down the saturation and lightness scales based on need. But, I concede that it’s problematic if you’re working with multiple hues while trying to achieve consistent perceived lightness and saturation across them all. It can be difficult to provide ample contrast across a spectrum of colors with HSL.
The OKLCH color space is also polar just like HSL, with the same benefits. You can pick your hue and use the chroma value (which is a bit like saturation in HSL) and the lightness scales accurately in the same way. Both OKLCH and OKLAB are designed to better match what our eyes perceive in terms of brightness and color compared to transitioning between colors in the sRGB space.
While these color spaces may not explicitly answer the age-old question, Is my blue the same as your blue?
the colors are much more consistent and require less finicking when you decide to base your whole website’s palette on a different theme color. With these color spaces, the contrasts between the computed colors remain much the same.
First place (winner!): Use light-dark()
Like this:
light-dark(lavender, saddlebrown);
With the previous color-mix()
example, if you choose a pale lavender in light mode, its dark mode counterpart is very dark lavender.
The light-dark()
function, conversely, provides complete control. You might want that element to be pale lavender in light mode and a deep burnt sienna brown in dark mode. Why not? You can still use color-mix()
within light-dark()
if you like — declare the colors however you like, and gain much more fine-grained control over your colors.
Feel free to experiment in the following editable demo:
Using color-scheme: light dark;
— or the corresponding meta tag in HTML on your page —is a prerequisite for the light-dark()
function because it allows the function to respect a person’s system preference, or whichever single light
or dark
value you have set on color-scheme
.
Another consideration is that light-dark()
is newly available across browsers, with just over 80% coverage across all users at the time I’m writing this. So, you might consider including a fallback in your CSS for browsers that lack support for the function.
What makes using color-scheme
and light-dark()
better than using @media
queries?
@media
queries have been excellent tools, but using them to query prefers-color-scheme
only ever follows the preference set within the person’s operating system. This is fine until you (rightfully) want to offer the visitor more choices, decoupled from whether they prefer the UI on their device to be dark or light.
We’re already capable of doing that, of course. We’ve become used to a lot of jiggery-pokery with extra CSS classes, using duplicated styles, or employing custom properties to make it happen.
The joy of using color-scheme
is threefold:
- It gives you the basic monochrome dark mode for free!
- It can natively do the mode switching based on OS mode preference.
- You can use JavaScript to toggle between light and dark mode, and the colors declared in the
light-dark()
functions will follow it.
Light, dark, and auto mode controls
Essentially, all we are doing is setting one of three options for whether the color-scheme
is light
, dark
, or updates auto
-matically.
I advise offering all three as discrete options, as it removes some complications for you! Any new visitor to the site will likely be in auto
mode because accepting the visitor’s OS setting is the least jarring default state. You then give that person the choice to stay with that or swap it out for a different color scheme. This way, there’s no need to sniff out what mode someone prefers to, for example, display the correct icon on a toggle and make it perform the correct action. There is also no need to keep an event listener on prefers-color-scheme
in case of changes — your color-scheme: light dark
declaration in CSS handles that for you.
Adjusting color-scheme
in pure CSS
Yes, this is totally possible! But the approach comes with a few caveats:
- You can’t use
<button>
— only radio inputs, or<options>
in a<select>
element. - It only works on a per page basis, not per website, which means changes are lost on reload or refresh.
- The browser needs to support the
:has()
pseudo-selector. Most modern browsers do, but some folks using older devices might miss out on the experience.
Using the :has()
pseudo-selector
This approach is almost alarmingly simple and is fantastic for a simple one-pager! Most of the heavy lifting is done with this:
/* default, or 'auto' */
html {
color-scheme: light dark;
}
html:has([value="light"]:checked {
color-scheme: light;
}
html:has([value="dark"]:checked {
color-scheme: dark;
}
The second and third rulesets above look for an attribute called value
on any element that has “light” or “dark” assigned to it, then change the color-scheme
to match only if that element is :checked
.
This approach is not very efficient if you have a huge page full of elements. In those cases, it’s better to be more specific. In the following two examples, the CSS selectors check for value
only within an element containing id="mode-switcher"
.
html:has(#mode-switcher [value="light"]:checked) { color-scheme: light }
/* Did you know you don't need the ";" for a one-liner? Now you do! */
Using a <select>
element:
Using <input type="radio">
:
We could theoretically use checkboxes for this, but since checkboxes are not supposed to be used for mutually exclusive options, I won’t provide an example here. What happens in the case of more than one option being checked? The last matching CSS declaration wins (which is dark
in the examples above).
Adjusting color-scheme
in HTML with JavaScript
I subscribe to Jeremy Keith’s maxim when it comes to reaching for JavaScript:
JavaScript should only do what only JavaScript can do.
This is exactly that kind of situation.
If you want to allow visitors to change the color scheme using buttons, or you would like the option to be saved the next time the visitor comes to the site, then we do need at least some JavaScript. Rather than using the :has()
pseudo-selector in CSS, we have a few alternative approaches for changing the color-scheme
when we add JavaScript to the mix.
Using <meta>
tags
If you have set your color-scheme
within a meta tag in the <head>
of your HTML:
<meta name="color-scheme" content="light dark">
…you might start by making a useful constant like so:
const colorScheme = document.querySelector('meta[name="color-scheme"]');
And then you can manipulate that, assigning it light
or dark
as you see fit:
colorScheme.setAttribute("content", "light"); // to light mode
colorScheme.setAttribute("content", "dark"); // to dark mode
colorScheme.setAttribute("content", "light dark"); // to auto mode
This is a very similar approach to using <meta>
tags but is different if you are setting the color-scheme
property in CSS:
html { color-scheme: light dark; }
Instead of setting a colorScheme
constant as we just did in the last example with the <meta>
tag, you might select the <html>
element instead:
const html = document.querySelector('html');
Now your manipulations look like this:
html.style.setProperty("color-scheme", "light"); // to light mode
html.style.setProperty("color-scheme", "dark"); // to dark mode
html.style.setProperty("color-scheme", "light dark"); // to auto mode
I like to turn those manipulations into functions so that I can reuse them:
function switchAuto() {
html.style.setProperty("color-scheme", "light dark");
}
function switchLight() {
html.style.setProperty("color-scheme", "light");
}
function switchDark() {
html.style.setProperty("color-scheme", "dark");
}
Alternatively, you might like to stay as DRY as possible and do something like this:
function switchMode(mode) {
html.style.setProperty("color-scheme", mode === "auto" ? "light dark" : mode);
}
The following demo shows how this JavaScript-based approach can be used with buttons, radio buttons, and a <select>
element. Please note that not all of the controls are hooked up to update the UI — the demo would end up too complicated since there’s no world where all three types of controls would be used in the same UI!
I opted to use onchange
and onclick
in the HTML elements mainly because I find them readable and neat. There’s nothing wrong with instead attaching a change event listener to your controls, especially if you need to trigger other actions when the options change. Using onclick
on a button doesn’t only work for clicks, the button is still keyboard-focusable and can be triggered with Spacebar and Enter too, as usual.
Remembering the selection for repeat visits
The biggest caveat to everything we’ve covered so far is that this only works once. In other words, once the visitor has left the site, we’re doing nothing to remember their color scheme preference. It would be a better user experience to store that preference and respect it anytime the visitor returns.
The Web Storage API is our go-to for this. And there are two available ways for us to store someone’s color scheme preference for future visits.
localStorage
Local storage saves values directly on the visitor’s device. This makes it a nice way to keep things off your server, as the stored data never expires, allowing us to call it anytime. That said, we’re prone to losing that data whenever the visitor clears cookies and cache and they’ll have to make a new selection that is freshly stored in localStorage
.
You pick a key name and give it a value with .setItem()
:
localStorage.setItem("mode", "dark");
The key and value are saved by the browser, and can be called up again for future visits:
const mode = localStorage.getItem("mode");
You can then use the value stored in this key to apply the person’s preferred color scheme.
sessionStorage
Session storage is thrown away as soon as a visitor browses away to another site or closes the current window/tab. However, the data we capture in sessionStorage
persists while the visitor navigates between pages or views on the same domain.
It looks a lot like localStorage
:
sessionStorage.setItem("mode", "dark");
const mode = sessionStorage.getItem("mode");
Which storage method should I use?
Personally, I started with sessionStorage
because I wanted my site to be as simple as possible, and to avoid anything that would trigger the need for a GDPR-compliant cookie banner if we were holding onto the person’s preference after their session ends. If most of your traffic comes from new visitors, then I suggest using sessionStorage
to prevent having to do extra work on the GDPR side of things.
That said, if your traffic is mostly made up of people who return to the site again and again, then localStorage
is likely a better approach. The convenience benefits your visitors, making it worth the GDPR work.
The following example shows the localStorage
approach. Open it up in a new window or tab, pick a theme other than what’s set in your operating system’s preferences, close the window or tab, then re-open the demo in a new window or tab. Does the demo respect the color scheme you selected? It should!
Choose the “Auto” option to go back to normal.
If you want to look more closely at what is going on, you can open up the developer tools in your browser (F12
for Windows, CTRL
+ click and select “Inspect” for macOS). From there, go into the “Application” tab and locate https://cdpn.io
in the list of items stored in localStorage
. You should see the saved key (mode
) and the value (dark
or light
). Then start clicking on the color scheme options again and watch the mode
update in real-time.
Accessibility
Congratulations! If you have got this far, you are considering or already providing versions of your website that are more comfortable for different people to use.
For example:
- People with strong floaters in their eyes may prefer to use dark mode.
- People with astigmatism may be able to focus more easily in light mode.
So, providing both versions leaves fewer people straining their eyes to access the content.
Contrast levels
I want to include a small addendum to this provision of a light and dark mode. An easy temptation is to go full monochrome black-on-white or white-on-black. It’s striking and punchy! I get it. But that’s just it — striking and punchy can also trigger migraines for some people who do a lot better with lower contrasts.
Providing high contrast is great for the people who need it. Some visual impairments do make it impossible to focus and get a sharp image, and a high contrast level can help people to better make out the word shapes through a blur. Minimum contrast levels are important and should be exceeded.
Thankfully, alongside other media queries, we can also query prefers-contrast
which accepts values for no-preference
, more
, less
, or custom
.
In the following example (which uses :has()
and color-mix()
), a <select>
element is displayed to offer contrast settings. When “Low” is selected, a filter of contrast(75%)
is placed across the page. When “High” is selected, CanvasText
and Canvas
are used unmixed for text color and background color:
Adding a quick high and low contrast theme gives your visitors even more choice for their reading comfort. Look at that — now you have three contrast levels in both dark and light modes — six color schemes to choose from!
ARIA-pressed
ARIA stands for Accessible Rich Internet Applications and is designed for adding a bit of extra info where needed to screen readers and other assistive tech.
The words “where needed” do heavy lifting here. It has been said that, like apostrophes, no ARIA is better than bad ARIA. So, best practice is to avoid putting it everywhere. For the most part (with only a few exceptions) native HTML elements are good to go out of the box, especially if you put useful text in your buttons!
The little bit of ARIA I use in this demo is for adding the aria-pressed
attribute to the buttons, as unlike a radio group or select element, it’s otherwise unclear to anyone which button is the “active” one, and ARIA helps nicely with this use case. Now a screen reader will announce both its accessible name and whether it is in a pressed or unpressed state along with a button.
Following is an example code snippet with all the ARIA code bolded — yes, suddenly there’s lots more! You may find more elegant (or DRY-er) ways to do this, but showing it this way first makes it more clear to demonstrate what’s happening.
Our buttons have id
s, which we have used to target them with some more handy const
s at the top. Each time we switch mode
, we make the button’s aria-pressed
value for the selected mode true
, and the other two false
:
const html = document.querySelector("html");
const mode = localStorage.getItem("mode");
const lightSwitch = document.querySelector('#lightSwitch');
const darkSwitch = document.querySelector('#darkSwitch');
const autoSwitch = document.querySelector('#autoSwitch');
if (mode === "light") switchLight();
if (mode === "dark") switchDark();
function switchAuto() {
html.style.setProperty("color-scheme", "light dark");
localStorage.removeItem("mode");
lightSwitch.setAttribute("aria-pressed","false");
darkSwitch.setAttribute("aria-pressed","false");
autoSwitch.setAttribute("aria-pressed","true");
}
function switchLight() {
html.style.setProperty("color-scheme", "light");
localStorage.setItem("mode", "light");
lightSwitch.setAttribute("aria-pressed","true");
darkSwitch.setAttribute("aria-pressed","false");
autoSwitch.setAttribute("aria-pressed","false");
}
function switchDark() {
html.style.setProperty("color-scheme", "dark");
localStorage.setItem("mode", "dark");
lightSwitch.setAttribute("aria-pressed","false");
darkSwitch.setAttribute("aria-pressed","true");
autoSwitch.setAttribute("aria-pressed","false");
}
On load, the buttons have a default setting, which is when the “Auto” mode button is active. Should there be any other mode in the localStorage
, we pick it up immediately and run either switchLight()
or switchDark()
, both of which contain the aria-pressed
changes relevant to that mode.
<button id="autoSwitch" aria-pressed="true" type="button" onclick="switchAuto()">Auto</button>
<button id="lightSwitch" aria-pressed="false" type="button" onclick="switchLight()">Light</button>
<button id="darkSwitch" aria-pressed="false" type="button" onclick="switchDark()">Dark</button>
The last benefit of aria-pressed
is that we can also target it for styling purposes:
button[aria-pressed="true"] {
background-color: transparent;
border-width: 2px;
}
Finally, we have a nice little button switcher, with its state clearly shown and announced, that remembers your choice when you come back to it. Done!
Outroduction
Or whatever the opposite of an introduction is…
…don’t let yourself get dragged into the old dark vs light mode argument. Both are good. Both are great! And both modes are now easy to create at once. At the start of your next project, work or hobby, do not give in to fear and pick a side — give both a try, and give in to choice.
Come to the light-dark() Side originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
from CSS-Tricks https://ift.tt/VgsGc61
via IFTTT
Thursday, October 24, 2024
Clear today!
With a high of F and a low of 34F. Currently, it's 45F and Clear outside.
Current wind speeds: 12 from the Northeast
Pollen: 0
Sunrise: October 24, 2024 at 07:11PM
Sunset: October 25, 2024 at 05:59AM
UV index: 0
Humidity: 49%
via https://ift.tt/e21VpQ7
October 25, 2024 at 10:02AM
You can use text-wrap: balance; on icons
Terence Eden on using text-wrap: balance
for more than headings:
But the name is, I think, slightly misleading. It doesn’t only work on text. It will work on any content. For example – I have a row of icons at the bottom of this page. If the viewport is too narrow, a single icon might drop to the next line. That can look a bit weird.
Heck yeah. I may have reached for some sort of auto-fitting grid approach, but hey, may as well go with a one-liner if you can! And while we’re on the topic, I just wanna mention that, yes, text-wrap: balance
will work on any content. — just know that the spec is a little opinionated on this and make sure that the content is fewer than five lines.
There’s likely more nuance to come if the note for Issue 6 in the spec is any indication about possibly allowing for a line length minimum:
Suggestion for value space is
match-indent
|<length>
|<percentage>
(with Xch given as an example to make that use case clear). Alternately<integer>
could actually count the characters.
You can use text-wrap: balance; on icons originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.
from CSS-Tricks https://ift.tt/dC5fcxG
via IFTTT
Wednesday, October 23, 2024
Clear today!
With a high of F and a low of 44F. Currently, it's 48F and Clear outside.
Current wind speeds: 14 from the South
Pollen: 0
Sunrise: October 23, 2024 at 07:10PM
Sunset: October 24, 2024 at 06:00AM
UV index: 0
Humidity: 78%
via https://ift.tt/fP6z4GQ
October 24, 2024 at 10:02AM
Mostly Clear today!
With a high of F and a low of 15F. Currently, it's 14F and Clear outside. Current wind speeds: 13 from the Southwest Pollen: 0 S...
-
So you want an auto-playing looping video without sound? In popular vernacular this is the very meaning of the word GIF . The word has stuck...
-
With a high of F and a low of 31F. Currently, it's 37F and Cloudy outside. Current wind speeds: 7 from the Northeast Pollen: 0 S...
-
Last year , we kicked out a roundup of published surveys, research, and other findings from around the web. There were some nice nuggets in ...