Back

Exploring CSS @-rules

Exploring CSS @-rules

As a web designer using CSS, you have likely encountered directives or block-level rules that start with the ”@” symbol. These are called CSS At-rules, which provide instructions on how CSS should behave.  A typical example is the @media query for designing responsive web layouts. However, @media is one common example. This article will explore the various at-rules, focusing on some of the most important ones and their use cases.

Learning CSS At-rules opens doors to more advanced development.

They allow you to achieve effects and functionalities that are impossible with just basic CSS selectors. For instance, you can create smooth animations with @keyframes.

At-rule like @media query enables the creation of responsive designs that adapt to various screen sizes and devices, providing a consistent, user-friendly experience across different platforms.

They help maintain modularity and organize your CSS architecture.

There are many reasons to learn these rules. I gave a quick summary above, but as you read the article, you will discover why they are essential.

Types of CSS At-Rules

CSS At-rules can be categorized into two main types: directives and block CSS At-rules. All available rules are detailed in the MDN documentation.

Directives CSS At-Rules

Directives CSS At-rules set the foundational styling parameters. They define styles across different elements and components within a stylesheet and should be placed at the top before all other CSS attributes and properties.

Below is its General syntax:

@identifier "rule";

The documentation contains many directives CSS At-rules, but this article will only explore the @import rule because of its high importance.

@import

The @import rule imports external stylesheets into a CSS file. It can be used to split large stylesheets into smaller ones suitable for modularization and organization. The imported stylesheets are fetched sequentially, which may impact page loading performance.

The @import rule must be placed at the top of the CSS file before other CSS rules.

The basic syntax of @import:

@import url('path/to/stylesheet.css');

The URL (uniform resource locator) specifies the path to the external stylesheet you want to import.

The @import rule is supported in all browsers.

Use Case

In this example, we would import the following external stylesheets, header.css and paragraph.css, into a main stylesheet, style.css, to style the HTML page below.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>HTML + CSS</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <h1>@import CSS at-rule</h1>
    <p>The @import rule is used to import external stylesheet in a CSS file.</p>
  </body>
</html>

Below is the code for the header.css

h1 {
  text-decoration: underline;
  color: tomato;
}

Below is the code for the paragraph.css

p {
  color: navy;
}

We would import these external stylesheets into the only linked stylesheet, styles.css.

@import url(header.css);
@import url(paragraph.css);

/* Additional styles can be added here */
body {
  font-family: Arial, sans-serif;
  margin: 20px;
}

Below is the output:

import

If you study the code blocks and the output above, you will notice that the only linked stylesheet is styles.css and how this rule is used to import external CSS files into it, carefully placing the rule at the beginning of the stylesheet.

As seen in this example, you will understand why I mentioned that this rule is useful for large projects where styles need to be segmented into different files for better maintenance and readability.

For more details, refer to the MDN documentation.

Block CSS At-Rules

As the name implies, they are CSS At-rules containing a block statement represented by curly braces "". These braces can include statements or other at-rules. Some block rules are conditional because they are applied within a specific condition.

General syntax:

/* standalone statements within a stylesheet */
@identifier {
  /* write your statements here*/
}
/* OR */

/* inside of a condition */
@identifier condition {
  /* statement go here */
}

Now, let’s explore the Block CSS At-rules that will help level up your design game.

@media

The @media rule is popularly known as media queries. It applies styles only when a specific media query condition is true. This is essential for responsive design, which allows you to tailor the appearance of your site to different devices and screen sizes.

The basic syntax for @media:

@media (media-feature) {
/* CSS rules go here */
}

The media-feature specifies the condition (e.g., min-width, max-width) that must be met for the styles to be applied.

The @media rule is supported in all modern browsers.

Use Case

In this example, we will create a simple HTML structure and apply different styles for smaller and larger screens using @media.

Below is the HTML code.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Responsive Design with @media CSS at-rule</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class="container">
      <div class="box">Blog 1</div>
      <div class="box">Blog 2</div>
      <div class="box">Blog 3</div>
    </div>
  </body>
</html>

Let’s apply some styling to the HTML code and implement the media query to demonstrate its use.

body {
  font-family: Arial, sans-serif;
  background-color: white;
  margin: 0;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

.container {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  width: 80%;
}

.box {
  background-color: teal;
  color: white;
  padding: 20px;
  margin: 10px;
  text-align: center;
  flex: 1;
}

/* Media Query for screens 600px and below */
@media (max-width: 600px) {
  body {
    background-color: whitesmoke;
 }

  .container {
    flex-direction: column;
    align-items: center;
 }

  .box {
    background-color: gray;
    color: cyan;
    width: 100%;
    margin: 5px 0;
 }
}

/* Media Query for screens 360px and below */
@media (max-width: 360px) {
  .box {
    background-color: darkcyan;
    color: lightyellow;
 }
  .container {
    width: 60%;
 }
}

Note that you can add more media queries for different screen sizes, but the order in which they are arranged is important depending on the media feature you use.

For example, if you write your media queries using max-width, all your max-width media queries should be written in descending order. This is because max-width applies styles to screens of the specified width and below. On the other hand, if you choose to write your media queries using min-width, all your min-width media queries should be written in ascending order. This is because min-width applies styles to screens from the specified width and up.

As the example above shows, the max-width media queries are written in descending order, starting from the larger screens and moving to the smaller screens.

For a broader understanding of CSS media queries, refer to this article.

Below is the default output of this example:

media query larger screen

After applying media queries for smaller screens at 600px or below, this is the output:

media query smaller screen

Finally, after applying media queries for screens at 360px or below, this is the output:

mobile view

In this example, the @media rule is used to apply different styles based on the screen width, using max-width as the media feature. For screens smaller than 600px, the background color of the body changes to whitesmoke, the .container changes its flex-direction to column, and the .box class changes its background color and width. For screens smaller than 360px, the .box class changes its background color and text color, and the .container changes its width.

By arranging the media queries in this order, you ensure that the styles are applied as intended.

For more details, refer to the MDN documentation.

@supports

The @supports rule lets you write CSS that applies styles based on the browser’s support for specific CSS features or properties.

The basic syntax for @supports:

@supports (property: value) {
    /* CSS rules go here */
}

The property: value specifies the CSS property and value to check for support.

The @supports rule is well-supported across all modern browsers.

Use Case

In this example, we would use @supports to apply styles based on the browser’s support for display: grid and scroll-timeline.

Below is the HTML code.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>HTML and CSS</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <h1>@supports CSS at-rule</h1>
    <p>This example will be used to demonstrate how @supports works</p>
  </body>
</html>

Below is the code for the CSS stylesheet:

@supports (display: grid) {
  p {
    font-size: 1rem;
    color: blue;
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 10px;
 }
}

Below is the output:

support

In this example, the styling applied to the paragraph tag will run because every modern browser supports display: grid.

The styling won’t be applied if you try using an abstract or an unsupported rule.

Let’s demonstrate this by using the @supports rule to check if the latest version of Chrome supports scroll-timeline.

Using the same HTML structure, let’s replace our stylesheet with this instead:

@supports (scroll-timeline: auto) {
  p {
    font-size: 1rem;
    color: green;
 scroll-timeline: auto;
 }
}

In this example, the paragraph will only turn green if the browser supports the scroll-timeline property. Since this property is not widely supported, the style changes will not be applied.

Below is the output:

not support

Alternatively, you can check which browsers support or do not support certain features by using websites like caniuse.com. But this rule is handy if you intend to check what your browser supports without referring to any website.

For more details, refer to the MDN documentation.

@keyframes

The @keyframes rule is the powerhouse of CSS animations. It defines our animations and tells CSS which properties to change, when, and how.

The basic syntax for @keyframes:

@keyframes animation-name {
 from {
    /* CSS properties */
 }
 to {
    /* CSS properties */
 }
}

/* or with percentage-based for more intermediate keyframes */

@keyframes animation-name {
 0% {
    /* CSS properties */
 }
 50% {
    /* CSS properties */
 }
 100% {
    /* CSS properties */
 }
}

where animation-name is the animation’s name, from and to are the keywords representing the animation’s starting (0%) and ending (100%) points. The percentage values (e.g., 0%, 50%, 100%) help define more intermediate values.

The @keyframes rule is supported in all modern browsers.

Use Case

In this example, we will create an animating box element that moves infinitely from left to right as its background color changes.

Below is the HTML code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>CSS Animations with @keyframes CSS at-rule</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class="box"></div>
  </body>
</html>

Below is the code for the CSS stylesheet:

body {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  margin: 0;
  background-color: #f0f0f0;
}

.box {
  width: 100px;
  height: 100px;
  background-color: red;
  animation: colorBox 3s infinite;
}

@keyframes colorBox {
 0% {
    transform: translateX(0);
    background-color: red;
 }
 50% {
    transform: translateX(300px);
    background-color: blue;
 }
 100% {
    transform: translateX(0);
    background-color: yellow;
 }
}

Below is the output:

CSSAnimationswithkeyframesCSSat-rule-GoogleChrome2024-07-2813-47-57-ezgif.com-crop

We used the animation property to apply the colorBox animation. The animation lasts 3 seconds (3s) and repeats infinitely (infinite).

Keyframes have many other important use cases, such as creating smooth transitions for hover effects, animating logos, and enhancing user interactions on web pages. Try implementing keyframes to make your web pages more interactive and visually appealing.

For more details, refer to the MDN documentation.

@layer

The @layer rule, also known as cascade layers, allows you to organize styles into named layers. These layers influence the order in which styles are applied and help resolve specificity conflicts. Before this rule, we relied on selector specificity or the !important flag to avoid conflicting styles, which is not advisable in larger projects.

The basic syntax for @layer:

@layer layer-name {
  /* CSS rules go here */
}

Note that the first declared layer gets the lowest priority, and the last gets the highest priority. Therefore, you can re-organize your layer declarations or set the order of the layers using the @layer directive, which follows this syntax:

@layer layer-name, layer-name, layer-name;

Since this is a directive, it must be placed at the top of your stylesheet and before any @import statements and other block CSS At-rules. This ensures this rule is applied throughout the stylesheet and other imported stylesheets.

The @layer rule is supported in all modern browsers.

Use Case

In this example, we create a .btn class for all buttons. However, the Read more button does not inherit this styling, though it shares the same class name; this is because there is a conflicting style between the .btn selector and the selector that targets the anchor tags. Using the !important flag could solve this, but it would escalate the problem of conflicting styles in the future, resulting in a messy stylesheet full of !important flags.

Below is the HTML Code.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@layer rule</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <nav>
      <a href="#" class="btn">Login</a>
    </nav>

    <h2>Selector Specificity</h2>
    <main>
      <section class="main-content">
        <p>We will use `@layer` to resolve this styling conflict.</p>
        <a href="#" class="btn">Read More</a>
      </section>
    </main>
  </body>
</html>

Below is the code for the CSS Stylesheet:

/* layout */
body {
  margin: 0;
  max-width: 600px;
  margin: 0 auto;
  padding: 15px;
  background: whitesmoke;
}

/* content */
.main-content a {
  color: red;
  background: transparent;
  text-decoration: underline;
}

/* component */
.btn {
  background: blue;
  color: #fff;
  padding: 8px 12px;
  border-radius: 8px;
  text-decoration: none;
  display: inline-block;
}

Below is the output without using layers:

styling without layers

Let’s use the @layer rule to solve this selector specificity problem. Using the same HTML structure, let’s replace our stylesheet with this instead:

@layer layout {
  body {
    margin: 0;
    max-width: 600px;
    margin: 0 auto;
    padding: 15px;
    background: whitesmoke;
 }
}

@layer content {
  .main-content a {
    color: red;
    background: transparent;
    text-decoration: underline;
 }
}

@layer component {
  .btn {
    background: blue;
    color: #fff;
    padding: 8px 12px;
    border-radius: 8px;
    text-decoration: none;
    display: inline-block;
 }
}

Below is the output using layers:

styling with layers

By organizing styles into layers, we ensure that the .btn class styles are applied correctly, resolving any specificity conflicts.

As mentioned earlier, you can further control the layer order by setting the arrangement using the @layer directive, which should be placed at the top of the stylesheet. For example:

@layer layout, component, content;

By using this directive you would ensure that content takes the highest priority, followed by layout, and then component.

Using the @layer rule, you can avoid specificity conflicts and maintain a clean, manageable stylesheet.

For more details, refer to the MDN documentation.

Conclusion

CSS At-rules enhances your ability to create flexible, responsive, and maintainable stylesheets. Throughout this article, we explored various at-rules and their practical applications in web development. You have learned how to use them to ensure that your stylesheets effectively deliver the desired visual outcomes and are easier to manage and scale as your projects grow. Now it’s time to elevate your web design skills and optimize your stylesheet using these at-rules.

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay