Псевдо коментарі в CSS (або як браузер парсити стилі)

17

Від автора: в специфікації про це нічого не сказано, але в файлах CSS можна імітувати C-подібні та/або Unix коментарі (з невеликими застереженнями). Багато статей було написано на цю тему. У даній же статті ми більш детально розглянемо їх.

CSS коментарі

У парсерах CSS, згідно специфікації, прийнятний тільки один стиль коментування: багаторядковий коментування, як у C-подібних мовах. Відкриває символ /*, кінцевий символ */. Тобто ось так можна проігнорувати стилі:

body {
background: red;
/*
background: white;
*/
}

Можна закоментувати цілий блок:

/*
body {
background: red;
}
*/

Також проігнорувати цілий блок можна випадково, припустившись помилки:

body {
background: red /* втрачена крапка з комою */
background: blue;
}

У цьому прикладі обидва селектора не спрацюють через пропущену крапки з комою в першому рядку. Парсер відокремлює селектори крапкою з комою, виявляє селектор, записаний у два рядки. З-за неправильного запису ігнорується весь контекст, тобто блок. Те ж саме буде, якщо пропустити значення властивості:

body {
background:
background: blue; /* це властивість не спрацює */
}

А це означає, що ми можемо використовувати неправильне оголошення властивостей, як…

Псевдо-коментарі

Ми називаємо таку форму запису «псевдо-коментарем», так як, по суті, це і не коментар зовсім. З-за своєї неправильно запису вони псують йде слідом властивість. Все через помилки під час обробки правил, оголошень блоків і селекторів:

«Якщо хоч в одному селекторі допущена помилка, буде проігноровано всі вираз, навіть якщо інші властивості записані за правилами CSS 2.1.»

У наступному прикладі з специфікації другий блок буде пропущено з-за зайвого символ «&» в селекторі:

h1, h2 {color: green }
h3, h4 & h5 {color: red } /* <= буде проігноровано */
h6 {color: black }

І знову, друге і третє властивість не спрацюють, так як в їх імена присутні зайві символи:

body {
background: red;
xbackground: white; /* властивість не знайдено */
y background: blue; /* назва властивості записано некоректно */
}

Швидкий лікнеп по англійських символів, які можна використовувати для однорядкових коментарів:

selector {
~ property-name: ignored;
` property-name: ignored;
! property-name: ignored;
@ property-name: ignored;
# property-name: ignored;
$ property-name: ignored;
% property-name: ignored;
^ property-name: ignored;
& property-name: ignored;
* property-name: ignored;
_ property-name: ignored;
— property-name: ignored;
+ property-name: ignored;
= property-name: ignored;
| property-name: ignored;
\ property-name: ignored;
: property-name: ignored;
property-name: ignored;
, property-name: ignored;
? property-name: ignored;
/ property-name: ignored;
}

Крім них можна використовувати й звичні C-подібні і Unix коментарі, # або //:

// background: ignored;
# background: ignored;

Крапка з комою

Крапка з комою є закінченням правила. Тобто їх не можна використовувати для коментування стоїть попереду тексту. У специфікації говориться, що парсер обробляє відсутність крапки з комою, як неправильне оголошення (пропущено назва властивості, двокрапка або значення).

Як вже згадувалося вище, при неправильному оголошенні багаторядкового коментаря, також ігнорується і внутрішнє оголошення. У прикладі нижче обидва властивості background будуть працювати, як коментарі, із-за того, що парсер буде шукати символ кінця оголошення (крапка з комою):

body {
background:
background: blue; /* обидві ігноруються */
}

Виправити це можна, додавши крапку з комою перед наступним властивістю (тобто властивість background: blue спрацює):

body {
background: ; /* ігнорується */
background: blue; /* виконується */
}

Той же ефект з пропущеної крапкою з комою і в псевдо-коментарях:

body {
background: # red /* ігнорується */
background: blue; /* також */
}

І працює, якщо повернути крапку з комою на місце:

body {
background: # red; /* ігнорується */
background: blue; /* обробляється */
}

Розміщення на тій же і на наступному рядку

Ось тут і підходить термін «псевдо». Може бути, і зовсім не варто називати цю форму запису «коментарем». Через псевдо-коментаря буде ігноруватися наступне за ним властивість. У наступному прикладі фон буде синім:

body {
//
background: white !important; /* ігнорується */
background: blue;
}

Псевдо-коментар, розміщений після правильно-оголошеного властивості на одній з ним рядку, буде придушувати виконання властивості на наступному рядку. У прикладі нижче фон буде білий, а не синій:

body {
background: white; // наступний рядок ігнорується…
background: blue !important;
}

Навіть у минифицированном вигляді, коли всі властивості записані в один рядок, псевдо-коментар поширюється тільки на слідом йде властивість. У прикладі нижче перше оголошення background ігнорується. Парсер доходить до символу кінця рядка, і друге оголошення background вже розпізнається. (фон буде синім):

body { // background: red !important; background: blue; }

Селектори

Ті ж правила застосовуються і до селекторам. Весь селектор буде проігнорований при наявності перед ним на одній з ним рядку псевдо-коментаря, навіть однорядкового:

// body {
background: white !important;
}

Або на наступному рядку:

//
body {
background: white !important;
}

Неправильна запис псевдо-коментарів

Робота псевдо-коментарів заснована на правилах по обробці помилок парсера. По суті, вони працюють із-за своєї ж неправильного запису.

Невідоме значення

«Юзер агенти повинні ігнорувати невідомі властивості». Оголошення з нерозпізнаними іменами властивостей не будуть працювати. Наприклад, властивість comment селектора body:

body {
comment: ‘текст або число’;
}

Неприпустимі значення

«Юзер агенти повинні ігнорувати оголошення з недопустимими значеннями.» Властивість color буде проігноровано, так як його значення записано у форматі рядка:

body {
color: red»;
}

Неправильні оголошення і вирази

«Юзер агенти повинні обробляти несподівані символи [або вираження] до кінця оголошення [або вираження], якщо дотримано правило парних дужок (), [], {} та лапок «».»

body {
-color: red;
}

Розбіжність символів (), [], {}, «» і » призводить до ігнорування всього блоку (і більш небезпечно). Символи лапок «» та » не обробляються так як дужки (), [], {}.

Символи лапок

Символ одиночної лапки на початку другого рядка в прикладі нижче зайвий, тому друга і третя рядки будуть проігноровані (т. е. фон буде червоний):

body {
background: red;
‘background: white; /* не виконується */
background: blue; /* також */
}

Однак третє оголошення після однієї лапки виконується (т. е. фон буде золотим):

body {
background: red;
‘background: white; /* не виконується */
background: blue; /* також */
background: gold; /* виконується */
}

Загалом, символом лапки не можна закоментувати рядок.

Символи дужок

В цілому, символи дужок (), [], {} не варто використовувати в псевдо-коментарях, т. к. вони мають куди більший ефект. Це пов’язано з можливістю парсера розпізнавати блоки. Їх використання може закоментувати більше одного блоку за раз. Для повноти картини, розглянемо деякі з них.

Наприклад, одиночний символ фігурної дужки заблокує взагалі всі наступні стилі до кінця файлу (не тільки поточного блоку). Те ж саме стосується і до ком, квадратним і звичайним дужкам. У прикладі нижче спрацює тільки властивість background: red;. Всі інші властивості будуть пропущені аж до кінця файлу:

body {
background: red;
{ /* *кожне* наступне властивість буде проігноровано,
включаючи всі наступні селектори аж до кінця файлу. */
background: white;
color: aqua;
margin: 5px;

}

При збігу парних символів, внутрішні властивості і наступні за закриває парним символом властивості будуть проігноровані. У прикладі нижче фон буде червоний, а не золотий:

body {
background: red;
(
background: white;
background: blue;
background: fuchsia;
)
background: gold;
}

Закриває кома або квадратна дужка пригнічують тільки наступне за ними властивість. У прикладі нижче фон буде блакитний:

body {
background: red;
]
background: white;
background: blue;
}

Однак закриває фігурна дужка } блокує взагалі всі наступні властивості в селекторі. У прикладі нижче фон буде червоний:

body {
background: red;
}
background: white;
background: blue;
}

@-правила

@-правила бувають у двох формах:

З фігурними дужками {…} як @media

З крапкою з комою ; як @charset

Псевдокомментарии в @-правила ведуть себе так само, як і в селекторах (тобто блокують весь блок).

Псевдо-коментарі @-правила з фігурними дужками

Якщо властивості знаходяться всередині селектора body, а той у свою чергу усередині @-правила типу @keyframes, @media @page або @font-face, і псевдо-коментар поставити перед @-правилом на одній з ним рядку, то весь блок стилів буде заблокований:

// @media (min-width: 0) {
body {
background: white !important;
}
}

Або на наступному рядку:

//
@media (min-width: 0) {
body {
background: white !important;
}
}

Псевдо-коментарі @-правила без фігурних дужок

Псевдо-коментарі в неблоковых @-правила типу @charset або @import блокують всі правило цілком. Псевдо-коментар після ключового слова блокує всю конструкцію:

/* Псевдо-коментар перед url блокує весь @import */
@import // url(‘libs/normalize.css’);

Однак псевдо-коментар перед @-правилом і блокує його і наступне за ним таке ж правило. Так відбувається тому, що парсер сприймає псевдо-коментар, як неправильно записане властивість, тим самим парсер його пропускає і шукає наступне за ним.

Тому псевдо-коментар перед одним @import заблокує серію з таких же @import’ів і перше властивість після останнього @import’а.

// @import url(‘libs/normalize.css’);
/* НІ ОДНЕ з виражень нижче не буде застосовано, т. к. аналізатор сприймає запис вище, як неправильну і буде шукати такі фігурні дужки. */
@import url(‘libs/normalize.css’);
@import url(‘libs/example.css’);
@import url(‘libs/other.css’);
@import url(‘libs/more.css’);
@import url(‘libs/another.css’);
@import url(‘libs/yetmore.css’);

Виправити це можна, додавши порожні фігурні дужки після закомментированного @import

// @import url(‘libs/normalize.css’);
{} /* тепер наступне вираз спрацює */
@import url(‘libs/normalize.css’);

Хоча це і весело погратися з налагодженням коду, краще уникати псевдо-коментарів @-правила без фігурних дужок з-за їх непередбачуваної поведінки.

@-правила і невідомі @-ключі

«Юзер агенти повинні ігнорувати неправильно записані ключові слова @-правила і все що слідує за ними аж до кінця блоку, в якому @-правило записано, або до крапки з комою, або до наступних фігурних дужок»

Проілюструвати дану ситуацію можна за допомогою невідомого @-правила @comment з фігурними дужками. Наприклад, правило нижче парс до закриває фігурної дужки }, розпізнано неправильно, записаним і пропускається:

@comment {
Не виконується взагалі.
}

Спершу, це може здатися взагалі нешкідливим, блок просто пропускається. Однак одинарна лапка повинна десь закритися. З-за відсутності другий одинарної закриває лапки на тій же рядку ми отримуємо помилку. Це призводить до того, що якщо закриває фігурна дужка в @-правилі знаходиться на тому ж рядку, що й одинарна лапка, то наступне @-правило або селектор теж будуть блокуватися.

@comment {
Не ‘виконується взагалі }
body { background: blue; } /* весь цей блок теж буде заблокований! */

Виправити це можна за допомогою подвійних лапок:

@comment {
«Не ‘виконується взагалі» } /* пофиксили */
body { background: blue; } /* цей селектор буде працювати */

Чи можна стерти фігурні дужки, перенести вираз на один рядок і додати в кінці крапку з комою:

@comment «Не виконується взагалі»;
body { background: blue; } /* працює */

Або на наступному рядку:

@comment
«Не ‘виконується взагалі»;
body { background: blue; } /* працює */

Препроцессоры

У різних препроцессорах CSS підтримуються схожі однорядкові і багаторядкові коментарі.

Sass

«У Sass підтримуються стандартні багаторядкові CSS коментарі /* */ і однорядкові //. Багаторядкові коментарі при компіляції зберігаються, а однорядкові видаляються.»

Джерело

В режимі стиснення, зазвичай, видаляються всі коментарі, відкриває якщо символ не починається з /*!. Однорядкові коментарі також можна залишити на виході з допомогою символу #.

body {
# background: red;
}

Less

Можна використовувати і блокові і однорядкові коментарі.»

Джерело

Я не дуже розумію, у яких випадках в Less коментарі видаляються або залишаються при компіляції. За інформацією з сайту StackOverflow лінійні коментарі збережуться на виході, якщо вони знаходяться всередині фігурних дужок.

Stylus

В Stylus теж присутні як /* */, так і // коментарі. Але вони будуть видалятися при компіляції при включеній директиві compress. Якщо вам потрібно, щоб багаторядкові коментарі завжди залишалися на виході, скористайтесь многострочным буферним коментарем.

«Багаторядкові коментарі не видаляються, якщо вони починаються з /*!. У такому разі Stylus залишить їх при компіляції, незважаючи на режим стиснення.»

/*!
* Цей коментар залишиться.
*/

Джерело

Кращі практики

«Читабильность можна порахувати»

https://www.python.org/dev/peps/pep-0020/>Zen of Python

Коментарі додають ясності коду, але його сприйняття залежить не тільки від цього параметра. Псевдо-коментарі в CSS меншою мірою призначені для людини, але для парсера. Якщо раптом вам потрібно скористатися псевдо-коментарями:

Використовуйте символи C-подібних і unix коментарів // або #.

Розмістіть псевдо-коментар над тим правилом, яке необхідно проігнорувати.

Або використовуйте пробіл на одному рядку з правилом, щоб заблокувати тільки одна властивість # background: ignored;

Застосування псевдо-коментарів:

Для налагодження, особливо в поєднанні з CSS панеллю розробника типу Web Developer extension (chrome, firefox, opera).

Для блокування оголошень, селекторів або @-правил з фігурними дужками.

Не використовуйте псевдо-коментарі:

Для пояснення контексту в @-правила без фігурних дужок (типу @import). Використовуйте звичайні /*…*/

Уникайте символів лапок. Людині їх важко розрізнити.

Уникайте дужки (), [], {} – вони тільки ускладнять вам роботу з налагодженням.

Не використовуйте псевдо-коментарі в готовому мінімізованому коді – вони не знищать ваш код, але все ж пару зайвих байт додадуть.