Селектори и каскада
Накратко: CSS селекторът избира елементите, за които е правилото, а каскадата решава кое правило печели при спор: произход, специфичност и ред.
Проблем
Цената не става червена
Магазинът пуска промоция: цената на хляба трябва да е червена. Колега добавя правило за .price най-долу в style.css. Цената става по-голяма и удебелена, но остава тъмносива.
Конзола
Конзолата е празна: CSS не дава грешки. Правилото е последно във файла и хваща цената, защото размерът и удебеляването минават. Не минава само цветът. Друго правило иска друг цвят за същия абзац, и браузърът е избрал него.
Идеята
Правило и селектор
CSS казва как изглежда всеки елемент от дървото. Казва го с правила:
.price { color: #c0392b; font-size: 20px;}.priceе селекторът: кои елементи хваща правилото.- В скобите са декларациите: свойство, двоеточие, стойност и точка и запетая.
Селекторите търсят в дървото, затова са като въпроси към него:
p(тип): всеки<p>..price(клас): всеки елемент сclass="price". Елементът може да има няколко класа:class="price sale".#product(id): елементът сid="product".[type="email"](атрибут): елементите с този атрибут и тази стойност, напримерinput[type="email"].article p(наследник, с интервал):<p>някъде в<article>, на каквато и да е дълбочина.nav > a(дете):<a>, който е пряко дете на<nav>..price.sale(без интервал): един елемент с двата класа.h1, h2(списък): правилото важи и за двата.
Стиловете се пишат главно с класове. Типът хваща всеки абзац на сайта, а id е за един елемент. Класът слагаш точно където трябва, и той не зависи от това как е подредено дървото.
Пробвай
Хвани точно тези
Махни подчертаването само на връзките в менюто (връзката в долната част остава подчертана) и удебели двете цени, без да удебеляваш описанията. Свойствата са text-decoration: none и font-weight: 700.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<title>Магазинът</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<a href="/bread">Хляб</a>
<a href="/milk">Мляко</a>
<a href="/cart">Кошница</a>
</nav>
</header>
<main>
<article>
<h2>Хляб „Добруджа“</h2>
<p>Бял хляб, 650 г, изпечен тази сутрин.</p>
<p class="price">1,80 €</p>
</article>
<article>
<h2>Прясно мляко 3%</h2>
<p>Литър, от ферма край Тетевен.</p>
<p class="price">2,40 €</p>
</article>
</main>
<footer>
<a href="/terms">Условия за доставка</a>
</footer>
</body>
</html>
style.cssbody { font-family: system-ui, sans-serif; margin: 16px; }
nav { display: flex; gap: 16px; }
nav a { text-decoration: none; }
.price { font-weight: 700; }
nav a хваща само връзките в <nav>, а a би хванал и връзката в долната част. .price хваща двата абзаца с класа, а p би удебелил и описанията. Без точката price търси елемент <price>, а такъв няма.
Идеята
Каскадата
Цената от началото се хваща от две правила, и двете задават color. Браузърът трябва да избере едно. Това правило за избор се казва каскада. Тя сравнява на три нива и спира на първото, което реши:
- Произход. Браузърът има свои стилове: затова
<h1>е голям, а връзката е синя и подчертана. Твоят CSS ги бие. - Специфичност. Колко точен е селекторът. Тя е три числа: колко id, колко класа, колко типа.
pе 0,0,1,.priceе 0,1,0,#product pе 1,0,1. Атрибутите ([type="email"]) и състоянията (:hover) броят като класове. Числата се сравняват колона по колона отляво. Щом една колона е различна, решено е: 1,0,1 бие 0,1,0. - Ред. При равна специфичност печели правилото, което е по-късно в кода.
Каскадата решава всяко свойство поотделно. Затова от .price минават font-size и font-weight: за тях друг кандидат няма. За color има, и той е по-силен.
Има и !important: color: #c0392b !important бие всяка обикновена декларация, каквато и да е специфичността. Работи, но после всяка промяна на цвета иска ново !important. Затова е знак, че някъде има твърде силен селектор, а не поправка.
Виж го
Виж как се избира
Четири правила и един елемент. Гледай как браузърът решава color и font-size поотделно.
/* <article id="product"> <p class="price sale">1,80 €</p> </article> */p { color: #555; font-size: 16px; }.price { color: #c0392b; font-size: 20px; }#product p { color: #222; }.sale { font-size: 24px; }Стъпка по стъпка, с думи
- Един елемент: <p> с класовете price и sale, в <article id="product">. И четирите селектора го хващат. Две от правилата искат да зададат color, а три искат font-size.
- Специфичността на селектора е три числа: колко id, колко класа, колко типа. #product p има едно id и един тип: 1,0,1.
- color: задават го три правила. Браузърът сравнява първата колона: 1 бие 0 и по-нататък не гледа. #product p печели, макар че .price е след него.
- font-size: пак три правила. Колоната id е равна (0). В колоната клас p отпада с 0. .price и .sale са равни и в трите колони.
- При равна специфичност печели правилото, което е по-късно в кода. .sale е на ред 7, след .price, затова font-size е 24px.
- Всяко свойство има свой победител: color от #product p, font-size от .sale. Правилото .price хваща елемента и пак губи и в двете.
Ето същият код в браузъра. Цената е тъмносива и в 24px, както в сцената. Пробвай да размениш местата на .price и .sale: размерът се сменя, а цветът не. Отвори инструментите за разработчици (F12), избери цената и виж панела Styles: загубилите декларации са зачертани.
Конзола
Идеята
Наследяване
Някои свойства минават от родителя към децата. Ако елементът няма свое правило за тях, той взима стойността на родителя. Това е наследяване.
- Наследяват се свойствата на текста:
color,font-family,font-size,font-weight,line-height,text-align. - Не се наследяват свойствата на кутията:
border,padding,margin,background,width. Рамка около картата не значи рамка около всеки абзац в нея.
Наследената стойност е най-слабата. Всяко правило, което хваща елемента пряко, я бие, дори p с 0,0,1. Бутоните и полетата имат свой шрифт и цвят от браузъра, затова не наследяват. inherit иска наследяване изрично: button { font-family: inherit; }.
Конзола
Текстът на картата е зелен и с шрифта Georgia, но рамката е само около картата. Бутонът взе шрифта заради inherit, а цветът му е останал черен: за color такова правило няма.
Пробвай
Поправи цената
Направи цената червена (#c0392b), без !important. Описанието трябва да остане тъмносиво (#222).
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<title>Хляб „Добруджа“</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<article id="product">
<h1>Хляб „Добруджа“</h1>
<p>Бял хляб, 650 г, изпечен тази сутрин.</p>
<p class="price">1,80 €</p>
<button type="button">Добави в кошницата</button>
</article>
</body>
</html>
style.cssbody { font-family: system-ui, sans-serif; margin: 16px; }
#product { color: #222; line-height: 1.5; }
h1 { font-size: 24px; }
/* Промоция: цената е червена */
.price { color: #c0392b; font-size: 20px; font-weight: 700; }
Не трябва по-силен селектор за цената, а по-слаб за описанието. #product { color: #222; line-height: 1.5; } дава цвета на статията, и абзаците го наследяват. Наследеното губи от .price, което хваща цената пряко. #product .price (1,1,0) също минава, но така следващата промяна ще иска още по-силен селектор. p.price не стига: 0,1,1 пак губи от 1,0,1 още в първата колона.
Пробвай
Намери грешката
Цената има класовете price и sale, но не е червена. Защо?
Избери реда с грешката.
Интервалът в .price .sale значи „елемент с клас sale вътре в елемент с клас price“. Тук двата класа са на един елемент, а за това се пишат слепени: .price.sale.
Пробвай
Попълни
Менюто има връзки и вложен списък с още връзки. Само връзките, които са преки деца на <nav>, трябва да са без подчертаване.
nav a {
text-decoration: none;
}> хваща само преките деца. С интервал (nav a) правилото би хванало и връзките във вложения списък.
По-точно, за любопитните
Произходът. Източниците са три: браузърът, потребителят (настройки и разширения) и авторът, тоест ти. При обикновените декларации авторът бие потребителя, а потребителят бие браузъра. При !important редът се обръща: !important на потребителя бие твоето. Така човек със слабо зрение може да наложи по-голям шрифт на всеки сайт.
style="…". Стилът в атрибута на елемента бие всеки селектор от файла. Затова и той е капан, както !important.
Колоните не преливат. 0,11,0 не е 1,1,0. Единадесет класа не бият едно id: сравнението свършва още в първата колона.
Какво не брои. * е 0,0,0. Комбинаторите (интервал, >, +, ~) не добавят нищо. Псевдоелементите (::before) броят като тип.
:is(), :not() и :where(). :is() и :not() вземат специфичността на най-силния си аргумент. :where() е винаги 0,0,0: удобно е за стилове по подразбиране, които всяко правило надвива.
Слоеве. @layer подрежда цели групи правила, и слоят решава преди специфичността. Големите библиотеки със стилове го ползват, за да не се бият с твоя CSS.
Ключовите думи. inherit взима стойността на родителя. initial е началната стойност на свойството от стандарта (за display това е inline). unset е inherit за наследяемите свойства и initial за другите. revert връща стила на браузъра.
Упражнение
Промоцията в каталога
Каталогът има три продукта, а млякото е в промоция (класът sale на картата). Целият текст в каталога е #222, а цените са удебелени. Цената на продукта в промоция е червена (#c0392b), а старата цена в нея е сива (#777). Етикетът −20% е с бял текст (#fff) на червен фон (#c0392b). Другите цени остават #222. Не ползвай !important.
Конзола
Решение
index.html<!doctype html>
<html lang="bg">
<head>
<meta charset="utf-8">
<title>Каталог</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<main id="catalog">
<article class="product">
<h2>Хляб „Добруджа“</h2>
<p class="price">1,80 €</p>
</article>
<article class="product sale">
<h2>Прясно мляко 3%</h2>
<p class="badge">−20%</p>
<p class="price"><s class="old">2,40 €</s> 1,92 €</p>
</article>
<article class="product">
<h2>Масло 125 г</h2>
<p class="price">3,20 €</p>
</article>
</main>
</body>
</html>
style.cssbody { font-family: system-ui, sans-serif; margin: 16px; }
#catalog { color: #222; }
.price { font-weight: 700; }
.sale .price { color: #c0392b; }
.old { color: #777; }
.badge { color: #fff; background: #c0392b; }
#catalog p хваща всеки абзац с 1,0,1 и бие всяко правило с класове. Затова цветът отива на #catalog, и текстът го наследява. После .sale .price (0,2,0) хваща само цената в промоция, .old дава сивото на старата цена (иначе тя би наследила червеното от абзаца), а .badge дава цвета и фона на етикета. Заглавията също са #222: те наследяват от #catalog.
Тест
Провери се
Свързани уроци
Раздел „Свързани уроци“- Нужно отпреди: HTML документът · Формуляри
- Продължава в: Box model · Flexbox: ред и колона · Grid: редове и колони · Responsive дизайн
- Задачи по темата: CSS: как изглежда страницата