Никита Дубко, HR Tech Яндекса
Никита Дубко, HR Tech Яндекса
@media (prefers-color-scheme: dark) {
html {
color-scheme: dark;
}
}
В докладе почти не будет упоминаний селекторов, связанных с Shadow DOM. Если вы зачем-то продолжаете читать этот длинный текст, то вот вам пара интересных фактов. Человеческий мозг имеет возможность хранить всё, что человек видит и слышит в течение всей жизни. Улитки могут спать три года, не употребляя никакой пищи. Хочу быть улиткой.
A–Z
, a–z
0–9
-
_
U+00A0
и выше\escaped
[0–9]
или -[0–9]
<div class="Pos(a) Bgc(#0280ae) W(120px) H(90px)"></div>
<div class="C(#0280ae) BdB Bdc(#0280ae) P(10px)">
Lorem ipsum
</div>
npm i @bramus/specificity
specificity "header:where(#top) nav li, #doormat"
(0,1,3)
(1,0,0)
(10, 1, 2)
(0, 10, 2)
(0, 2, 10)
(0, 1, 10)
(0, 1, 1)
(0, 0, 0)
#id
.class
div
*
@namespace svg url('http://www.w3.org/2000/svg');
a {
color: orangered;
}
svg|a {
fill: blueviolet;
}
@namespace
[attr]
[a=v]
[a~=v]
[a|=v]
[a^=v]
[a$=v]
[a*=v]
[a=v i]
[a=v s]
[id=some]
[class~=some]
.parent .child
,
.parent > .child
.child + .child
.child ~ .child
.parent {
color: tomato;
.child {
color: violet;
&:hover {
color: violetblue;
}
}
}
::before
:before
:before
:after
:first-line
:first-letter
::before
::after
::first-letter
::first-line
::backdrop
::placeholder
span::before {
content: "ⓘ" / "Дополнительная информация:";
}
::before
::marker
::cue
<video src="video.mp4">
<track default kind="captions" srclang="en" src="en.vtt">
</video>
<style>
video {
width: 100%;
}
video::cue {
font-size: 1rem;
color: yellow;
}
</style>
::selection
::target-text
https://mefody.dev/chunks/selection/
#:~:text=There%20is%C2%A0another%20selection
::grammar-error
, ::spelling-error
::view-transition
::view-transition
└─ ::view-transition-group(root)
└─ ::view-transition-image-pair(root)
├─ ::view-transition-old(root)
└─ ::view-transition-new(root)
По-настоящему красивые переходы средствами браузера
::file-selector-button
::highlight()
:root
:link
:visited
:any-link
:focus
:active
:hover
:placeholder-shown
:disabled
:enabled
:optional
:required
:picture-in-picture
:focus-visible
.button:focus {
outline: none;
}
.button:focus-visible {
border: 2px solid #FFFFFF;
outline: none;
}
:focus-visible
:focus-within
:target
<a href="#target">К цели!</a>
<div id="target">А вот и я!</div>
:target-within
:checked
, :indeterminate
:in-range
,:out-of-range
:read-only
,:read-write
:invalid
:valid
:user-invalid
:user-valid
:empty
div:empty {
outline: 2px solid deeppink;
background: deeppink;
height: 1em;
}
:default
:autofill
input:-internal-autofill-selected {
appearance: menulist-button;
background-image: none !important;
background-color: light-dark(
rgb(255, 235, 153),
rgba(70, 90, 126, 0.4)
) !important;
color: fieldtext !important;
}
:buffering
:muted
:paused
:playing
:seeking
:stalled
:volume-locked
:defined
:scope
:popover-open
:lang()
<p lang="de">Wie spät ist es?</p>
<p lang="ru">Который час?</p>
<style>
:lang(ru) {
font-family: SomeCyrillicFont, sans-serif;
}
:lang(de) {
font-family: SomeGermanFont, sans-serif;
}
</style>
:dir()
nav {
text-align: right;
}
nav:dir(rtl) {
text-align: left;
}
:only-child
,:only-of-type
:first-child
,:first-of-type
,:last-child
,:last-of-type
:not()
:not(#id)
:not(.class)
.card {
margin-right: 2em;
}
.card:last-child {
margin-right: 0;
}
.card:not(:last-child) {
margin-right: 2em;
}
:is(#id)
article h1,
article h2,
article h3,
article h4 {
font-weight: bold;
}
article :is(h1, h2, h3, h4) {
font-weight: bold;
}
article h1,
article h2,
article h3,
article h4 {
font-weight: bold;
}
article :where(h1, h2, h3, h4) {
font-weight: bold;
}
:where(#id)
:nth-child()
:nth-child(2n+1 of li.test)
:nth-last-child(3n+2)
:nth-last-child(6):first-child {
background: tomato;
}
a:nth-last-child(6):first-child,
a:nth-last-child(6):first-child ~ a {
background: tomato;
}
:nth-of-type(3n+2)
:nth-last-of-type(3n+2)
:has()
.card:has(
:any-link:not[href^="https://mefody.dev/"]
) {
background: maroon;
}
<article>
<div>
<p>Я параграф. Красивый такой параграф.</p>
</div>
</article>
<style>
div:has(article p) {
font-weight: bold;
}
</style>
Интересный случай с селекторами
<article>
<div>
<p>Я параграф. Красивый такой параграф.</p>
</div>
</article>
<style>
/* ❌ */
div:has(article p) {
font-weight: bold;
}
</style>
Интересный случай с селекторами
<article>
<div>
<p>Я параграф. Красивый такой параграф.</p>
</div>
</article>
<style>
/* ✅ */
div:has(:is(article p)) {
font-weight: bold;
}
</style>
Интересный случай с селекторами
const ParentComponent: FC = (items) => {
let size = 'default';
if (items.length >= 6) {
size = 'big';
}
return <ChildComponent size={size} />;
}
.cards {
grid-template-columns: repeat(2, 1fr);
}
.cards.cards_size_big {
grid-template-columns: repeat(3, 1fr);
}
.cards {
grid-template-columns: repeat(2, 1fr);
}
.cards:has(.card:nth-child(6)) {
grid-template-columns: repeat(3, 1fr);
}
ul:has(
> :nth-child(7)
):has(
> :nth-child(-n+9):last-child
)
Style a parent element based on its number of children using CSS :has()
.img:has(+ p) {
margin-bottom: 2em;
}
.card:has(.button:hover) {
/* TODO */
}
.bucket:not(:has(.item)) {
/* Корзина пуста */
}
ul:has(li:has(a))
column||cell
<table>
<colgroup>
<col span="2">
<col>
</colgroup>
<tbody>
<tr>
<td>1.1</td>
<td>1.2</td>
<td>2</td>
</tr>
</tbody>
</table>
Column combinator
form:not([action])::after {
content: 'Forms must have action attributes.' !important;
}
[tabindex]:not([tabindex="0"]):not([tabindex="-1"])::after {
content: 'Do not disrupt the natural tab order.' !important;
}
head,
head script,
head script[type]:not([type="text/javascript"]) {
display: block;
}
head script[src]::before {
content: "[Blocking Script – " attr(src) "]";
}
head style:not(:empty) ~ script::before {
content: "[JS blocked by CSS – " attr(src) "]";
}