CSS :has() & Responsive Design

The CSS :has() pseudo-class was shipped for Safari in March 2022 and for Chromium browsers the same August. It's described on the Webkit blog as a way to "apply CSS to an element based on what’s happening inside that element" - so if I was to try and assign it some kind of pseudocode equivalent, an if contains.

The demands of responsive design by nature require flexibility, so the conditional qualities that :has() offers is a great addition to the CSS toolset and widens our options in aiming to achieve this kind of flexibility.

Towards the end of 2022 I was working on applying responsive design principles to the primary apps of a client suite of web apps.

In the interfaces, we have quite a few places where we have a heading aligned with some action buttons (shown here as the 'My latest devices' heading):

 

CURRENT DESKTOP HEADER LAYOUT

The heading and buttons collectively make up our React component Header, which accepts both as props.

On mobile devices, this can end up looking pretty congested - text bunched up together, and tall buttons where the icon looks a little out of place.

 

 

 

 

PREVIOUS MOBILE HEADER LAYOUT - CROWDED

Here’s what I tried at first in an effort to solve the problem:

Different button

I thought at first that we could make the buttons into a single ‘actions menu’ sort of button, before realizing that on some pages, there was a similar button just above. Having two lots of "actions" could be confusing, and even though on mobile, one would have to scroll down to see the second button and then the first one would be out of view; it wouldn't completely dispel the risk of things getting mixed up, and probably isn't best practice.

 

TWO ‘ACTIONS’ BUTTONS WASN’T GOING TO BE AS USABLE
 

Keeping these buttons, but just applying flex-direction: column so they would sit nicely underneath the heading

I then considered simply changing the main container for Header to use flex-direction: column for smaller screens, so the elements could all have some breathing room, like this:

 

COLUMN LAYOUT GAVE MULTIPLE BUTTONS SOME SPACE

That also changed the headers that just had one button, like the one adjacent to the Actions button shown above. Perhaps this is not so bad, but it was not desired. An 'actions' menu aligned next to the heading seemed most suitable, even on a small screen.

 

NOT AS DESIRED JUST FOR A SINGLE BUTTON

 

DESIRED

I also tried to implement a JavaScript solution where I added a class based on the conditions I was looking for, but the way that the header component and its props had been implemented meant it was difficult to avoid unnecessary complexity.

CSS :has() saves the day

After deciding CSS was probably the only way, I thought again about what I needed: a style that exists only where there are more than two buttons in the header. A style when a header only has two buttons.

There had been a lot of hype around CSS's :has() in my weekly dev newsletters, but even so, I wasn't sure in detail what it did - I wasn't sure it could solve this problem. After some Googling and going back to those articles, I learned that it was a "way to apply CSS to an element based on what’s happening inside that element."

At first-glance though, :has() didn't seem very complex, or powerful. I had questions I wanted :has() to answer: "Does my div have more than one button?", namely. Even though it's called :has() rather than :have, I was determined that it could answer this question for me, despite not seeing the examples that I needed immediately.

An explanatory video

Understanding CSS:has(), the top result when searching for "css has" on YouTube, really breaks down just what you can do with this seemingly humble pseudo-classs.

Towards the close of the video it presented some pretty unreadable code, but the use of the already familiar nth-child caught my eye.

The video presented a way to colour the background of alternative rows in a table:

tbody:has()(> tr:nth-child(7)) > tr:nth-child(odd) { background: silver; }

Admittedly, it took me a moment to understand this code. What is happening here?

The tbody:has()(> tr:nth-child(7)) looks for the element which has a direct descendant (> being the child combinator) which fits the description tr:nth-child(7). In other words, it's looking for a table which has more than 6 rows. There's another child combinator after that to select any row which is an odd-number child, of that same table.

So essentially, highlight in silver any odd-numbered row that's a child of any table, as long as that table has more than 6 rows. This shows how powerful :has() really is, and how it can be combined with other features of the language to achieve this power.

How I applied that to my responsive design

As explained above, the criteria I wanted to apply when it came to ordering my header buttons underneath the heading text was "the header has (within it) more than one button". I could create that criteria with this code:

.header:has()(button:nth-child(2)) { flex-direction: column; text-align: center; h2 { padding-bottom: $spacing-sm; } }

And create the design I desired once again.

 

FINAL MOBILE HEADER LAYOUT

Concluding thoughts

On the WebKit blog, Jen Simmons writes, “It’s been a long-standing dream of front-end developers to have a way to apply CSS to an element based on what’s happening inside that element.” Using has() in the scenario above, avoiding either a more convoluted solution or a restricted design has allowed me to explore why this is such a helpful development. I imagine it will provide simpler solutions for responsive design problems and a variety of other styling obstacles for time to come.

AdobeStock, zwei Männer im Büro im Austausch
Success Story

Lehmann&Voss: Successful transition to the cloud

With the introduction of M365, Lehmann&Voss has embarked on its digital journey to the cloud. A change and adoption strategy was central to the successful implementation.

Foto des Gebäudes der Schwäbisch Hall vor blauem Himmel
Success Story

Schwäbisch Hall: A modern workplace in the cloud with M365

With the introduction of M365, Schwäbisch Hall can benefit from the wide range of possibilities on offer and has thus laid the foundations for a modern digital working environment.

Blog 5/25/21

From the idea to the product: The genesis of Skwill

We strongly believe in the benefits of continuous learning at work; this has led us to developing products that we also enjoy using ourselves. Meet Skwill.

Blog 11/12/20

Announcing Domain-Driven Design Exercises

Interested in Domain Driven Design? Then this DDD exercise is perfect for you!

Blog 7/14/21

Building and Publishing Design Systems | Part 2

Learn how to build and publish design systems effectively. Discover best practices for creating reusable components and enhancing UI consistency.

Blog 7/13/21

Composite UI with Design System and Micro Frontends

Discover how to create scalable composite UIs using design systems and micro-frontends. Enhance consistency and agility in your development process.

Blog 11/15/22

5 lessons from running a (remote) design systems book club

Last year I gifted a design systems book I had been reading to a friend and she suggested starting a mini book club so that she’d have some accountability to finish reading the book.

Blog 7/15/21

Building a micro frontend consuming a design system | Part 3

In this blopgpost, you will learn how to create a react application that consumes a design system.

News 11/13/24

25 years of catworkx

25 years of catworkx – a quarter of a century of success and innovation! We celebrated this special anniversary in style on a Danube cruise: from Passau via Vienna to Budapest and back, we took time to look back together on 25 years of developments and successes. A big thank you to our fantastic team and everyone who has joined us on this journey – we look forward to the next 25 years with you!

Standort 11/17/20

Our Sites

We are your on-site digitization partner: Our locations in Germany, Austria, Switzerland, Latvia and Malaysia.

Vernetzte Landschaft mit Sonnenuntergang – Digitalisierung in Energieversorgung und Smart Grids.
Branche

Energy Industry

From billing to archiving: IT projects for the energy industry

Kompetenz 7/18/23

Advisory

Support your business goals in the best possible way ►Digital strategy & IT strategy ✅ We will be happy to advise you.

Technologie 1/19/21

Hyland

We are partners with Hyland. Hyland is a Gartner and Forrester award-winning leader in content services, enabling thousands of organizations to deliver better experiences for people.

News 9/29/22

TIMETOACT GROUP acquires monitoring specialist OpenAdvice

We expand our offer in application performance monitoring and business service assurance with acquisition of OpenAdvice IT Services GmbH

News 1/26/21

The IPG Group becomes part of the TIMETOACT GROUP

The TIMETOACT GROUP acquires the majority of the shares of IPG Information Process Group Holding AG, based in Winterthur. Through the acquisition, the competencies for Identity and Access Management (IAM) solutions in the DACH market are combined.

Kompetenz

Green IT: Your Status Quo Assessment for Sustainable IT

IT sustainability: Efficient resource use, reduced energy consumption, and extended device lifespan for lower CO2 emissions and costs.

Logo von IPG - Experts in IAM
Unternehmen 1/26/21

IPG - Identity and Governance Management

IPG Group specializes in the design, integration, operation and training of IAM solutions.

Kompetenz

Business Innovation & Digital Transformation

Utilise digital technologies to improve processes, make data-driven decisions and develop new business models. Without losing sight of governance, risk and compliance.

Kompetenz

Artificial Intelligence & Data Strategy

Every company collects and manages vast amounts of data, e.g. from production processes or business transactions.

News 2/1/23

We are offering cloud-based SAP solutions consulting now!

TIMETOACT GROUP expands into cloud-based SAP solutions consulting with the acquisition of WCA Walldorf Consulting and target Software Solution

Bleiben Sie mit dem TIMETOACT GROUP Newsletter auf dem Laufenden!