Discover the World Of Coding

FEATUREDLanguagesNewsTechnology

Is writing CSS in 2023 different or weird from what it was a few years ago?

In 2023, CSS is undergoing a rapid evolution, surpassing its previous pace of progress. With the introduction of new and upcoming features, the language has significantly changed the way it is written since the emergence of Flexbox and Grid years ago. In this article, Geoff Graham highlights the features that have greatly impacted his current CSS practices, while also mentioning those that haven’t had a considerable effect, at least not yet.


Is there anything in the world of front-end development that is evolving as rapidly as CSS these days? After a significant period of time following the introduction of groundbreaking features like Flexbox and Grid, observing the continuous release of new CSS features over the past few years has been akin to watching an exhilarating game of rugby on television. The pace of evolution is both exciting and, at times, overwhelming.

However, despite the abundance of new bells and whistles, one may wonder if these changes have truly revolutionized the way CSS is written. Undoubtedly, the introduction of new features has influenced my current CSS writing style, although not as dramatically as I initially expected.

Despite encountering numerous blog posts showcasing high-level examples and creative experiments utilizing these innovative features, practical applications in production or everyday use seem to be scarce. I remember how Sass gained widespread popularity in CSS tutorials, often being the preferred syntax for code examples and snippets. However, I haven’t witnessed the same level of organic adoption for features like logical properties, despite having full browser support for about two years now.

I want to clarify that I don’t intend to criticize anyone or anything. On the contrary, I am genuinely thrilled about CSS’s evolution. Many of the latest features fulfill long-standing desires of developers. Indeed, some of these features have found their way into my CSS code as well. While the impact may not be revolutionary, it has been enough to make the process of writing CSS more enjoyable than ever.

Let me share the various ways in which these CSS advancements have made a difference.

More And More Container Queries

Let me confess: I have never been fond of writing media queries for responsive layouts. The content’s behavior varies based on the viewport width and the component it’s placed within. Striking a balance for one component always involves a complex juggling act with the content in other components, leading to a clutter of media queries at seemingly random breakpoints. Although using Sass to nest media queries within a selector has made it somewhat bearable, it still doesn’t make the process of creating and modifying queries enjoyable, especially when presented with a new design and UI changes.

For me, container queries are the perfect solution. Now, I can encapsulate child elements within a parent container and rely on the container’s size to determine layout shifts without worrying about other surrounding components.

“I find container queries particularly appealing because they have a CSS-like essence. Being able to define a container directly within a selector follows a natural property-value structure, which eliminates the need for upfront mathematical calculations to determine breakpoints.”

.parent {

container-type: inline-size;

}

@container (min-width: 600px) {
.child {
align-self: center;
}
}

I continue to utilize media queries to create responsive layouts, although I predominantly employ them for larger designs that consist of grouped containers. By doing so, breakpoints become more foreseeable, and they can effectively target particular devices without the need to factor in the content within each separate container.

Learn About Container Queries

Grouping Style In Layers

I really enjoy using this approach to manage the cascade! It allows me to wrap any reset or third-party CSS from frameworks neatly in a cascade layer and place them at the bottom of the file, ensuring my own styles take precedence.

Although I haven’t used cascade layers in a production environment yet, they have become my go-to choice for almost every CodePen demo I create. The good thing is that browser support is not an issue for this technique. However, when working on larger projects, I still rely on Sass for specific advantages, and I find it more comfortable to maintain styles in partialized files.

Yet, for isolated demos like those on CodePen where all my styles are in one place, cascade layers are perfect! I usually only need one layer for the base styles since non-layered styles have higher specificity than layered ones. As a result, my demo-specific styles remain clean, uncluttered, and still have the ability to override the base styles, making it incredibly convenient to access and manage them.

body {
display: grid;
gap: 3rem;
place-items: center;
}

@layer base {
body {
font-size: 1.25rem;
line-height: 1.35;
padding: 3rem;
}
}

Learn More About Cascade Layers

 :is() And :where()

I frequently utilize these newer relational pseudo-selectors, not primarily for selecting elements based on relationships, but rather for effectively managing specificity. Unlike cascade layers, I find these selectors valuable in real-world applications.

The reason for this preference lies in the behavior of :is() – it determines specificity based on the most specific selector within its argument list, rather than the main selector.

/* Specificity: 0 1 1 */
:is(ol, .list, ul) li {}

/* Specificity: 0 0 2 */
ol li {}

When it comes to selector specificity, the .list selector bestows a higher score on the first ruleset, which effectively gives it an advantage over the second ruleset, despite the latter being placed higher in the cascade.

On the contrary, the specificity of :where() amounts to zero, meaning it doesn’t contribute to the overall score of the selector it’s applied to. The content within its argument list doesn’t impact its behavior either. Similar to how I utilize :is() to increase specificity, I employ :where() to decrease it. My preference is to keep specificity relatively low so that the cascade operates smoothly with minimal hindrance, and :where() facilitates that, especially when defining global styles.

A great example is using :where() to enclose :not() in order to prevent the latter from elevating specificity:

Before: .some-container :not(.special) { /* styles / } After: .some-container :where(:not(.special)) { / styles */ }

/* Specificity: 0 0 0 */
:where(:not(.some-element)) {}

Collectively, the :is() and :where() functions not only aid in handling specificity but also alleviate the burden of having to explicitly label elements.

I am among those individuals who still have a fondness for the BEM syntax. However, naming poses as one of its most challenging aspects. Frequently, I encounter difficulties in coming up with appropriate names that effectively describe an element’s purpose and its connection to neighboring elements. Thanks to the specificity-wrangling abilities of :is() and :where(), I can now depend less on complex class names and instead utilize element selectors more efficiently.

Learn More About :is() And :where()

The New Color Function Syntax

The revised syntax for color functions such as rgb() and hsl() (and the developing oklch() and oklab()) may not be the kind of attention-grabbing news that elicits excitement and admiration, but it significantly improves the process of defining color values.

One notable improvement is that I no longer need to resort to rgba() or hsla() when dealing with alpha values. In the past, I always used those functions regardless of whether alpha was required or not, simply to avoid the hassle of deciding which version to use

color: hsl(50deg, 100%, 50%);

/* Same */
color: hsla(50deg, 100%, 50% / 1)

“Indeed, including the additional ‘a’, ‘/’, and ‘1’ was a worthwhile investment, as it relieved the burden of having to deliberate over which function to employ.

However, the enhanced color syntax behaves akin to a honey badger—it simply disregards certain aspects. It pays no mind to the surplus ‘a’ in the function name, nor does it even acknowledge the presence of commas.”

color: hsl(50deg 100% 50% / .5);

“Yeah, that has certainly altered how I approach writing colors in CSS. What I’m particularly enthusiastic about is incorporating the newer oklch() and oklab() color spaces, now that they enjoy complete browser support!”

Learn More About CSS Color 4 Features

Sniffing Out User Preferences

Many of us were excited when we received media queries that consider a user’s display preferences, especially their preferred color scheme. This allows us to easily create both dark and light interfaces.

:root {
–bg-color: hsl(0deg 0% 100%);
–text-color: hsl(0deg 0% 0%);
}

@media (prefers-color-scheme: dark) {
:root {
–bg-color: hsl(0deg 0% 0%);
–text-color: hsl(0deg 0% 100%);
}
}

body {
background: var(–bg-color);
color: var(–text-color);
}

The feature that has had the most significant impact on my CSS is the “prefers-reduced-motion” query. Whenever I work on a project that includes CSS animations and transitions, it’s the primary consideration for me. I appreciate the concept that reducing motion preferences doesn’t imply eliminating all animations. Therefore, I frequently utilize the “prefers-reduced-motion” query to slow down animations when that preference is detected. This typically involves incorporating the following code (usually in a cascade layer for base styles): [The original code will be inserted here].

@layer base {
:root {
–anim-duration: 1s;
}

/* Reduced motion by default */
body {
animation-duration: –anim-duration;
transition: –anim-duration;
}

/* Opt into increased motion */
@media screen and (prefers-reduced-motion: no-preference) {
body {
–anim-duration: .25s;
}
}
}

Learn More About Preference Queries

Defining Color Palettes

Since adopting Sass, I have been utilizing variables to define and assign colors. The introduction of CSS custom properties was an exciting development for me. Prior to passing them into variables with more practical names, I used to give generic names to the colors in a palette

/* Color Palette */
–red: #ff0000;
/* etc. */

/* Brand Colors */
–color-primary: var(–red);
/* etc. */

Currently, I continue to engage in this practice, but I have taken it a step further by incorporating color functions into projects featuring extensive color palettes.

:root {
/* Primary Color HSL */
–h: 21deg;
–s: 100%;
–l: 50%;

–color-primary: hsl(var(–h) var(–s) var(–l) / 1);
}

.bg-color {
background: var(–color-primary);
}

.bg-color–secondary {
–h: 56deg;
background: hsl(var(–h) var(–s) var(–l) / 1);
}

Perhaps it could be considered slightly abstract, but for certain projects involving numerous shades like red, orange, yellow, etc., having such precise control to manipulate them is truly beneficial. There might be even more capabilities with color-mix() that I haven’t fully explored yet.

Learn More About Defining Color Palettes

What I’m Not Using

Hmm, it seems like my approach to writing CSS has evolved compared to how I used to do it! Although I don’t consciously feel the difference, it’s likely because there are numerous new features available that I haven’t explored yet. The features I’ve actually adopted are quite limited in comparison to the many others I still need to learn, either due to browser compatibility issues or simply not having had the opportunity to delve into them yet.

CSS Nesting

I’m eagerly anticipating CSS nesting because it could be the tipping point for me to abandon Sass in favor of vanilla CSS. However, its adoption depends on Firefox adding support for it, which might happen soon.

Style Queries 

I’ve always been interested in applying styles to elements based on the styles of other elements. Although I don’t have specific use cases for style queries yet, I believe that with increasing browser support, we’ll see more experimentation from talented developers in the form of blog posts.

:has() 

Once Firefox supports the :has() selector, I’ll make sure to incorporate it into my CSS. I’ve played around with it, and it’s fascinating to see others experimenting with it as well. The ability to select a parent element based on its child is a powerful addition to CSS.

Dynamic Viewport Units 

Since gaining wide support in late 2022, I’ve started using dynamic viewport units in my styles. I find them particularly useful when setting elements to full height on mobile devices. This change has subtly influenced how I write CSS.

Media Query Range Syntax 

To be honest, I haven’t given much thought to the improved syntax for responsive media queries on the viewport. I’m aware of it, but I haven’t integrated it into my everyday CSS yet due to my lack of familiarity with it.

OKLCH/OKLAB Color Spaces 

The oklch() color function seems to be my favorite, and I expect it to become widely used in my CSS, especially since it gained broad support earlier this year. I haven’t had many opportunities to utilize it in projects yet, but I believe it will become an integral part of my CSS workflow.

The only downside I see is its incompatibility with another exciting feature, color().

color() 

As of May 2023, color() has gained wide support, allowing tapping into any color space, be it sRGB, Display P3, or Rec2020. Although it’s still new for me to include in my everyday CSS, I’m confident it will become a go-to function for versatile color definition

–primary-color: color(display-p3 1 0.4 0);

I’m not fond of using RGB values as they are difficult to comprehend, unlike HSL, which is much more straightforward. Due to this reason, I’ll probably continue using oklch() or hsl() in most situations. It’s unfortunate that we don’t have an option to do something like this: [the specific action or feature you had in mind].

/* 👎 */
–primary-color: color(oklch 70% 0.222 41.29);

Instead, we have to do this

/* 👍 */
–primary-color: oklch(70% 0.222 41.29);

The puzzling aspect of this is that Display P3 doesn’t have a distinct function similar to OKLCH.

/*👎 */
–primary-color: display-p3(1 0.434 0.088);

We are currently compelled to utilize the color() function to access Display P3, which contrasts with the situation for OKLCH/OKLAB, where we are required to use specific functions. It is possible that in the future, we may have a unified global color() function that supports all these functionalities. Until then, in my CSS code, I find myself using both color() and specific functions like oklch(), depending on what I am working on.

Additionally, I would include color-mix() in this category, as it achieved full support simultaneously with color(). While it’s not something I regularly employ yet, I am interested in experimenting with it, especially for creating color palettes.

How about you? Your approach to writing CSS must have evolved over the past five years, right? Are you now handling the cascade in a different way? Do you find yourself using more vanilla CSS rather than relying on a preprocessor? And what about typography? Are you now more proficient in managing line heights and scale? I’d love to hear or see how you’re currently working with CSS.

One thought on “Is writing CSS in 2023 different or weird from what it was a few years ago?

  • Your blog stands out in a sea of generic and formulaic content Your unique voice and perspective are what keep me coming back for more

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *