- CSS :has() feature detection with @supports(selector(…)) : You want :has(+ *) , not :has(*)
- # The Problem with :has(*)
- # Full Code
- # TL;DR
- # Spread the word
- Published by Bramus!
- Join the Conversation
- Leave a comment
- Cancel reply
- Css Css Has Not
- How and when to use the CSS :has selector
- :not()
- Getting the error «Nested CSS was detected, but CSS nesting has not been
- CSS :not() with Multiple Classes
- CSS :Not attribute [duplicate]
CSS :has() feature detection with @supports(selector(…)) : You want :has(+ *) , not :has(*)
To feature detect browser support for the CSS :has() selector, you can use @supports(selector(…)) . When doing so, it is important to include a valid selector as its argument. As I’ve tweeted before, you must pass a selector such as * into :has() when used in a feature query .
/* ❌ This will always evaluate to false */ @supports selector(:has()) < … >/* ✅ This will evaluate to true in browsers that support :has() */ @supports selector(:has(*))
💁♂️ Initially this selector requirement was only the case in Safari, but this has since been adjusted at the spec level making it a requirement in all other browsers.
I use this technique to conditionally show a warning in many of my :has() demos.
/* Style warning block */ .no-support < margin: 1em 0; padding: 1em; border: 1px solid #ccc; background-color: #ff00002b; display: block; >/* Hide warning block in case :has() support is detected */ @supports selector(:has(*)) < .no-support < display: none; >>
If your browser has no support, you get to see a warning message telling you about it. This is the case in Firefox which, at the time of writing, does not support :has() out of the box just yet.
# The Problem with :has(*)
While the approach above does allow you to feature detect :has() , it is not 100% closing . The culprit here is Firefox, which currently has experimental support behind a feature flag.
When flipping the layout.css.has-selector.enabled flag on, Firefox will correctly claim support when using @supports selector(:has(*)) . This, however, does not account for the fact that this experimental implementation does not support relative selector parsing (yet) .
UPDATE 2023.03.07 – As of March 2023, Firefox has support for relative selectors inside :has() so regular feature detection works just fine now. However, do note that the :has() implementation in Firefox is not finalized yet, as style invalidation still needs to be tackled. See this comment for some more info.
As many of my demos use relative selectors within :has() , I do want a warning to be shown even when the flag is flipped on. The solution here is to actually use a relative selector such as + * as the argument to :has() .
/* Hide warning block in case :has() support – including relative selectors – is detected */ @supports selector(:has(+ *)) < .no-support < display: none; >>
# Full Code
The full code used in my demo looks like this. It can detect both levels of support and also allows showing a certain box in case support is claimed.
🚨 Your browser does not support CSS :has()
, so this demo will not work correctly.
🚨 Your browser does not support relative selectors in CSS :has()
, so this demo will not work correctly.
.no-support, .has-support < margin: 1em 0; padding: 1em; border: 1px solid #ccc; >.no-support < background-color: #ff00002b; display: block; >.has-support < background-color: #00ff002b; display: none; >@supports selector(:has(*)) < .no-support[data-support="css-has-basic"] < display: none; >.has-support[data-support="css-has-basic"] < display: block; >> @supports selector(:has(+ *)) < .no-support[data-support="css-has-relative"] < display: none; >.has-support[data-support="css-has-relative"] < display: block; >>
# TL;DR
If you’re feature detecting :has() with @supports you must pass a selector into :has() . This can be * but if your code relies on relative selectors used inside :has() , use @supports selector(:has(+ *)) instead. This must be done to filter out Firefox visitors who have flipped on the experimental :has() support which currently lacks support for relative selectors.
# Spread the word
To help spread the contents of this post, feel free to retweet its announcement tweet:
🔥 Like what you see? Want to stay in the loop? Here’s how:
Published by Bramus!
Bramus is a frontend web developer from Belgium, working as a Chrome Developer Relations Engineer at Google. From the moment he discovered view-source at the age of 14 (way back in 1997), he fell in love with the web and has been tinkering with it ever since (more …) View more posts
Unless noted otherwise, the contents of this post are licensed under the
Creative Commons Attribution 4.0 License and code samples are licensed under the
Join the Conversation
Thanks for helping us keep on top of things, as usual, Bramus! Looks like this is throwing a false positive in Firefox 112, CodePen for testing: https://codepen.io/5t3ph/pen/qBJPjad If you come up with the right combo, let me know!
Technically it’s not a false positive as Firefox does support relative selector matching by now – see bug https://bugzilla.mozilla.org/show_bug.cgi?id=1774588, which is closed/fixed. However, what’s still an issue is style invalidation. Updates to the DOM or CSS don’t visually update things matched by `:has()` selectors with relative selectors. See my demo at https://cdpn.io/pen/debug/YzvowwJ for example. It kinda works, but you often need to force a style recalc by toggling the `.special` class off and on again – `$0.classList.toggle(‘special’); $0.offsetLeft; $0.classList.toggle(‘special’); `
Leave a comment
Cancel reply
This site uses Akismet to reduce spam. Learn how your comment data is processed.
Css Css Has Not
The :has selector is part of the CSS Selectors Level 4 specification which is the same spec that has the extremely useful :not pseudo-class. You could argue that the CSS :has selector is more powerful than just a “parent” selector, which is exactly what Bramus has done!
div:has(p) < background: red; >/* */ h2, .subtitle < margin: 0 0 1.5rem 0; >.header-group:has(h2):has(.subtitle) h2 < margin: 0 0 0.2rem 0; /* reduce spacing on header, because subtitle will handle it */ >/* Blog Post Title Blog Post Title This is a subtitle */ .parent .child < color: red; >a:has(> img) < border: 20px solid white; >/* Matches elements that have a as a child element */ figure:has(figcaption) < … >/* Matches
elements that is a child of a that contains a child element */ figure:has(figcaption) img < … >article:has(h2):has(ul) < >article:has(h2, ul) < >article:has(h2, ul, ::-blahdeath) < /* the invalid ::-blahdeath in there is just ignored, so it's treated like :has(h2, ul) */ >@supports(selector(:has(p))) < /* Supported! */ >ul li:not(:first-of-type) < color: red; >ul li:not(:last-of-type) < margin-bottom: 20px; >:is(section, article, aside, nav) :is(h1, h2, h3, h4, h5, h6) < color: #BADA55; >/* . which would be the equivalent of: */ section h1, section h2, section h3, section h4, section h5, section h6, article h1, article h2, article h3, article h4, article h5, article h6, aside h1, aside h2, aside h3, aside h4, aside h5, aside h6, nav h1, nav h2, nav h3, nav h4, nav h5, nav h6 < color: #BADA55; >body:has(.printarea) < // Content creator has provide a div with a class of "ptintarea" they want printed. Hide everything else. >A:has(B.red) < . >A:has(B.green) < . >A:has(B.blue) < . >div:has(:focus) <> div:has(:hover) <> div:has(:target) <> How and when to use the CSS :has selector
When to use CSS :has() Usually, when we write CSS rules that target HTML elements, the browser’s engine evaluates the rules from right to left to apply them. For instance, the following CSS rule targets the
element inside the : section p < color: red; >Now, let’s say we want to target an element based on its content or
:not()
The :not () CSS pseudo-class represents elements that do not match a list of selectors. Since it prevents specific items from being selected, it is known as the negation pseudo-class. / Selects any element that is NOT a paragraph / :not (p) < color: blue; >The :not () pseudo-class has a number of quirks, tricks, and unexpected results that
/* Selects any element that is NOT a paragraph */ :not(p) < color: blue; >:not( ) I am a paragraph.
I am NOT a paragraph.
.fancy < text-shadow: 2px 2px 3px gold; >/* elements that don't have a class `.fancy` */ p:not(.fancy) < color: green; >/* Elements that are not
elements */ body :not(p) < text-decoration: underline; >/* Elements that are not
and not elements */ body :not(div):not(span) < font-weight: bold; >/* Elements that are not s or `.fancy` */ body :not(div, .fancy) < text-decoration: overline underline; >/* Elements inside an that aren't a with a class of `.foo` */ h2 :not(span.foo) < color: red; >/* Selects any element that is NOT a paragraph */\n:not(p) \n :not( )\n I am a paragraph.
\nI am NOT a paragraph.
\n\n \n \n
\n .fancy \n\n/* elements that don't have a class `.fancy` */\np:not(.fancy) \n\n/* Elements that are not
elements */\nbody :not(p) \n\n/* Elements that are not
and not elements */\nbody :not(div):not(span) \n\n/* Elements that are not s or `.fancy` */\nbody :not(div, .fancy) \n\n/* Elements inside an that aren't a with a class of `.foo` */\nh2 :not(span.foo) \n Getting the error «Nested CSS was detected, but CSS nesting has not been
configured correctly» in React app?
This is mostly just bad news. Create React App’s Tailwind support means that they will detect tailwind.config.js in the project and add tailwindcss to their existing postcss configuration.Source in CRA. The guide that Tailwind offers on their site creates a dummy postcss.config.js — Making changes in this file does not change the actual postcss configuration.
(8:3) Nested CSS was detected, but CSS nesting has not been configured correctly. Please enable a CSS nesting plugin *before* Tailwind in your configuration. See how here: https://tailwindcss.com/docs/using-with-preprocessors#nesting module.exports = < plugins: < "tailwindcss/nesting": <>, tailwindcss: <>, autoprefixer: <>, >, >; import React from "react"; import ReactDOM from "react-dom"; import "./index.css"; ReactDOM.render( aaa bbb , document.getElementById("root") ); @tailwind base; @tailwind components; @tailwind utilities; .a < @apply text-blue-500; .b < @apply text-green-500; >> < "name": "tailwindtest", "dependencies": < "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "5.0.0" >, "scripts": < "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" >, "devDependencies": < "autoprefixer": "^10.4.2", "postcss": "^8.4.5", "tailwindcss": "^3.0.12" >> npm run eject FILE="node_modules/react-scripts/config/webpack.config.js" function replace < TARGET_FILE=$1 PATTERN_TO_FIND=$2 VALUE_FOR_REPLACEMENT=$3 OLD_FILE_CONTENT=$(cat "$TARGET_FILE") # we need to collect the content of the file so we can overwrite it in the next command echo "$OLD_FILE_CONTENT" | sed -e "s/$PATTERN_TO_FIND/$VALUE_FOR_REPLACEMENT/g" >"$TARGET_FILE" > # add postcss-nesting replace "$FILE" "'postcss-flexbugs-fixes'," "'postcss-flexbugs-fixes','postcss-nesting'," # add tailwind/nesting replace "$FILE" "'tailwindcss'," "'tailwindcss\/nesting', 'tailwindcss'," "scripts": < "postinstall": "node script.js", . >const fs = require('fs'); fs.readFile('node_modules/react-scripts/config/webpack.config.js', 'utf8', (err, data) => < if (err) < return console.log(err); >const result = data.replace("'postcss-flexbugs-fixes',", "'postcss-flexbugs-fixes','postcss-nesting',").replace("'tailwindcss',", "'tailwindcss/nesting', 'tailwindcss',"); fs.writeFile('node_modules/react-scripts/config/webpack.config.js', result, 'utf8', (err) => < if (err) < return console.log(err); >return console.log(true); >); return console.log(true); >);
CSS :not() with Multiple Classes
If the tag is “body” and the class list does not include “home” and the class list does not include “away” and the class list does not include “page-50”, then … Of course using the not yet widely available level 4 selectors (body:not(.home, .away, .page-50) would be simpler:
body:not(.home) < >body:not(.home):not(.away):not(.page-50) < >.headline:not(h1, h2, h3) < . >#some-id *, #some-id *:not(div), #some-id *:not(svg *) < stuff >html:not('.foo'), html:not('.bar')
CSS :Not attribute [duplicate]
Other tables don’t have the width attribute. Using only CSS I need to set the width of the . Stack Overflow. About; Products For Teams; Stack Overflow Public questions & answers; Stack Overflow for Teams Where developers & technologists share private knowledge with …
table:not(width="100%") < width: myValue; >table:not([width="100%"]) < width: myValue; >table:not(.foo) table < width:100%; >table.customWidth