feat(content): add a new article about CSS
This commit is contained in:
parent
cce7779c5a
commit
874def264c
3 changed files with 1086 additions and 0 deletions
|
@ -36,6 +36,7 @@ outputFormats:
|
||||||
|
|
||||||
markup:
|
markup:
|
||||||
highlight:
|
highlight:
|
||||||
|
lineNumbersInTable: false
|
||||||
noClasses: false
|
noClasses: false
|
||||||
goldmark:
|
goldmark:
|
||||||
parser:
|
parser:
|
||||||
|
|
542
content/blog/008-embracing-modern-css/index.en.md
Normal file
542
content/blog/008-embracing-modern-css/index.en.md
Normal file
|
@ -0,0 +1,542 @@
|
||||||
|
---
|
||||||
|
# vim: spell spelllang=en
|
||||||
|
title: 'Embracing Modern CSS'
|
||||||
|
slug: '008-embracing-modern-css'
|
||||||
|
date: '2025-07-06T14:29:26+02:00'
|
||||||
|
draft: false
|
||||||
|
categories:
|
||||||
|
- dev
|
||||||
|
tags:
|
||||||
|
- CSS
|
||||||
|
summary: |
|
||||||
|
Where I write about my discoveries of recent CSS I was unaware of.
|
||||||
|
description: |
|
||||||
|
The article explores recent developments in CSS, such as nested rules, CSS
|
||||||
|
variables, and classless CSS, which allow for cleaner and more efficient
|
||||||
|
stylesheet writing. It also mentions future features like CSS mixins, custom
|
||||||
|
CSS properties, and CSS scopes, which promise to further enhance the way user
|
||||||
|
interfaces are styled.
|
||||||
|
---
|
||||||
|
|
||||||
|
I recently stumbled upon a note on a page of [Plain Vanilla] in which I learned
|
||||||
|
that nested CSS is a thing. It's a thing that's been around for quite some time,
|
||||||
|
but I did not know about it (I'm quite late in my RSS reader).
|
||||||
|
|
||||||
|
This allowed me to catch up on some of the recent evolutions of CSS.
|
||||||
|
|
||||||
|
[Plain Vanilla]: https://plainvanillaweb.com/pages/styling.html
|
||||||
|
|
||||||
|
## Nested CSS
|
||||||
|
|
||||||
|
Nesting CSS rules is one of the main reasons I've been using [SASS] for 15
|
||||||
|
years.
|
||||||
|
|
||||||
|
I've always preferred to write nested rules to group together coherent units of
|
||||||
|
CSS. For example,
|
||||||
|
|
||||||
|
```scss
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
nav {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
makes more sense to me than
|
||||||
|
|
||||||
|
```css
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I have a few reasons for this:
|
||||||
|
|
||||||
|
* It's easier to read because selectors are sorter and the hierarchy is easier
|
||||||
|
to grasp;
|
||||||
|
* I can move around a group of selector without forgetting a declaration;
|
||||||
|
* I can use my IDE code folding based on indent to close a group and navigate
|
||||||
|
long CSS files.
|
||||||
|
|
||||||
|
Since the [CSS Nesting Module] is [Baseline Widely Available] and is supported
|
||||||
|
by [90% of users], It can be used to write nested CSS. So now, this is a thing in
|
||||||
|
CSS:
|
||||||
|
|
||||||
|
```css
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
nav {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The [CSS file] of this site has been rewritten using nested CSS.
|
||||||
|
|
||||||
|
[SASS]: https://sass-lang.com
|
||||||
|
[CSS Nesting Module]: https://drafts.csswg.org/css-nesting/
|
||||||
|
[Baseline Widely Available]: https://webstatus.dev/features/Nesting
|
||||||
|
[90% of users]: https://caniuse.com/css-nesting
|
||||||
|
[CSS file]: https://bcarlin.net/static/css/bcarlin.css
|
||||||
|
|
||||||
|
## CSS Variables
|
||||||
|
|
||||||
|
To me, variables are essential to ensure a coherent user interface. They allow to
|
||||||
|
reuse colors, sizes, spacing, and so on.
|
||||||
|
|
||||||
|
This is also a reason why I've been using [SASS]. It allowed me to write CSS
|
||||||
|
with reusable variables :
|
||||||
|
|
||||||
|
```scss
|
||||||
|
$color-error: red;
|
||||||
|
$color-success: green;
|
||||||
|
|
||||||
|
label {
|
||||||
|
&.error {
|
||||||
|
color: $color-error;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: $color-success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
&.error {
|
||||||
|
background-color: $color-error;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: $color-success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
I missed the [CSS Custom Properties for Cascading Variables Module Level 1]
|
||||||
|
module from 2017, which allowed me to write the same thing in pure CSS:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--color-error: red;
|
||||||
|
--color-success: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
&.error {
|
||||||
|
color: var(--color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
&.error {
|
||||||
|
background-color: var(--color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: var(--color-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[CSS Custom Properties for Cascading Variables Module Level 1]: https://www.w3.org/TR/css-variables/
|
||||||
|
|
||||||
|
## Classless CSS
|
||||||
|
|
||||||
|
Maybe I an going backwards here, given the popularity of utility-first CSS
|
||||||
|
frameworks like [TailwindCSS].
|
||||||
|
|
||||||
|
This one is not really a CSS feature *per se*, but it is a way to write CSS,
|
||||||
|
where semantically correct HTML is automatically styled correctly. To some
|
||||||
|
extent, it is, however, backed by some [CSS Selectors Level 4] which are now
|
||||||
|
Widely implemented across browsers, such as `:has`, `:is`, `:where`, `:not` and
|
||||||
|
so on.
|
||||||
|
|
||||||
|
I used to use [BootstrapCSS] in my projects because it is complete and easy to
|
||||||
|
use, but I never liked the way it imposed a heavy CSS Structure on my source. For
|
||||||
|
this site, I was looking for something lighter and came across [PicoCSS] which
|
||||||
|
styles 90% of my site without changing anything to my templates.
|
||||||
|
|
||||||
|
I already had a meaningful semantic HTML base structure:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<!-- ... -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/blog">Blog</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1>Page Title</h1>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<!-- ... -->
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<!-- ... -->
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
And I really like the way it works: the content is styled based on its semantic
|
||||||
|
markup, and not on a HTML imposed structure.
|
||||||
|
|
||||||
|
For example, here is [Bootstrap Modal component]:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Modal Title</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Modal Body
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary">Save changes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is the [Modal component from Tailwind Plus]:
|
||||||
|
|
||||||
|
```HTML
|
||||||
|
<div>
|
||||||
|
<button class="rounded-md bg-gray-950/5 px-2.5 py-1.5 text-sm font-semibold text-gray-900 hover:bg-gray-950/10">Open dialog</button>
|
||||||
|
|
||||||
|
<div class="relative z-10" aria-labelledby="dialog-title" role="dialog" aria-modal="true">
|
||||||
|
<div class="fixed inset-0 bg-gray-500/75 transition-opacity" aria-hidden="true"></div>
|
||||||
|
|
||||||
|
<div class="fixed inset-0 z-10 w-screen overflow-y-auto">
|
||||||
|
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||||
|
<div class="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg">
|
||||||
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||||
|
<div class="sm:flex sm:items-start">
|
||||||
|
<div class="mx-auto flex size-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:size-10">
|
||||||
|
<svg class="size-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||||
|
<h3 class="text-base font-semibold text-gray-900" id="dialog-title">Modal Title</h3>
|
||||||
|
<div class="mt-2">
|
||||||
|
Modal Body
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
|
||||||
|
<button type="button" class="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-red-500 sm:ml-3 sm:w-auto">Save changes</button>
|
||||||
|
<button type="button" class="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-gray-300 ring-inset hover:bg-gray-50 sm:mt-0 sm:w-auto">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Compare those with [PicoCSS Modal component]:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<dialog open>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<button aria-label="Close" rel="prev"></button>
|
||||||
|
<p>
|
||||||
|
<strong>Modal Title</strong>
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
Modal Body
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<button class="secondary">Close</button>
|
||||||
|
<button>Save changes</button>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</dialog>
|
||||||
|
```
|
||||||
|
|
||||||
|
It makes a huge difference in simplicity, readability and accessibility (note
|
||||||
|
that the ARIA attributes are rendered useless because the semantic markup
|
||||||
|
already carries that information).
|
||||||
|
|
||||||
|
|
||||||
|
[TailwindCSS]: https://tailwindcss.com
|
||||||
|
[CSS Selectors Level 4]: https://drafts.csswg.org/selectors-4/
|
||||||
|
[BootstrapCSS]: https://getbootstrap.com
|
||||||
|
[PicoCSS]: https://picocss.com
|
||||||
|
[Bootstrap Modal Component]: https://getbootstrap.com/docs/4.3/components/modal/#live-demo
|
||||||
|
[Modal component from Tailwind Plus]: https://tailwindcss.com/plus/ui-blocks/application-ui/overlays/modal-dialogs
|
||||||
|
[PicoCSS Modal component]: https://picocss.com/docs/modal
|
||||||
|
|
||||||
|
## `@import` to split CSS files
|
||||||
|
|
||||||
|
One last thing I liked to use SASS for was the possibility to split CSS files
|
||||||
|
into smaller ones to make them easier to grasp. For example:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
@use 'reset';
|
||||||
|
@use 'typography';
|
||||||
|
@use 'layout';
|
||||||
|
@use 'content';
|
||||||
|
```
|
||||||
|
|
||||||
|
With the [CSS Cascading and Inheritance Level 5] module, CSS has that natively:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import url('reset.css');
|
||||||
|
@import url('typography.css');
|
||||||
|
@import url('layout.css');
|
||||||
|
@import url('content.css');
|
||||||
|
```
|
||||||
|
|
||||||
|
From my understanding, the `@import`ed CSS files are downloaded in parallel,
|
||||||
|
which reduces the penalty of having several files to download.
|
||||||
|
|
||||||
|
CSS `@import` rules even have the benefit of being conditional. For example:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import url("light.css") only screen and (prefers-color-scheme: light);
|
||||||
|
@import url('dark.css') only screen and (prefers-color-scheme: dark);
|
||||||
|
```
|
||||||
|
|
||||||
|
[CSS Cascading and Inheritance Level 5]: https://drafts.csswg.org/css-cascade-5/
|
||||||
|
|
||||||
|
## Things I'm looking forward to
|
||||||
|
|
||||||
|
Those are some things I'm looking forward to using. I do not use them yet
|
||||||
|
because of browser support or because I did not have a use for them yet. But I'm
|
||||||
|
excited to try them out.
|
||||||
|
|
||||||
|
### CSS Mixins
|
||||||
|
|
||||||
|
CSS Mixins are also a major feature of SASS, and foster a cleaner and more
|
||||||
|
reusable CSS code.
|
||||||
|
|
||||||
|
CSS will have them with the [CSS Functions and Mixins Module], which is still a
|
||||||
|
draft where mixins are not specified yet.
|
||||||
|
|
||||||
|
In the meantime, here is an example from [SASS Mixin Guide]:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
@mixin rtl($property, $ltr-value, $rtl-value) {
|
||||||
|
#{$property}: $ltr-value;
|
||||||
|
|
||||||
|
[dir=rtl] & {
|
||||||
|
#{$property}: $rtl-value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
@include rtl(float, left, right);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Though in some cases, it can easily be replaced with CSS variables:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--sidebar-float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir=rtl] {
|
||||||
|
--sidebar-float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
float: var(--sidebar-float);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[SASS Mixin Guide]: https://sass-lang.com/documentation/at-rules/mixin/#arguments
|
||||||
|
[CSS Functions and Mixins Module]: https://www.w3.org/TR/2025/WD-css-mixins-1-20250515/
|
||||||
|
|
||||||
|
### CSS Custom Properties
|
||||||
|
|
||||||
|
This one is a nice little feature from the [CSS Properties and Values API Level
|
||||||
|
1] module which extends CSS variables nicely.
|
||||||
|
|
||||||
|
They allow to define the type, initial value and inheritance rule of a custom
|
||||||
|
variables. For example:
|
||||||
|
|
||||||
|
```css {linenos=true}
|
||||||
|
@property --my-color {
|
||||||
|
syntax: "<color>";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
--my-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
--my-color: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: var(--my-color);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, the definition of `--my-color` on line 12 is not valid (it is a length and
|
||||||
|
not a color). As the property value is not inherited from a parent, the initial
|
||||||
|
value will be used: a `<button class="secondary">` will have a black background.
|
||||||
|
|
||||||
|
However, as the property is defined to be a color, linters like [Stylelint] and
|
||||||
|
[ESLint] will eventually be able to catch such errors, in addition to catching
|
||||||
|
typos in values or in the property name.
|
||||||
|
|
||||||
|
[CSS Properties and Values API Level 1]: https://drafts.css-houdini.org/css-properties-values-api
|
||||||
|
[Stylelint]: https://stylelint.io
|
||||||
|
[ESLint]: https://eslint.org
|
||||||
|
|
||||||
|
### CSS Scopes
|
||||||
|
|
||||||
|
This one is maybe the one I am expecting the most.
|
||||||
|
|
||||||
|
To style a UI component, it is often necessary to target a specific element of
|
||||||
|
the component, and repeat selectors:
|
||||||
|
|
||||||
|
```css
|
||||||
|
|
||||||
|
.card { /*...*/ }
|
||||||
|
.card article { /*...*/ }
|
||||||
|
.card article header { /*...*/ }
|
||||||
|
.card article footer { /*...*/ }
|
||||||
|
.card article footer button { /*...*/ }
|
||||||
|
```
|
||||||
|
|
||||||
|
With nested CSS modules, this can be simplified to:
|
||||||
|
|
||||||
|
```css
|
||||||
|
.card {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
article {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
header {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
button {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It can however have some edge cases and yield unexpected results (see the
|
||||||
|
[example on MDN]).
|
||||||
|
|
||||||
|
Scopes are a new feature from the [CSS Cascading and Inheritance Level 6]
|
||||||
|
module. They are a more natural way of defining rules:
|
||||||
|
|
||||||
|
```css
|
||||||
|
@scope (.card){
|
||||||
|
:scope {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
header {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
button {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`@scope` power comes from several fact:
|
||||||
|
|
||||||
|
* It follows a proximity rules: an element is styled with the nearest scope
|
||||||
|
rules;
|
||||||
|
* It adds no specificity to the selector, which means that it can be overridden
|
||||||
|
more easily;
|
||||||
|
* it is more expressive.
|
||||||
|
|
||||||
|
[CSS Cascading and Inheritance Level 6]: https://drafts.csswg.org/css-cascade-6/
|
||||||
|
[example on MDN]: https://developer.mozilla.org/en-US/docs/Web/CSS/@scope#how_scope_conflicts_are_resolved
|
543
content/blog/008-embracing-modern-css/index.fr.md
Normal file
543
content/blog/008-embracing-modern-css/index.fr.md
Normal file
|
@ -0,0 +1,543 @@
|
||||||
|
---
|
||||||
|
# vim: spell spelllang=fr
|
||||||
|
title: 'Adopter un CSS moderne'
|
||||||
|
slug: '008-adopter-css-moderne'
|
||||||
|
date: '2025-07-06T14:29:26+02:00'
|
||||||
|
draft: false
|
||||||
|
categories:
|
||||||
|
- dev
|
||||||
|
tags:
|
||||||
|
- CSS
|
||||||
|
summary:
|
||||||
|
Exploration de développements récents de CSS dont j'étais passé à côté.
|
||||||
|
description: |
|
||||||
|
L'article explore les récentes évolutions du CSS, telles que l'imbrication des
|
||||||
|
règles, les variables CSS, et l'utilisation de CSS sans classes, qui
|
||||||
|
permettent une écriture plus propre et plus efficace des feuilles de style. Il
|
||||||
|
mentionne également des fonctionnalités futures comme les mixins CSS, les
|
||||||
|
propriétés personnalisées CSS, et les portées CSS, qui promettent d'améliorer
|
||||||
|
encore davantage la manière de styliser les interfaces utilisateur.
|
||||||
|
---
|
||||||
|
|
||||||
|
Je suis récemment tombé sur une note dans une page de [Plain Vanilla] dans
|
||||||
|
laquelle j'ai appris que le CSS imbriqué est valide. C'est possible depuis assez
|
||||||
|
longtemps, mais je ne le savais pas (je suis assez en retard dans mon lecteur
|
||||||
|
RSS).
|
||||||
|
|
||||||
|
Cela m'a permis de rattraper certaines des récentes évolutions du CSS.
|
||||||
|
|
||||||
|
[Plain Vanilla]: https://plainvanillaweb.com/pages/styling.html
|
||||||
|
|
||||||
|
## CSS imbriqué
|
||||||
|
|
||||||
|
L'imbrication des règles CSS est l'une des principales raisons pour lesquelles
|
||||||
|
j'ai utilisé [SASS] pendant 15 ans. J'ai toujours préféré écrire des règles
|
||||||
|
imbriquées pour regrouper des unités cohérentes de CSS. Par exemple,
|
||||||
|
|
||||||
|
```scss
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
nav {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
a plus de sens pour moi que
|
||||||
|
|
||||||
|
```css
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
header nav ul li {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Il y a plusieurs raisons à ça :
|
||||||
|
|
||||||
|
* C'est plus facile à lire car les sélecteurs sont plus courts et la hiérarchie
|
||||||
|
est plus facile à comprendre ;
|
||||||
|
* Je peux déplacer un groupe de sélecteurs sans risquer d'oublier une
|
||||||
|
déclaration ;
|
||||||
|
* Je peux utiliser le repliement de code basé sur l'indentation de mon IDE pour
|
||||||
|
fermer un groupe et naviguer dans de longs fichiers CSS.
|
||||||
|
|
||||||
|
Depuis que le module [CSS Nesting] est disponible de manière générale et est
|
||||||
|
[supporté par 90% des utilisateurs], il peut être utilisé pour écrire du CSS
|
||||||
|
imbriqué. Donc maintenant, c'est du CSS valide :
|
||||||
|
|
||||||
|
```css
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
nav {
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Le fichier [CSS de ce site] a été réécrit en utilisant le CSS imbriqué.
|
||||||
|
|
||||||
|
[SASS]: https://sass-lang.com
|
||||||
|
[CSS Nesting]: https://drafts.csswg.org/css-nesting/
|
||||||
|
[supporté par 90% des utilisateurs]: https://caniuse.com/css-nesting
|
||||||
|
[CSS de ce site]: https://bcarlin.net/static/css/bcarlin.css
|
||||||
|
|
||||||
|
## Variables CSS
|
||||||
|
|
||||||
|
Pour moi, les variables sont essentielles pour assurer une interface utilisateur
|
||||||
|
cohérente. Elles permettent de réutiliser des couleurs, des tailles, des
|
||||||
|
espacements, etc.
|
||||||
|
|
||||||
|
C'est aussi une raison pour laquelle j'ai utilisé SASS. Cela m'a permis d'écrire
|
||||||
|
du CSS avec des variables réutilisables :
|
||||||
|
|
||||||
|
```scss
|
||||||
|
$color-error: red;
|
||||||
|
$color-success: green;
|
||||||
|
|
||||||
|
label {
|
||||||
|
&.error {
|
||||||
|
color: $color-error;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: $color-success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
&.error {
|
||||||
|
background-color: $color-error;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: $color-success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
J'ai manqué la publication du module [CSS Custom Properties for Cascading
|
||||||
|
Variables Module Level 1] de 2017, qui m'a permis d'écrire la même chose en CSS
|
||||||
|
pur :
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--color-error: red;
|
||||||
|
--color-success: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
&.error {
|
||||||
|
color: var(--color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
&.error {
|
||||||
|
background-color: var(--color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: var(--color-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[CSS Custom Properties for Cascading Variables Module Level 1]: https://www.w3.org/TR/css-variables/
|
||||||
|
|
||||||
|
## CSS Sans Classes
|
||||||
|
|
||||||
|
Peut-être que je vais à contre-courant ici, , compte tenu de la popularité des
|
||||||
|
frameworks CSS « utilitaires » comme [TailwindCSS].
|
||||||
|
|
||||||
|
Ce point n'est pas vraiment une fonctionnalité CSS en soi, mais c'est une façon
|
||||||
|
d'écrire du CSS, où le HTML sémantiquement correct est automatiquement mis en
|
||||||
|
forme correctement. Dans une certaine mesure, il est cependant soutenu par
|
||||||
|
certains [Sélecteurs CSS Niveau 4] qui sont maintenant largement implémentés
|
||||||
|
dans les navigateurs, tels que `:has`, `:is`, `:where`, `:not`, etc.
|
||||||
|
|
||||||
|
J'utilisais habituellement [BootstrapCSS] dans mes projets parce qu'il est
|
||||||
|
complet et facile à utiliser, mais je n'ai jamais aimé la façon dont il imposait
|
||||||
|
une structure CSS relativement lourde. Pour ce site, je cherchais quelque chose
|
||||||
|
de plus léger et je suis tombé sur [PicoCSS] qui a mis en forme 90% de mon site
|
||||||
|
sans changer quoi que ce soit à mes modèles.
|
||||||
|
|
||||||
|
J'avais déjà une structure HTML de base sémantiquement significative :
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<!-- ... -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Accueil</a></li>
|
||||||
|
<li><a href="/blog">Blog</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h1>Titre de la Page</h1>
|
||||||
|
</header>
|
||||||
|
<section>
|
||||||
|
<!-- ... -->
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<!-- ... -->
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
Et j'aime vraiment la façon dont cela fonctionne : le contenu est mis en forme
|
||||||
|
en fonction de son balisage sémantique, et non en fonction d'une structure HTML
|
||||||
|
imposée.
|
||||||
|
|
||||||
|
Par exemple, voici le [composant Modal de Bootstrap] :
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Titre de la fenêtre</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Fermer">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
Corps de la fenêtre
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Fermer</button>
|
||||||
|
<button type="button" class="btn btn-primary">Enregistrer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Voici le [composant Modal de Tailwind Plus] :
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div>
|
||||||
|
<button class="rounded-md bg-gray-950/5 px-2.5 py-1.5 text-sm font-semibold text-gray-900 hover\:bg-gray-950/10">Ouvrir le dialogue</button>
|
||||||
|
<div class="relative z-10" aria-labelledby="dialog-title" role="dialog" aria-modal="true">
|
||||||
|
<div class="fixed inset-0 bg-gray-500/75 transition-opacity" aria-hidden="true"></div>
|
||||||
|
<div class="fixed inset-0 z-10 w-screen overflow-y-auto">
|
||||||
|
<div class="flex min-h-full items-end justify-center p-4 text-center sm\:items-center sm\:p-0">
|
||||||
|
<div class="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm\:my-8 sm\:w-full sm\:max-w-lg">
|
||||||
|
<div class="bg-white px-4 pt-5 pb-4 sm\:p-6 sm\:pb-4">
|
||||||
|
<div class="sm\:flex sm\:items-start">
|
||||||
|
<div class="mx-auto flex size-12 shrink-0 items-center justify-center rounded-full bg-red-100 sm\:mx-0 sm\:size-10">
|
||||||
|
<svg class="size-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="mt-3 text-center sm\:mt-0 sm\:ml-4 sm\:text-left">
|
||||||
|
<h3 class="text-base font-semibold text-gray-900" id="dialog-title">Titre de la fenêtre</h3>
|
||||||
|
<div class="mt-2">
|
||||||
|
Corps de la fenêtre
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 px-4 py-3 sm\:flex sm\:flex-row-reverse sm\:px-6">
|
||||||
|
<button type="button" class="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-xs hover\:bg-red-500 sm\:ml-3 sm\:w-auto">Enregistrer</button>
|
||||||
|
<button type="button" class="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-gray-300 ring-inset hover\:bg-gray-50 sm\:mt-0 sm\:w-auto">Fermer</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Comparez-les avec le [composant Modal de PicoCSS] :
|
||||||
|
|
||||||
|
```html
|
||||||
|
<dialog open>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<button aria-label="Fermer" rel="prev"></button>
|
||||||
|
<p>
|
||||||
|
<strong>Titre de la fenêtre</strong>
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
Corps de la fenêtre
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<button class="secondary">Fermer</button>
|
||||||
|
<button>Enregistrer</button>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
</dialog>
|
||||||
|
```
|
||||||
|
|
||||||
|
Cela fait une énorme différence en termes de simplicité, de lisibilité et
|
||||||
|
d'accessibilité (notez que les attributs ARIA sont rendus inutiles car le
|
||||||
|
balisage sémantique porte déjà cette information).
|
||||||
|
|
||||||
|
[TailwindCSS]: https://tailwindcss.com
|
||||||
|
[Sélecteurs CSS Niveau 4]: https://drafts.csswg.org/selectors-4/
|
||||||
|
[BootstrapCSS]: https://getbootstrap.com
|
||||||
|
[PicoCSS]: https://picocss.com
|
||||||
|
[composant Modal de Bootstrap]: https://getbootstrap.com/docs/4.3/components/modal/#live-demo
|
||||||
|
[composant Modal de Tailwind Plus]: https://tailwindcss.com/plus/ui-blocks/application-ui/overlays/modal-dialogs
|
||||||
|
[composant Modal de PicoCSS]: https://picocss.com/docs/modal
|
||||||
|
|
||||||
|
## `@import` pour diviser les fichiers CSS
|
||||||
|
|
||||||
|
Un dernièr point pour lequel j'aimais utiliser SASS était la possibilité de
|
||||||
|
diviser les fichiers CSS en fichiers plus petits pour les rendre plus faciles à
|
||||||
|
comprendre. Par exemple :
|
||||||
|
|
||||||
|
```scss
|
||||||
|
@use 'reset';
|
||||||
|
@use 'typography';
|
||||||
|
@use 'layout';
|
||||||
|
@use 'content';
|
||||||
|
```
|
||||||
|
|
||||||
|
Avec le module [CSS Cascading and Inheritance Level 5], CSS a cela nativement :
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import url('reset.css');
|
||||||
|
@import url('typography.css');
|
||||||
|
@import url('layout.css');
|
||||||
|
@import url('content.css');
|
||||||
|
```
|
||||||
|
|
||||||
|
De ma compréhension, les fichiers CSS `@import`és sont téléchargés en parallèle,
|
||||||
|
ce qui réduit le coût d'avoir plusieurs fichiers à télécharger. Les règles
|
||||||
|
CSS `@import` ont même l'avantage d'être conditionnelles. Par exemple :
|
||||||
|
|
||||||
|
```css
|
||||||
|
@import url("light.css") only screen and (prefers-color-scheme: light);
|
||||||
|
@import url('dark.css') only screen and (prefers-color-scheme: dark);
|
||||||
|
```
|
||||||
|
|
||||||
|
[CSS Cascading and Inheritance Level 5]: https://drafts.csswg.org/css-cascade-5/
|
||||||
|
|
||||||
|
## Les évolutions que j'attends avec impatience
|
||||||
|
|
||||||
|
Voici quelques spécifications que j'ai hâte d'utiliser. Je ne les utilise pas
|
||||||
|
encore en raison du support des navigateurs ou parce que je n'en ai pas encore
|
||||||
|
eu besoin. Mais je suis impatient de les essayer.
|
||||||
|
|
||||||
|
### Mixins CSS
|
||||||
|
|
||||||
|
Les Mixins CSS sont également une fonctionnalité majeure de SASS, et favorisent
|
||||||
|
un code CSS plus propre et plus réutilisable.
|
||||||
|
|
||||||
|
CSS les aura avec le [Module des fonctions et mixins CSS], qui est encore un
|
||||||
|
brouillon, et dans lequel les mixins ne sont pas encore spécifiés.
|
||||||
|
|
||||||
|
En attendant, voici un exemple du [Guide des Mixins SASS] :
|
||||||
|
|
||||||
|
```scss
|
||||||
|
@mixin rtl($property, $ltr-value, $rtl-value) {
|
||||||
|
#{$property}: $ltr-value;
|
||||||
|
|
||||||
|
[dir=rtl] & {
|
||||||
|
#{$property}: $rtl-value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
@include rtl(float, left, right);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Bien que dans certains cas, cela puisse être facilement remplacé par des
|
||||||
|
variables CSS :
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--sidebar-float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
[dir=rtl] {
|
||||||
|
--sidebar-float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
float: var(--sidebar-float);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[Guide des Mixins SASS]: https://sass-lang.com/documentation/at-rules/mixin/#arguments
|
||||||
|
[Module des fonctions et mixins CSS]: https://www.w3.org/TR/2025/WD-css-mixins-1-20250515/
|
||||||
|
|
||||||
|
### Propriétés Personnalisées CSS
|
||||||
|
|
||||||
|
Celle-ci est une petite fonctionnalité sympa du module [CSS Properties and
|
||||||
|
Values API Level 1] qui étend les variables CSS.
|
||||||
|
|
||||||
|
Elles permettent de définir le type, la valeur initiale et la règle d'héritage
|
||||||
|
d'une variable personnalisée. Par exemple :
|
||||||
|
|
||||||
|
```css {linenos=true}
|
||||||
|
@property --my-color {
|
||||||
|
syntax: "<color>";
|
||||||
|
inherits: false;
|
||||||
|
initial-value: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary {
|
||||||
|
--my-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary {
|
||||||
|
--my-color: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: var(--my-color);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ici, la définition de `--my-color` à la ligne 12 n'est pas valide (c'est une
|
||||||
|
longueur et non une couleur). Comme la valeur de la propriété n'est pas héritée
|
||||||
|
d'un parent, la valeur initiale sera utilisée : un `<button class="secondary">`
|
||||||
|
aura un fond noir.
|
||||||
|
|
||||||
|
Cependant, comme la propriété est définie pour être une couleur, des linters
|
||||||
|
comme [Stylelint] et [ESLint] pourront attraper de telles erreurs, en plus
|
||||||
|
d'attraper les fautes de frappe dans les valeurs ou dans le nom de la propriété.
|
||||||
|
|
||||||
|
[CSS Properties and Values API Level 1]: https://drafts.css-houdini.org/css-properties-values-api
|
||||||
|
[Stylelint]: https://stylelint.io
|
||||||
|
[ESLint]: https://eslint.org
|
||||||
|
|
||||||
|
### Scopes CSS
|
||||||
|
|
||||||
|
Celle-ci est peut-être celle que j'attends le plus. Pour styliser un composant
|
||||||
|
d'interface utilisateur, il est souvent nécessaire de cibler un élément
|
||||||
|
spécifique du composant, et de répéter les sélecteurs :
|
||||||
|
|
||||||
|
```css
|
||||||
|
.card { /*...*/ }
|
||||||
|
.card article { /*...*/ }
|
||||||
|
.card article header { /*...*/ }
|
||||||
|
.card article footer { /*...*/ }
|
||||||
|
.card article footer button { /*...*/ }
|
||||||
|
```
|
||||||
|
|
||||||
|
Avec les modules CSS imbriqués, cela peut être simplifié en :
|
||||||
|
|
||||||
|
```css
|
||||||
|
.card {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
article {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
header {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
button {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Cela peut cependant avoir quelques cas particuliers et donner des résultats
|
||||||
|
inattendus (voir l'[exemple sur MDN]).
|
||||||
|
|
||||||
|
Les portées sont une nouvelle fonctionnalité du module [CSS Cascading and
|
||||||
|
Inheritance Level 6]. Elles sont une manière plus naturelle de définir des
|
||||||
|
règles :
|
||||||
|
|
||||||
|
```css
|
||||||
|
@scope (.card) {
|
||||||
|
:scope {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
header {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
/*...*/
|
||||||
|
|
||||||
|
button {
|
||||||
|
/*...*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
La puissance de @scope vient de plusieurs faits :
|
||||||
|
|
||||||
|
* Il suit des règles de proximité : un élément est stylisé avec les règles de
|
||||||
|
portée les plus proches ;
|
||||||
|
* Il n'ajoute aucune spécificité au sélecteur, ce qui signifie qu'il peut être
|
||||||
|
remplacé plus facilement ;
|
||||||
|
* Il est plus expressif.
|
||||||
|
|
||||||
|
[CSS Cascading and Inheritance Level 6]: https://drafts.csswg.org/css-cascade-6/
|
||||||
|
[exemple sur MDN]: https://developer.mozilla.org/en-US/docs/Web/CSS/@scope#how_scope_conflicts_are_resolved
|
Loading…
Add table
Add a link
Reference in a new issue