Tumgik
#:not
stolpnik · 9 years
Link
0 notes
dixie-links · 9 years
Link
0 notes
timerecursion · 10 years
Text
CSS4 Selectors :not()
:not pseudo-class selector is part of the well supported CSS3 standard and received an upgrade in CSS4. The way it works is that you pass a selector to say that you don't want a style applied to this element.
For example. If we want to make all bold text red, except the bold text with a class simple, we could use the following rule:
strong:not(.simple) { color: red; }
In CSS4 we can pass a comma separated list of selectors, giving us greater control and flexibility. Like so:
strong:not(.simple, span) { color: red; }
0 notes
grrrrfuck · 12 years
Text
typical
The :not() selector is only supported by modern browsers (Firefox, Safari and Opera), :not(IE).
0 notes
jcmarchi · 2 months
Text
Pop(over) the Balloons
New Post has been published on https://thedigitalinsider.com/popover-the-balloons/
Pop(over) the Balloons
I’ve always been fascinated with how much we can do with just HTML and CSS. The new interactive features of the Popover API are yet another example of just how far we can get with those two languages alone.
You may have seen other tutorials out there showing off what the Popover API can do, but this is more of a beating-it-mercilessly-into-submission kind of article. We’ll add a little more pop music to the mix, like with balloons… some literal “pop” if you will.
What I’ve done is make a game — using only HTML and CSS, of course — leaning on the Popover API. You’re tasked with popping as many balloons as possible in under a minute. But be careful! Some balloons are (as Gollum would say) “tricksy” and trigger more balloons.
I have cleverly called it Pop(over) the Balloons and we’re going to make it together, step by step. When we’re done it’ll look something like (OK, exactly like) this:
Handling the popover attribute
Any element can be a popover as long as we fashion it with the popover attribute:
<div popover>...</div>
We don’t even have to supply popover with a value. By default, popover‘s initial value is auto and uses what the spec calls “light dismiss.” That means the popover can be closed by clicking anywhere outside of it. And when the popover opens, unless they are nested, any other popovers on the page close. Auto popovers are interdependent like that.
The other option is to set popover to a manual value:
<div popover=“manual”>...</div>
…which means that the element is manually opened and closed — we literally have to click a specific button to open and close it. In other words, manual creates an ornery popup that only closes when you hit the correct button and is completely independent of other popovers on the page.
Using the <details> element as a starter
One of the challenges of building a game with the Popover API is that you can’t load a page with a popover already open… and there’s no getting around that with JavaScript if our goal is to build the game with only HTML and CSS.
Enter the <details> element. Unlike a popover, the <details> element can be open by default:
<details open> <!-- rest of the game --> </details>
If we pursue this route, we’re able to show a bunch of buttons (balloons) and “pop” all of them down to the very last balloon by closing the <details>. In other words, we can plop our starting balloons in an open <details> element so they are displayed on the page on load.
This is the basic structure I’m talking about:
<details open> <summary>🎈</summary> <button>🎈</button> <button>🎈</button> <button>🎈</button> </details>
In this way, we can click on the balloon in <summary> to close the <details> and “pop” all of the button balloons, leaving us with one balloon (the <summary> at the end (which we’ll solve how to remove a little later).
You might think that <dialog> would be a more semantic direction for our game, and you’d be right. But there are two downsides with <dialog> that won’t let us use it here:
The only way to close a <dialog> that’s open on page load is with JavaScript. As far as I know, there isn’t a close <button> we can drop in the game that will close a <dialog> that’s open on load.
<dialog>s are modal and prevent clicking on other things while they’re open. We need to allow gamers to pop balloons outside of the <dialog> in order to beat the timer.
Thus we will be using a <details open> element as the game’s top-level container and using a plain ol’ <div> for the popups themselves, i.e. <div popover>.
All we need to do for the time being is make sure all of these popovers and buttons are wired together so that clicking a button opens a popover. You’ve probably learned this already from other tutorials, but we need to tell the popover element that there is a button it needs to respond to, and then tell the button that there is a popup it needs to open. For that, we give the popover element a unique ID (as all IDs should be) and then reference it on the <button> with a popovertarget attribute:
<!-- Level 0 is open by default --> <details open> <summary>🎈</summary> <button popovertarget="lvl1">🎈</button> </details> <!-- Level 1 --> <div id="lvl1" popover="manual"> <h2>Level 1 Popup</h2> </div>
This is the idea when everything is wired together:
Opening and closing popovers
There’s a little more work to do in that last demo. One of the downsides to the game thus far is that clicking the <button> of a popup opens more popups; click that same <button> again and they disappear. This makes the game too easy.
We can separate the opening and closing behavior by setting the popovertargetaction attribute (no, the HTML spec authors were not concerned with brevity) on the <button>. If we set the attribute value to either show or hide, the <button> will only perform that one action for that specific popover.
<!-- Level 0 is open by default --> <details open> <summary>🎈</summary> <!-- Show Level 1 Popup --> <button popovertarget="lvl1" popovertargetaction="show">🎈</button> <!-- Hide Level 1 Popup --> <button popovertarget="lvl1" popovertargetaction="hide">🎈</button> </details> <!-- Level 1 --> <div id="lvl1" popover="manual"> <h2>Level 1 Popup</h2> <!-- Open/Close Level 2 Poppup --> <button popovertarget="lvl2">🎈</button> </div> <!-- etc. -->
Note, that I’ve added a new <button> inside the <div> that is set to target another <div> to pop open or close by intentionally not setting the popovertargetaction attribute on it. See how challenging (in a good way) it is to “pop” the elements:
Styling balloons
Now we need to style the <summary> and <button> elements the same so that a player cannot tell which is which. Note that I said <summary> and not <details>. That’s because <summary> is the actual element we click to open and close the <details> container.
Most of this is pretty standard CSS work: setting backgrounds, padding, margin, sizing, borders, etc. But there are a couple of important, not necessarily intuitive, things to include.
First, there’s setting the list-style-type property to none on the <summary> element to get rid of the triangular marker that indicates whether the <details> is open or closed. That marker is really useful and great to have by default, but for a game like this, it would be better to remove that hint for a better challenge.
Safari doesn’t like that same approach. To remove the <details> marker here, we need to set a special vendor-prefixed pseudo-element, summary::-webkit-details-marker to display: none.
It’d be good if the mouse cursor indicated that the balloons are clickable, so we can set cursor: pointer on the <summary> elements as well.
One last detail is setting the user-select property to none on the <summary>s to prevent the balloons — which are simply emoji text — from being selected. This makes them more like objects on the page.
And yes, it’s 2024 and we still need that prefixed -webkit-user-select property to account for Safari support. Thanks, Apple.
Putting all of that in code on a .balloon class we’ll use for the <button> and <summary> elements:
.balloon background-color: transparent; border: none; cursor: pointer; display: block; font-size: 4em; height: 1em; list-style-type: none; margin: 0; padding: 0; text-align: center; -webkit-user-select: none; /* Safari fallback */ user-select: none; width: 1em;
One problem with the balloons is that some of them are intentionally doing nothing at all. That’s because the popovers they close are not open. The player might think they didn’t click/tap that particular balloon or that the game is broken, so let’s add a little scaling while the balloon is in its :active state of clicking:
.balloon:active scale: 0.7; transition: 0.5s;
Bonus: Because the cursor is a hand pointing its index finger, clicking a balloon sort of looks like the hand is poking the balloon with the finger. 👉🎈💥
The way we distribute the balloons around the screen is another important thing to consider. We’re unable to position them randomly without JavaScript so that’s out. I tried a bunch of things, like making up my own “random” numbers defined as custom properties that can be used as multipliers, but I couldn’t get the overall result to feel all that “random” without overlapping balloons or establishing some sort of visual pattern.
I ultimately landed on a method that uses a class to position the balloons in different rows and columns — not like CSS Grid or Multicolumns, but imaginary rows and columns based on physical insets. It’ll look a bit Grid-like and is less “randomness” than I want, but as long as none of the balloons have the same two classes, they won’t overlap each other.
I decided on an 8×8 grid but left the first “row” and “column” empty so the balloons are clear of the browser’s left and top edges.
/* Rows */ .r1 --row: 1; .r2 --row: 2; /* all the way up to .r7 */ /* Columns */ .c1 --col: 1; .c2 --col: 2; /* all the way up to .c7 */ .balloon /* This is how they're placed using the rows and columns */ top: calc(12.5vh * (var(--row) + 1) - 12.5vh); left: calc(12.5vw * (var(--col) + 1) - 12.5vw);
Congratulating The Player (Or Not)
We have most of the game pieces in place, but it’d be great to have some sort of victory dance popover to congratulate players when they successfully pop all of the balloons in time.
Everything goes back to a <details open> element. Once that element is not open, the game should be over with the last step being to pop that final balloon. So, if we give that element an ID of, say, #root, we could create a condition to hide it with display: none when it is :not() in an open state:
#root:not([open]) display: none;
This is where it’s great that we have the :has() pseudo-selector because we can use it to select the #root element’s parent element so that when #root is closed we can select a child of that parent — a new element with an ID of #congrats — to display a faux popover displaying the congratulatory message to the player. (Yes, I’m aware of the irony.)
#game:has(#root:not([open])) #congrats display: flex;
If we were to play the game at this point, we could receive the victory message without popping all the balloons. Again, manual popovers won’t close unless the correct button is clicked — even if we close its ancestral <details> element.
Is there a way within CSS to know that a popover is still open? Yes, enter the :popover-open pseudo-class.
The :popover-open pseudo-class selects an open popover. We can use it in combination with :has() from earlier to prevent the message from showing up if a popover is still open on the page. Here’s what it looks like to chain these things together to work like an and conditional statement.
/* If #game does *not* have an open #root * but has an element with an open popover * (i.e. the game isn't over), * then select the #congrats element... */ #game:has(#root:not([open])):has(:popover-open) #congrats /* ...and hide it */ display: none;
Now, the player is only congratulated when they actually, you know, win.
Conversely, if a player is unable to pop all of the balloons before a timer expires, we ought to inform the player that the game is over. Since we don’t have an if() conditional statement in CSS (not yet, at least) we’ll run an animation for one minute so that this message fades in to end the game.
#fail animation: fadein 0.5s forwards 60s; display: flex; opacity: 0; z-index: -1; @keyframes fadein 0% opacity: 0; z-index: -1; 100% opacity: 1; z-index: 10;
But we don’t want the fail message to trigger if the victory screen is showing, so we can write a selector that prevents the #fail message from displaying at the same time as #congrats message.
#game:has(#root:not([open])) #fail display: none;
We need a game timer
A player should know how much time they have to pop all of the balloons. We can create a rather “simple” timer with an element that takes up the screen’s full width (100vw), scaling it in the horizontal direction, then matching it up with the animation above that allows the #fail message to fade in.
#timer width: 100vw; height: 1em; #bar animation: 60s timebar forwards; background-color: #e60b0b; width: 100vw; height: 1em; transform-origin: right; @keyframes timebar 0% scale: 1 1; 100% scale: 0 1;
Having just one point of failure can make the game a little too easy, so let’s try adding a second <details> element with a second “root” ID, #root2. Once more, we can use :has to check that neither the #root nor #root2 elements are open before displaying the #congrats message.
#game:has(#root:not([open])):has(#root2:not([open])) #congrats display: flex;
Wrapping up
The only thing left to do is play the game!
Fun, right? I’m sure we could have built something more robust without the self-imposed limitation of a JavaScript-free approach, and it’s not like we gave this a good-faith accessibility pass, but pushing an API to the limit is both fun and educational, right?
I’m interested: What other wacky ideas can you think up for using popovers? Maybe you have another game in mind, some slick UI effect, or some clever way of combining popovers with other emerging CSS features, like anchor positioning. Whatever it is, please share!
0 notes
jcmarchi · 2 months
Text
CSS Selectors
New Post has been published on https://thedigitalinsider.com/css-selectors/
CSS Selectors
Overview
CSS is really good at many things, but it’s really, really good at two specific things: selecting elements and styling them. That’s the raison d’être for CSS and why it’s a core web language. In this guide, we will cover the different ways to select elements — because the styles we write are pretty much useless without the ability to select which elements to apply them to.
The source of truth for CSS selectors is documented in the Selectors Module Level 4 specification. With one exception (which we’ll get to), all of the selectors covered here are well-covered by browsers across the board, and most certainly by all modern browsers.
In addition to selectors, this guide also looks at CSS combinators. If selectors identify what we are selecting, you might think of combinators as how the styles are applied. Combinators are like additional instructions we give CSS to select a very particular element on the page, not totally unlike the way we can use filters in search engines to find the exact result we want.
Quick reference
Common Selectors
/* Universal */ * box-sizing: border-box; /* Type or Tag */ p margin-block: 1.5rem; /* Classname */ .class text-decoration: underline; /* ID */ #id font-family: monospace; /* Relational */ li:has(a) display: flex;
Common Combinators
/* Descendant */ header h1 /* Selects all Heading 1 elements in a Header element. */ /* Child */ header > h1 /* Selects all Heading 1 elements that are children of Header elements. */ /* General sibling */ h1 ~ p /* Selects a Paragraph as long as it follows a Heading 1. */ /* Adjacent sibling */ h1 + p /* Selects a Paragraph if it immediately follows a Heading 1 */ /* Chained */ h1, p /* Selects both elements. */
General Selectors
When we talk about CSS selectors, we’re talking about the first part of a CSS ruleset:
/* CSS Ruleset */ selector /* Style rule */ property: value;
See that selector? That can be as simple as the HTML tag we want to select. For example, let’s select all <article> elements on a given page.
/* Select all <article> elements... */ article /* ... and apply this background-color on them */ background-color: hsl(25 100% 50%);
That’s the general process of selecting elements to apply styles to them. Selecting an element by its HTML tag is merely one selector type of several. Let’s see what those are in the following section.
Element selectors
Element selectors are exactly the type of selector we looked at in that last example: Select the element’s HTML tag and start styling!
That’s great and all, but consider this: Do you actually want to select all of the <article> elements on the page? That’s what we’re doing when we select an element by its tag — any and all HTML elements matching that tag get the styles. The following demo selects all <article> elements on the page, then applies a white (#fff) background to them. Notice how all three articles get the white background even though we only wrote one selector.
I’ve tried to make it so the relevant for code for this and other demos in this guide is provided at the top of the CSS tab. Anything in a @layer can be ignored. And if you’re new to @layer, you can learn all about it in our CSS Cascade Layers guide.
But maybe what we actually want is for the first element to have a different background — maybe it’s a featured piece of content and we need to make it stand out from the other articles. That requires us to be more specific in the type of selector we use to apply the styles.
Let’s turn our attention to other selector types that allow us to be more specific about what we’re selecting.
ID selectors
ID selectors are one way we can select one element without selecting another of the same element type. Let’s say we were to update the HTML in our <article> example so that the first article is “tagged” with an ID:
<article id="featured"> <!-- Article 1 --> </article> <article> <!-- Article 2 --> </article> <article> <!-- Article 3 --> </article>
Now we can use that ID to differentiate that first article from the others and apply styles specifically to it. We prepend a hashtag character (#) to the ID name when writing our CSS selector to properly select it.
/* Selects all <article> elements */ article background: #fff; /* Selects any element with id="featured" */ #featured background: hsl(35 100% 90%); border-color: hsl(35 100% 50%);
There we go, that makes the first article pop a little more than the others!
Before you go running out and adding IDs all over your HTML, be aware that IDs are considered a heavy-handed approach to selecting. IDs are so specific, that it is tough to override them with other styles in your CSS. IDs have so much specificity power than any selector trying to override it needs at least an ID as well. Once you’ve reached near the top of the ladder of this specificity war, it tends to lead to using !important rules and such that are in turn nearly impossible to override.
Let’s rearrange our CSS from that last example to see that in action:
/* Selects any element with id="featured" */ #featured background: hsl(35 100% 90%); border-color: hsl(35 100% 50%); /* Selects all <article> elements */ article background: #fff;
The ID selector now comes before the element selector. According to how the CSS Cascade determines styles, you might expect that the article elements all get a white background since that ruleset comes after the ID selector ruleset. But that’s not what happens.
So, you see how IDs might be a little too “specific” when it comes to selecting elements because it affects the order in which the CSS Cascade applies styles and that makes styles more difficult to manage and maintain.
The other reason to avoid IDs as selectors? We’re technically only allowed to use an ID once on a page, per ID. In other words, we can have one element with #featured but not two. That severely limits what we’re able to style if we need to extend those styles to other elements — not even getting into the difficulty of overriding the ID’s styles.
A better use case for IDs is for selecting items in JavaScript — not only does that prevent the sort of style conflict we saw above, but it helps maintain a separation of concerns between what we select in CSS for styling versus what we select in JavaScript for interaction.
Another thing about ID selectors: The ID establishes what we call an “anchor” which is a fancy term for saying we can link directly to an element on the page. For example, if we have an article with an ID assigned to it:
<article id="featured">...</article>
…then we can create a link to it like this:
<a href="featured">Jump to article below ⬇️</a> <!-- muuuuuuch further down the page. --> <article id="featured">...</article>
Clicking the link will navigate you to the element as though the link is anchored to that element. Try doing exactly that in the following demo:
This little HTML goodie opens up some pretty darn interesting possibilities when we sprinkle in a little CSS. Here are a few articles to explore those possibilities.
Class selectors
Class selectors might be the most commonly used type of CSS selector you will see around the web. Classes are ideal because they are slightly more specific than element selectors but without the heavy-handedness of IDs. You can read a deep explanation of how the CSS Cascade determines specificity, but the following is an abbreviated illustration focusing specifically (get it?!) on the selector types we’ve looked at so far.
That’s what makes class selectors so popular — they’re only slightly more specific than elements, but keep specificity low enough to be manageable if we need to override the styles in one ruleset with styles in another.
The only difference when writing a class is that we prepend a period (.) in front of the class name instead of the hashtag (#).
/* Selects all <article> elements */ article background: #fff; /* Selects any element with class="featured" */ .featured background: hsl(35 100% 90%); border-color: hsl(35 100% 50%);
Here’s how our <article> example shapes up when we swap out #featured with .featured.
Same result, better specificity. And, yes, we can absolutely combine different selector types on the same element:
<article id="someID" class="featured">...</article>
Do you see all of the possibilities we have to select an <article>? We can select it by:
Its element type (article)
Its ID (#someID)
Its class (.featured)
The following articles will give you some clever ideas for using class selectors in CSS.
But we have even more ways to select elements like this, so let’s continue.
Attribute selectors
ID and class selectors technically fall into this attribute selectors category. We call them “attributes” because they are present in the HTML and give more context about the element. All of the following are attributes in HTML:
<!-- ID, Class, Data Attribute --> <article id="#id" class=".class" data-attribute="attribute"> </article> <!-- href, Title, Target --> <a href="https://css-tricks.com" title="Visit CSS-Tricks" target="_blank"></a> <!-- src, Width, Height, Loading --> <img src="star.svg" width="250" height="250" loading="laxy" > <!-- Type, ID, Name, Checked --> <input type="checkbox" id="consent" name="consent" checked /> <!-- Class, Role, Aria Label --> <div class="buttons" role="tablist" aria-label="Tab Buttons">
Anything with an equals sign (=) followed by a value in that example code is an attribute. So, we can technically style all links with an href attribute equal to https://css-tricks.com:
a[href="https://css-tricks.com"] color: orangered;
Notice the syntax? We’re using square brackets ([]) to select an attribute instead of a period or hashtag as we do with classes and IDs, respectively.
The equals sign used in attributes suggests that there’s more we can do to select elements besides matching something that’s exactly equal to the value. That is indeed the case. For example, we can make sure that the matching selector is capitalized or not. A good use for that could be selecting elements with the href attribute as long as they do not contain uppercase letters:
/* Case sensitive */ a[href*='css-tricks' s]
The s in there tells CSS that we only want to select a link with an href attribute that does not contain uppercase letters.
<!-- 👎 No match --> <a href="https://CSS-Tricks.com">...</a> <!-- 👍 Match! --> <a href="https://css-tricks.com">...</a>
If case sensitivity isn’t a big deal, we can tell CSS that as well:
/* Case insensitive */ a[href*='css-tricks' i]
Now, either one of the link examples will match regardless of there being upper- or lowercase letters in the href attribute.
<!-- 👍 I match! --> <a href="https://CSS-Tricks.com">...</a> <!-- 👍 I match too! --> <a href="https://css-tricks.com">...</a>
There are many, many different types of HTML attributes. Be sure to check out our Data Attributes guide for a complete rundown of not only [data-attribute] but how they relate to other attributes and how to style them with CSS.
Universal selector
CSS-Tricks has a special relationship with the Universal Selector — it’s our logo!
That’s right, the asterisk symbol (*) is a selector all unto itself whose purpose is to select all the things. Quite literally, we can select everything on a page — every single element — with that one little asterisk. Note I said every single element, so this won’t pick up things like IDs, classes, or even pseudo-elements. It’s the element selector for selecting all elements.
/* Select ALL THE THINGS! 💥 */ * /* Styles */
Or, we can use it with another selector type to select everything inside a specific element.
/* Select everything in an <article> */ article * /* Styles */
That is a handy way to select everything in an <article>, even in the future if you decide to add other elements inside that element to the HTML. The times you’ll see the Universal Selector used most is to set border-sizing on all elements across the board, including all elements and pseudo-elements.
*, *::before, *::after box-sizing: border-box;
There’s a good reason this snippet of CSS winds up in so many stylesheets, which you can read all about in the following articles.
Sometimes the Universal Selector is implied. For example, when using a pseudo selector at the start of a new selector. These are selecting exactly the same:
*:has(article) :has(article)
Pseudo-selectors
Pseudo-selectors are for selecting pseudo-elements, just as element selectors are for selecting elements. And a pseudo-element is just like an element, but it doesn’t actually show up in the HTML. If pseudo-elements are new to you, we have a quick explainer you can reference.
Every element has a ::before and ::after pseudo-element attached to it even though we can’t see it in the HTML.
<div class="container"> <!-- ::before psuedo-element here --> <div>Item</div> <div>Item</div> <div>Item</div> <!-- ::after psuedo-element here --> </div>
These are super handy because they’re additional ways we can hook into an element an apply additional styles without adding more markup to the HTML. Keep things as clean as possible, right?!
We know that ::before and ::after are pseudo-elements because they are preceded by a pair of colons (::). That’s how we select them, too!
.container::before /* Styles */
The ::before and ::after pseudo-elements can also be written with a single colon — i.e., :before and :after — but it’s still more common to see a double colon because it helps distinguish pseudo-elements from pseudo-classes.
But there’s a catch when using pseudo-selectors: they require the content property. That’s because pseudos aren’t “real” elements but ones that do not exist as far as HTML is concerned. That means they need content that can be displayed… even if it’s empty content:
.container::before content: "";
Of course, if we were to supply words in the content property, those would be displayed on the page.
Article
on
Feb 4, 2022
Meet the Pseudo Class Selectors
Video
on
Feb 25, 2015
#94: Intro to Pseudo Elements
▶ Running Time: 18:37
Article
on
Aug 29, 2018
::before vs :before
Article
on
Sep 21, 2021
7 Practical Uses for the ::before and ::after Pseudo-Elements in CSS
Article
on
Aug 3, 2021
Use Cases for Multiple Pseudo Elements
Article
on
Aug 19, 2021
A Whole Bunch of Amazing Stuff Pseudo Elements Can Do
Article
on
Jul 9, 2019
A Little Reminder That Pseudo Elements are Children, Kinda.
Article
on
Dec 14, 2020
One Invalid Pseudo Selector Equals an Entire Ignored Selector
Article
on
Sep 27, 2021
CSS Pseudo Commas
Article
on
Apr 16, 2013
List of Pseudo-Elements to Style Form Controls
Article
on
Oct 24, 2020
Animating the `content` Property
Article
on
May 31, 2017
Animating Single Div Art
Article
on
Jun 5, 2020
Text Wrapping & Inline Pseudo Elements
Article
on
Jul 25, 2011
3D Cube with One Element
Complex selectors
Complex selectors may need a little marketing help because “complex” is an awfully scary term to come across when you’re in the beginning stages of learning this stuff. While selectors can indeed become complex and messy, the general idea is super straightforward: we can combine multiple selectors in the same ruleset.
Let’s look at three different routes we have for writing these “not-so-complex” complex selectors.
Listing selectors
First off, it’s possible to combine selectors so that they share the same set of styles. All we do is separate each selector with a comma.
.selector-1, .selector-2, .selector-3 /* We share these styles! 🤗 */
You’ll see this often when styling headings — which tend to share the same general styling except, perhaps, for font-size.
h1, h2, h3, h4, h5, h6 color: hsl(25 80% 15%); font-family: "Poppins", system-ui;
Adding a line break between selectors can make things more legible. You can probably imagine how complex and messy this might get. Here’s one, for example:
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;
Ummmm, okay. No one wants this in their stylesheet. It’s tough to tell what exactly is being selected, right?
The good news is that we have modern ways of combining these selectors more efficiently, such as the :is() pseudo selector. In this example, notice that we’re technically selecting all of the same elements. If we were to take out the four section, article, aside, and nav element selectors and left the descendants in place, we’d have this:
h1, h2, h3, h4, h5, h6, h1, h2, h3, h4, h5, h6, h1, h2, h3, h4, h5, h6, h1, h2, h3, h4, h5, h6, color: #BADA55;
The only difference is which element those headings are scoped to. This is where :is() comes in handy because we can match those four elements like this:
:is(section, article, aside, nav) color: #BADA55;
That will apply color to the elements themselves, but what we want is to apply it to the headings. Instead of listing those out for each heading, we can reach for :is() again to select them in one fell swoop:
/* Matches any of the following headings scoped to any of the following elements. */ :is(section, article, aside, nav) :is(h1, h2, h3, h4, h5, h6) color: #BADA55;
While we’re talking about :is() it’s worth noting that we have the :where() pseudo selector as well and that it does the exact same thing as :is(). The difference? The specificity of :is() will equal the specificity of the most specific element in the list. Meanwhile, :where() maintains zero specificity. So, if you want a complex selector like this that’s easier to override, go with :where() instead.
Almanac
on
Dec 2, 2022
:is
:is(ul, ol) li color: #f8a100;
Almanac
on
Jul 14, 2021
:where
main :where(h1, h2, h3) color: #f8a100;
Article
on
Apr 1, 2021
:where() has a cool specificity trick, too.
Article
on
Jun 10, 2020
CSS :is() and :where() are coming to browsers
Article
on
Apr 15, 2021
Platform News: Prefers Contrast, MathML, :is(), and CSS Background Initial Values
Article
on
Jul 12, 2021
Using the Specificity of :where() as a CSS Reset
Nesting selectors
That last example showing how :is() can be used to write more efficient complex selectors is good, but we can do even better now that CSS nesting is a widely supported feature.
This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.
Desktop
Chrome Firefox IE Edge Safari 120 117 No 120 17.2
Mobile / Tablet
Android Chrome Android Firefox Android iOS Safari 126 127 126 17.2
CSS nesting allows us to better see the relationship between selectors. You know how we can clearly see the relationship between elements in HTML when we indent descendant elements?
<!-- Parent --> <article> <!-- Child --> <img src="" alt="..."> <!-- Child --> <div class="article-content"> <!-- Grandchild --> <h2>Title</h2> <!-- Grandchild --> <p>Article content.</p> </div> </article>
CSS nesting is a similar way that we can format CSS rulesets. We start with a parent ruleset and then embed descendant rulesets inside. So, if we were to select the <h2> element in that last HTML example, we might write a descendant selector like this:
article h2 /* Styles */
With nesting:
article /* Article styles */ h2 /* Heading 2 styles */
You probably noticed that we can technically go one level deeper since the heading is contained in another .article-content element:
article /* Article styles */ .article-content /* Container styles */ h2 /* Heading 2 styles */
So, all said and done, selecting the heading with nesting is the equivalent of writing a descendant selector in a flat structure:
article .article-content h2 /* Heading 2 styles */
You might be wondering how the heck it’s possible to write a chained selector in a nesting format. I mean, we could easily nest a chained selector inside another selector:
article /* Article styles */ h2.article-content /* Heading 2 styles */
But it’s not like we can re-declare the article element selector as a nested selector:
article /* Article styles */ /* Nope! 👎 */ article.article-element /* Container styles */ /* Nope! 👎 */ h2.article-content /* Heading 2 styles */
Even if we could do that, it sort of defeats the purpose of a neatly organized nest that shows the relationships between selectors. Instead, we can use the ampersand (&) symbol to represent the selector that we’re nesting into. We call this the nesting selector.
article &.article-content /* Equates to: article.article-content */
Compounding selectors
We’ve talked quite a bit about the Cascade and how it determines which styles to apply to matching selectors using a specificity score. We saw earlier how an element selector is less specific than a class selector, which is less specific than an ID selector, and so on.
article /* Specificity: 0, 0, 1 */ .featured /* Specificity: 0, 1, 0 */ #featured /* Specificity: 1, 0, 0 */
Well, we can increase specificity by chaining — or “compounding” — selectors together. This way, we give our selector a higher priority when it comes to evaluating two or more matching styles. Again, overriding ID selectors is incredibly difficult so we’ll work with the element and class selectors to illustrate chained selectors.
We can chain our article element selector with our .featured class selector to generate a higher specificity score.
article /* Specificity: 0, 0, 1 */ .featured /* Specificity: 0, 1, 0 */ articie.featured /* Specificity: 0, 1, 1 */
This new compound selector is more specific (and powerful!) than the other two individual selectors. Notice in the following demo how the compound selector comes before the two individual selectors in the CSS yet still beats them when the Cascade evaluates their specificity scores.
Interestingly, we can use “fake” classes in chained selectors as a strategy for managing specificity. Take this real-life example:
.wp-block-theme-button .button:not(.specificity):not(.extra-specificity)
Whoa, right? There’s a lot going on there. But the idea is this: the .specificity and .extra-specificity class selectors are only there to bump up the specificity of the .wp-block-theme .button descendant selector. Let’s compare the specificity score with and without those artificial classes (that are :not() included in the match).
.wp-block-theme-button .button /* Specificity: 0, 2, 0 */ .wp-block-theme-button .button:not(.specificity) /* Specificity: 0, 3, 0 */ .wp-block-theme-button .button:not(.specificity):not(.extra-specificity /* Specificity: 0, 4, 0 */
Interesting! I’m not sure if I would use this in my own CSS but it is a less heavy-handed approach than resorting to the !important keyword, which is just as tough to override as an ID selector.
Combinators
If selectors are “what” we select in CSS, then you might think of CSS combinators as “how” we select them. they’re used to write selectors that combine other selectors in order to target elements. Inception!
The name “combinator” is excellent because it accurately conveys the many different ways we’re able to combine selectors. Why would we need to combine selectors? As we discussed earlier with Chained Selectors, there are two common situations where we’d want to do that:
When we want to increase the specificity of what is selected.
When we want to select an element based on a condition.
Let’s go over the many types of combinators that are available in CSS to account for those two situations in addition to chained selectors.
Descendant combinator
We call it a “descendant” combinator because we use it to select elements inside other elements, sorta like this:
/* Selects all elements in .parent with .child class */ .parent .child
…which would select all of the elements with the .child class in the following HTML example:
<div class="parent"> <div class="child"></div> <div class="child"></div> <div class="friend"></div> <div class="child"></div> <div class="child"></div> </div>
See that element with the .friend classname? That’s the only element inside of the .parent element that is not selected with the .parent .child descendant combinator since it does not match .child even though it is also a descendant of the .parent element.
Child combinator
A child combinator is really just an offshoot of the descendant combinator, only it is more specific than the descendant combinator because it only selects direct children of an element, rather than any descendant.
Let’s revise the last HTML example we looked at by introducing a descendant element that goes deeper into the family tree, like a .grandchild:
<div class="parent"> <div class="child"></div> <div class="child"> <div class="grandchild"></div> </div> <div class="child"></div> <div class="child"></div> </div>
So, what we have is a .parent to four .child elements, one of which contains a .grandchild element inside of it.
Maybe we want to select the .child element without inadvertently selecting the second .child element’s .grandchild. That’s what a child combinator can do. All of the following child combinators would accomplish the same thing:
/* Select only the "direct" children of .parent */ .parent > .child .parent > div .parent > *
See how we’re combining different selector types to make a selection? We’re combinating, dangit! We’re just doing it in slightly different ways based on the type of child selector we’re combining.
/* Select only the "direct" children of .parent */ .parent > #child { /* direct child with #child ID */ .parent > .child /* direct child with .child class */ .parent > div /* direct child div elements */ .parent > * /* all direct child elements */
It’s pretty darn neat that we not only have a way to select only the direct children of an element, but be more or less specific about it based on the type of selector. For example, the ID selector is more specific than the class selector, which is more specific than the element selector, and so on.
General sibling combinator
If two elements share the same parent element, that makes them siblings like brother and sister. We saw an example of this in passing when discussing the descendant combinator. Let’s revise the class names from that example to make the sibling relationship a little clearer:
<div class="parent"> <div class="brother"></div> <div class="sister"></div> </div>
This is how we can select the .sister element as long as it is preceded by a sibling with class .brother.
/* Select .sister only if follows .brother */ .brother ~ .sister
The Tilda symbol (~) is what tells us this is a sibling combinator.
It doesn’t matter if a .sister comes immediately after a .brother or not — as long as a .sister comes after a brother and they share the same parent element, it will be selected. Let’s see a more complicated HTML example:
<main class="parent"> <!-- .sister immediately after .brother --> <div class="brother"></div> <div class="sister"></div> <!-- .sister immediately after .brother --> <div class="brother"></div> <div class="sister"></div> <!-- .sister immediately after .sister --> <div class="sister"></div> <!-- .cousin immediately after .brother --> <div class="brother"></div> <div class="cousin"> <!-- .sister contained in a .cousin --> <div class="sister"></div> </div> </main>
The sibling combinator we wrote only selects the first three .sister elements because they are the only ones that come after a .brother element and share the same parent — even in the case of the third .sister which comes after another sister! The fourth .sister is contained inside of a .cousin, which prevents it from matching the selector.
Let’s see this in context. So, we can select all of the elements with an element selector since each element in the HTML is a div:
From there, we can select just the brothers with a class selector to give them a different background color:
We can also use a class selector to set a different background color on all of the elements with a .sister class:
And, finally, we can use a general sibling combinator to select only sisters that are directly after a brother.
Did you notice how the last .sister element’s background color remained green while the others became purple? That’s because it’s the only .sister in the bunch that does not share the same .parent as a .brother element.
Adjacent combinator
Believe it or not, we can get even more specific about what elements we select with an adjacent combinator. The general sibling selector we just looked at will select all of the .sister elements on the page as long as it shares the same parent as .brother and comes after the .brother.
What makes an adjacent combinator different is that it selects any element immediately following another. Remember how the last .sister didn’t match because it is contained in a different parent element (i.e., .cousin)? Well, we can indeed select it by itself using an adjacent combinator:
/* Select .sister only if directly follows .brother */ .brother + .sister
Notice what happens when we add that to our last example:
The first two .sister elements changed color! That’s because they are the only sisters that come immediately after a .brother. The third .sister comes immediately after another .sister and the fourth one is contained in a .cousin which prevents both of them from matching the selection.
Learn more about CSS selectors
Table of contents
References
The vast majority of what you’re reading here is information pulled from articles we’ve published on CSS-Tricks and those are linked up throughout the guide. In addition to those articles, the following resources were super helpful for putting this guide together.
0 notes
jcmarchi · 5 months
Text
Overcoming the Top Security Challenges of AI-Driven Low-Code/No Code Development
New Post has been published on https://thedigitalinsider.com/overcoming-the-top-security-challenges-of-ai-driven-low-code-no-code-development/
Overcoming the Top Security Challenges of AI-Driven Low-Code/No Code Development
Low-code development platforms have changed the way people create custom business solutions, including apps, workflows, and copilots. These tools empower citizen developers and create a more agile environment for app development. Adding AI to the mix has only enhanced this capability. The fact that there aren’t enough people at an organization that have the skills (and time) to build the number of apps, automations and so on that are needed to drive innovation forward has given rise to the low-code/no-code paradigm. Now, without needing formal technical training, citizen developers can leverage user-friendly platforms and Generative AI to create, innovate and deploy AI-driven solutions.
But how secure is this practice? The reality is that it’s introducing a host of new risks. Here’s the good news: you don’t have to choose between security and the efficiency that business-led innovation provides.
A shift beyond the traditional purview
IT and security teams are used to focusing their efforts on scanning and looking for vulnerabilities written into code. They’ve centered on making sure developers are building secure software, assuring the software is secure and then – once it’s in production – monitoring it for deviations or for anything suspicious after the fact.
With the rise of low code and no code, more people than ever are building applications and using automation to create applications – outside the traditional development process. These are often employees with little to no software development background, and these apps are being created outside of security’s purview.
This creates a situation where IT is no longer building everything for the organization, and the security team lacks visibility. In a large organization, you might get a few hundred apps built in a year through professional development; with low/no code, you could get far more than that. That’s a lot of potential apps that could go unnoticed or unmonitored by security teams.
A wealth of new risks
 Some of the potential security concerns associated with low-code/no-code development include:
Not in IT’s purview – as just mentioned, citizen developers work outside the lines of IT professionals, creating a lack of visibility and shadow app development. Additionally, these tools enable an infinite number of people to create apps and automations quickly, with just a few clicks. That means there’s an untold number of apps being created at breakneck pace by an untold number of people all without IT having the full picture.
No software development lifecycle (SDLC) – Developing software in this way means there’s no SDLC in place, which can lead to inconsistency, confusion and lack of accountability in addition to risk.
Novice developers – These apps are often being built by people with less technical skill and experience, opening the door to mistakes and security threats. They don’t necessarily think about the security or development ramifications in the way that a professional developer or someone with more technical experience would. And if a vulnerability is found in a specific component that is embedded into a large number of apps, it has the potential to be exploited across multiple instances
Bad identity practices – Identity management can also be an issue. If you want to empower a business user to build an application, the number one thing that might stop them is a lack of permissions. Often, this can be circumvented, and what happens is that you might have a user using someone else’s identity. In this case, there is no way to figure out if they’ve done something wrong. If you access something you are not allowed to or you tried to do something malicious, security will come looking for the borrowed user’s identity because there’s no way to distinguish between the two.
No code to scan – This causes a lack of transparency that can hinder troubleshooting, debugging and security analysis, as well as possible compliance and regulatory concerns.
These risks can all contribute to potential data leakage. No matter how an application is built – whether it gets built with drag-and-drop, a text-based prompt, or with code – it has an identity, it has access to data, it can perform operations, and it needs to communicate with users. Data is being moved, often between different places in the organization; this can easily break data boundaries or barriers.
Data privacy and compliance are also at stake. Sensitive data lives within these applications, but it’s being handled by business users who don’t know how (nor even think to) to properly store it. That can lead to a host of additional issues, including compliance violations.
Regaining visibility
As mentioned, one of the big challenges with low/no code is that it’s not under the purview of IT/security, which means data is traversing apps. There’s not always a clear understanding of who is really creating these apps, and there’s an overall lack of visibility into what’s really happening. And not every organization is even fully aware of what’s happening. Or they think citizen development isn’t happening in their organization, but it almost certainly is.
So, how can security leaders gain control and mitigate risk? The first step is to look into the citizen developer initiatives within your organization, find out who (if anyone) is leading these efforts and connect with them. You don’t want these teams to feel penalized or hindered; as a security leader, your goal should be to support their efforts but provide education and guidance on making the process safer.
Security must start with visibility. Key to this is creating an inventory of applications and developing an understanding of who is building what. Having this information will help ensure that if some kind of breach does occur, you’ll be able to trace the steps and figure out what happened.
Establish a framework for what secure development looks like. This includes the necessary policies and technical controls that will ensure users make the right choices. Even professional developers make mistakes when it comes to sensitive data; it’s even harder to control this with business users. But with the right controls in place, you can make it difficult to make a mistake.
Toward more secure low-code/no-code
The traditional process of manual coding has hindered innovation, especially in competitive time-to-market scenarios. With today’s low-code and no code platforms, even people without development experience can create AI-driven solutions. While this has streamlined app development, it can also jeopardize the safety and security of organizations. It doesn’t have to be a choice between citizen development and security, however; security leaders can partner with business users to find a balance for both.
0 notes