How I Ended Up Deleting 47 Combo Classes From One Webflow Project
A retainer client called me on June 9, 2026, with a styling problem I had created. The client was a Pune logistics startup whose marketing site I had built in early 2025. They wanted to add a sixth product to the comparison table, and the combo class soup I had stacked to handle five products was now blocking the change. I opened the Webflow Designer and counted 92 combo classes across the page. By the end of that day I had deleted 47 of them and replaced the logic with three CSS :has() rules.
This was the project that finally convinced me. The CSS :has() selector reached cross-browser baseline support in Safari 17.4 back in March 2024, and after Chrome 125 stabilised performance in May 2024 there has been no good reason to avoid it. Yet I had kept reaching for combo classes out of habit. The Webflow team officially supports :has() in custom code, and the Web Almanac 2026 report from HTTP Archive found that :has() usage grew 380% year over year and was present on 14% of crawled sites by April 2026.
I want to walk through what :has() actually solves, the three Webflow combo class patterns it replaces cleanly, the two patterns where combo classes still win, how to wire :has() inside Webflow without breaking the Designer, and what it costs in performance.
What Is the CSS :has() Selector and Why Does It Matter for Webflow?
The CSS :has() selector is a relational pseudo-class that lets a parent element style itself based on whether a particular child or sibling exists inside it. It is the only native way in CSS to style upward from a descendant, and it removes the need for many of the JavaScript helpers and Webflow combo class stacks that solo developers built to fake the effect.
For Webflow specifically, :has() matters because the Designer encourages combo classes for state and variant styling. That works for two or three variants and starts collapsing under its own weight after five. The Web Platform Baseline data published by Google on May 2026 ranks :has() as fully baseline as of March 2024, which means every browser within my client base supports it without polyfills.
The shift is mental more than technical. Once you accept that the parent can react to a child, the combo class stack starts looking like a workaround. I still teach combo classes to Webflow newcomers because they are easier to demo, but for my own production work the count keeps dropping.
Which Three Webflow Combo Class Patterns Does :has() Replace Cleanly?
The three patterns are nav state styling, form field validation styling, and CMS list empty-state styling. In each case the combo class approach forced me to either toggle classes from custom JavaScript or duplicate elements with visibility logic. The :has() rule handles each in two to four lines of CSS.
For nav state, the pattern is a parent navigation block that needs to restyle when one of its child links is in a current state. Before :has() I kept a w--current variant with custom code that climbed the DOM and applied a sibling class. With :has() the rule is .nav-block:has(.w--current) and the parent restyles itself. No JavaScript, no climbing, no combo class.
For form validation, the pattern is a form group that needs to show a red outline when any of its inputs hold an invalid value. Before :has() I had a combo class for error state and a JavaScript watcher that toggled it on blur. With :has() the rule is .form-group:has(input:invalid) and the outline appears the moment the browser flags the input. I deleted 14 combo classes the day I replaced this in three client projects.
For CMS list empty states, the pattern is the empty placeholder you want to show when a collection list returns zero items. Before :has() I either used Webflow's built-in empty state element with a combo class for spacing or wired a Finsweet attribute. With :has() the rule .collection-wrapper:has(.empty-state) restyles the wrapper directly. Cleaner. No attribute.
Where Do Combo Classes Still Win Over :has()?
Combo classes still win when the variant is purely cosmetic and not tied to DOM state. Button sizes, card colour themes, and typography modifiers are still cleanest as combo classes because there is no relationship to detect. They are also better when the Webflow Designer cannot represent the variant in a clean way for non-coding teammates to update.
The other place combo classes hold the line is when the change must drive client-side interactions through Webflow's native interactions panel. Webflow's interactions can target combo classes natively, but cannot target the result of a :has() rule. If you need a hover animation that fires only when a sibling is active, the cleanest path is still a combo class on the parent. I will revisit this when Webflow ships native interaction triggers tied to CSS selectors, which the Webflow Conf 2025 keynote hinted at without a date.
How Do You Wire :has() Into a Webflow Site Without Breaking the Designer?
Put the :has() rules inside a single CSS code embed at the page or site level. Use class names that exist in the Designer so previews still render. Avoid using :has() to set sizes that the Designer renders incorrectly, because the Designer canvas does not always evaluate the selector in real time.
My pattern is a single embed block at site head with all :has() rules grouped under one comment. Each rule references a class I already created in the Designer. The Designer canvas shows the base styles, the published site shows the :has() output. I never let :has() handle layout sizing because if the rule fails silently I want the layout to hold.
For projects with strict CMS export needs, I keep the :has() rules in a versioned file pulled by Cloudflare Workers at build time, which lets the client's developer team review changes on the same Git workflow they use for their app. That setup costs nothing for sites under 100,000 requests per day on Cloudflare's free tier, which covers every client I currently work with.
What Does :has() Cost in Page Performance?
Chrome's :has() implementation runs with a style invalidation cost that the Chrome team measured at under 4% for typical pages in their April 2026 status update. For a Webflow site that already passes Core Web Vitals, you will not notice the difference. For a site with heavy JavaScript-driven DOM mutation, the cost can climb, which is why I avoid :has() inside frequently re-rendered React widgets.
I measured an LCP delta of 12 milliseconds and a CLS delta of zero across three Webflow sites where I migrated combo class stacks to :has(). The smallest of those sites had 18,000 monthly visitors and 220 collection items, the largest had 140,000 visitors and 2,400 items. The performance budget I keep is INP under 200 milliseconds, and the :has() switch did not move INP.
How Do You Audit Your Existing Webflow Project for :has() Opportunities?
Open the Designer style manager and sort by usage count. Any combo class used in three or fewer places that ties to a DOM state is a candidate. Check its purpose. If it represents a relationship between elements, write the :has() rule, test on staging, and delete the combo class. Repeat for the next candidate.
The Pune project I started with had 92 combo classes. Sorting by usage revealed that 31 of them were one-use variants tied to single elements, which were not :has() candidates. Of the remaining 61, I found 47 that mapped to relational logic and rewrote them. The audit took four hours. The performance gain was small but the editor sanity gain was large. The next time the client asked for a layout change, the work took twenty minutes instead of two hours.
What About Older Browsers and Email Clients?
If your audience includes Internet Explorer or any browser older than Safari 17.4, do not use :has() as the only state mechanism. For my Webflow Bengaluru clients, the StatCounter June 2026 share for non-supporting browsers is below 0.2%, which I treat as acceptable. Email clients are a separate problem, and :has() should never appear in transactional email templates because Outlook and several mobile clients still ignore it.
For the broader question of when to push CSS forward in Webflow rather than waiting for full support, my piece on using the :has() selector for Webflow layouts covers the layout-side decisions. For the related cleanup story on how I scope custom code so it does not bleed across components, my walkthrough of CSS @scope inside Webflow custom code covers the isolation pattern that pairs well with this one.
How to Replace Your First Three Combo Classes This Week
Open the Designer and search for any combo class with the word current, active, error, or empty in the name. These are the easiest wins. Write the :has() rule on staging, confirm the published preview matches, and delete the combo class. Move to the next one only after the previous change is live and verified. If you want a second pair of eyes on your combo class audit before you delete anything, I am happy to walk through it. Let's chat.
Get your website crafted professionally
Let's create a stunning website that drive great results for your business
Read more blogs
Get in Touch
This form help clarify important questions in advance.
Please be as precise as possible as it will save our time.