init
This commit is contained in:
parent
06f61d8ce8
commit
f301cb744d
2271 changed files with 103162 additions and 0 deletions
|
@ -0,0 +1,33 @@
|
|||
Несмотря на то, что меню более-менее прилично отображается, эта вёрстка совершенно не семантична.
|
||||
|
||||
Ошибки:
|
||||
<ol>
|
||||
<li>Во-первых, меню представляет собой *список элементов*, а для списка существует тег `LI`.
|
||||
|
||||
**Семантический подход -- это когда теги используются по назначению.** Для элементов списка `<li>`, для адреса `<address>`, для заголовка таблицы `<th>` и т.п.
|
||||
</li>
|
||||
<li>Во-вторых, класс `rounded-horizontal-blocks` показывает, что содержимое должно быть *оформлено* как скругленные горизонтальные блоки. Любой класс, отражающий оформление, несемантичен.
|
||||
|
||||
**Правильно -- чтобы класс был *смысловым***. Например, `<ul class="menu">` будет говорить о том, что смысл элемента -- "меню".</li>
|
||||
<li>В-третьих, элемент `.vertical-splitter`. Здесь класс вполне семантичен, этот элемент списка является вертикальным разделителем, так что здесь всё в порядке. Но на этот раз несемантичность -- в содержимом.
|
||||
|
||||
**Мы, по возможности, стараемся, чтобы HTML содержал именно информацию, а символ вертикальной черты`|` выполняет чисто оформительскую функцию.**
|
||||
|
||||
Поэтому от него следует либо вообще избавиться, либо переместить в CSS при помощи `::before`.</li>
|
||||
</ol>
|
||||
|
||||
И, наконец, это не обязательно и не ошибка, но обычно элементы, которые являются ссылками или кнопками, оформляют в `<a>` или `<button>`.
|
||||
|
||||
Вариант ниже -- семантичен:
|
||||
|
||||
```html
|
||||
<ul class="menu">
|
||||
<li class="item"><a href="#">Главная</a></li>
|
||||
<li class="vertical-splitter"></li>
|
||||
<li class="item"><a href="#">Товары</a></li>
|
||||
<li class="item"><a href="#">Фотографии</a></li>
|
||||
<li class="item"><a href="#">Контакты</a></li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
[edit src="solution"]Полное меню со стилями[/edit]
|
1
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/solution/.plnkr
Executable file
1
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/solution/.plnkr
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"menu-semantic","plunk":"3H9dtlru2CjxYJNDl3RQ"}
|
54
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/solution/index.html
Executable file
54
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/solution/index.html
Executable file
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu li {
|
||||
list-style: none;
|
||||
|
||||
float: left;
|
||||
padding: 6px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.menu li.item {
|
||||
border: 1px solid gray;
|
||||
border-radius: 10px;
|
||||
|
||||
cursor: pointer;
|
||||
font-size: 90%;
|
||||
background: #FFF5EE;
|
||||
}
|
||||
|
||||
.menu li.vertical-splitter:before {
|
||||
content: "|";
|
||||
}
|
||||
|
||||
.menu .item a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.menu .item a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<ul class="menu">
|
||||
<li class="item"><a href="#">Главная</a></li>
|
||||
<li class="vertical-splitter"></li>
|
||||
<li class="item"><a href="#">Товары</a></li>
|
||||
<li class="item"><a href="#">Фотографии</a></li>
|
||||
<li class="item"><a href="#">Контакты</a></li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
49
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/task.md
Normal file
49
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/task.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Семантическое меню
|
||||
|
||||
[importance 5]
|
||||
|
||||
Посмотрите на вёрстку горизонтального меню.
|
||||
|
||||
```html
|
||||
<div class="rounded-horizontal-blocks">
|
||||
<div class="item">Главная</div>
|
||||
<div class="vertical-splitter">|</div>
|
||||
<div class="item">Товары</div>
|
||||
<div class="item">Фотографии</div>
|
||||
<div class="item">Контакты</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
|
||||
```css
|
||||
/*+ hide="Результат со стилями (показать стили)" */
|
||||
.rounded-horizontal-blocks .item {
|
||||
float: left;
|
||||
padding: 6px;
|
||||
margin: 0 2px;
|
||||
|
||||
border: 1px solid gray;
|
||||
border-radius: 10px;
|
||||
|
||||
cursor: pointer;
|
||||
font-size: 90%;
|
||||
background: #FFF5EE;
|
||||
}
|
||||
|
||||
.vertical-splitter {
|
||||
float: left;
|
||||
padding: 6px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
```
|
||||
|
||||
[iframe src="task" border=1 height=50 edit link]
|
||||
|
||||
Что делает эту вёрстку несемантичной? Найдите 3 ошибки.
|
||||
|
||||
Как бы вы сверстали меню правильно?
|
1
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/task/.plnkr
Executable file
1
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/task/.plnkr
Executable file
|
@ -0,0 +1 @@
|
|||
{"name":"menu-semantic-src","plunk":"WniZKDl3PHrw64PBEXdm"}
|
41
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/task/index.html
Executable file
41
02-ui/05-widgets/03-widgets-markup/01-semantic-menu/task/index.html
Executable file
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.rounded-horizontal-blocks .item {
|
||||
float: left;
|
||||
padding: 6px;
|
||||
margin: 0 2px;
|
||||
|
||||
border: 1px solid gray;
|
||||
border-radius: 10px;
|
||||
|
||||
cursor: pointer;
|
||||
font-size: 90%;
|
||||
background: #FFF5EE;
|
||||
}
|
||||
|
||||
.vertical-splitter {
|
||||
float: left;
|
||||
padding: 6px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="rounded-horizontal-blocks">
|
||||
<div class="item">Главная</div>
|
||||
<div class="vertical-splitter">|</div>
|
||||
<div class="item">Товары</div>
|
||||
<div class="item">Фотографии</div>
|
||||
<div class="item">Контакты</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
<ul>
|
||||
<li>Самая главная ошибка: классы без префиксов. Это означает, что если внутри содержимого будет что-либо с классом `.headers`, то стили оформят это как заголовок таба. Что, конечно же, будет неверно.</li>
|
||||
<li>Класс `selected`, соответствующий "текущей" выбранной вкладке, находится на ссылке:
|
||||
|
||||
```html
|
||||
<li><a href="#tabs-3" class="selected">Открытая вкладка</a></li>
|
||||
```
|
||||
|
||||
...Но состояние "выбранности" относится к заголовку `LI` целиком. Для его нормального отображения класс должен быть на `LI`.</li>
|
||||
<li>В использовании `.tabs > div` особо криминала нет, но нужно учесть, что добавить `DIV` с другим функционалом на этот уровень будет затруднительно. Иначе говоря, такая вёрстка усложняет расширение виджета. Лучше использовать для вкладки класс с префиксом `<div class="tabs-tab">` и, соответственно, селектор `.tabs-tab`.</li>
|
||||
</ul>
|
||||
|
||||
Правильный вариант:
|
||||
|
||||
```html
|
||||
<div id="tabs" class="tabs">
|
||||
<ul class="*!*tabs-headers*/!*">
|
||||
<li><a href="#tabs-1">Вкладка 1</a></li>
|
||||
<li><a href="#tabs-2">Вкладка 2</a></li>
|
||||
<li *!*class="tabs-selected"*/!*><a href="#tabs-3">Открытая..</a></li>
|
||||
</ul>
|
||||
<div *!*class="tabs-tab"*/!* id="tabs-1">Содержимое...</div>
|
||||
<div *!*class="tabs-tab"*/!* id="tabs-2">Содержимое...</div>
|
||||
<div *!*class="tabs-tab"*/!* id="tabs-3" class="*!*tabs-selected*/!*">
|
||||
Посетитель видит содержимое третьей вкладки.
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
BIN
02-ui/05-widgets/03-widgets-markup/02-markup-errors/tabs-example.png
Executable file
BIN
02-ui/05-widgets/03-widgets-markup/02-markup-errors/tabs-example.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
44
02-ui/05-widgets/03-widgets-markup/02-markup-errors/task.md
Normal file
44
02-ui/05-widgets/03-widgets-markup/02-markup-errors/task.md
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Ошибки в вёрстке
|
||||
|
||||
[importance 5]
|
||||
|
||||
Посмотрите на вёрстку "табов" (вкладок). Какие ошибки вы в ней видите? Как правильно?
|
||||
|
||||
```html
|
||||
<div id="tabs" class="tabs">
|
||||
<ul class="headers">
|
||||
<li><a href="#tabs-1">Вкладка 1</a></li>
|
||||
<li><a href="#tabs-2">Вкладка 2</a></li>
|
||||
<li><a href="#tabs-3" class="selected">Открытая вкладка</a></li>
|
||||
</ul>
|
||||
<div id="tabs-1">Содержимое в первой вкладке.</div>
|
||||
<div id="tabs-2">Содержимое во второй вкладке.</div>
|
||||
<div id="tabs-3" class="selected">
|
||||
Посетитель видит содержимое третьей вкладки.
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
|
||||
```css
|
||||
.tabs .headers {
|
||||
/* стиль для заголовков вкладок */
|
||||
}
|
||||
|
||||
.tabs .headers .selected {
|
||||
/* стиль для заголовка выбранной вкладки */
|
||||
}
|
||||
|
||||
.tabs > div {
|
||||
/* стиль для обычной вкладки */
|
||||
}
|
||||
|
||||
.tabs > div.selected {
|
||||
/* стиль для выбранной вкладки */
|
||||
}
|
||||
```
|
||||
|
||||
Примерный внешний вид:
|
||||
|
||||
<img src="tabs-example.png">
|
193
02-ui/05-widgets/03-widgets-markup/article.md
Normal file
193
02-ui/05-widgets/03-widgets-markup/article.md
Normal file
|
@ -0,0 +1,193 @@
|
|||
# Вёрстка графических компонентов
|
||||
|
||||
При создании графических компонент ("виджетов") в первую очередь придумывается их HTML/CSS-структура.
|
||||
|
||||
Как будет выглядеть виджет в обычном состоянии? Как будет меняться в процессе взаимодействия с посетителем?
|
||||
|
||||
Чтобы разработка виджета была удобной, при вёрстке полезно соблюдать несколько простых, но очень важных соглашений.
|
||||
|
||||
[cut]
|
||||
|
||||
## Семантическая вёрстка
|
||||
|
||||
**HTML-разметка и названия CSS-классов должны отражать не оформление, а смысл.**
|
||||
|
||||
Например, сообщение об ошибке можно сверстать так:
|
||||
|
||||
```html
|
||||
<div *!*style="color:red; border: 1px solid red"*/!*>
|
||||
Плохая вёрстка сообщения об ошибке: атрибут style!
|
||||
</div>
|
||||
```
|
||||
|
||||
...Или так:
|
||||
|
||||
```html
|
||||
<div *!*class="red red-border"*/!*>
|
||||
Плохая вёрстка сообщения об ошибке: несемантический class!
|
||||
</div>
|
||||
```
|
||||
|
||||
В обоих случаях вёрстка не является семантической. В первом случае -- стиль, а во втором -- класс содержат информацию об *оформлении*.
|
||||
|
||||
**При семантической вёрстке классы описывают смысл ("что это?" -- меню, кнопка...) и состояние (открыто, закрыто, отключено...) компонента.**
|
||||
|
||||
Например:
|
||||
|
||||
```html
|
||||
<div *!*class="error"*/!*>
|
||||
Сообщение об ошибке (error), правильная вёрстка!
|
||||
</div>
|
||||
```
|
||||
|
||||
У предупреждения будет класс `message` и так далее, по смыслу.
|
||||
|
||||
```html
|
||||
<div *!*class="warning"*/!*>
|
||||
Предупреждение (warning), правильная вёрстка!
|
||||
</div>
|
||||
```
|
||||
|
||||
**Семантическая верстка упрощает поддержку и развитие CSS, упрощает взаимодействие между членами команды.**
|
||||
|
||||
Такая верстка удобна для организации JS-кода. В коде мы просто ставим нужный класс, остальное делает CSS.
|
||||
|
||||
## Состояние виджета -- класс на элементе
|
||||
|
||||
Зачастую компонент может иметь несколько состояний. Например, меню может быть открыто или закрыто.
|
||||
|
||||
**Состояние должно добавляться CSS-классом не на тот элемент, который нужно скрыть/показать/..., а на тот, к которому оно "по смыслу" относится, обычно -- на корневой элемент.**
|
||||
|
||||
Например, меню в закрытом состоянии скрывает свой список элементов. Класс `open` нужно добавлять не к списку опций `<ul>`, который скрывается-показывается, а к *корневому элементу* виджета, поскольку это состояние касается всего меню:
|
||||
|
||||
```html
|
||||
<div class="menu *!*open*/!*">
|
||||
<span class="title">Заголовок меню</span>
|
||||
<ul>
|
||||
<li>Список элементов</li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
Ещё пример -- индикатор загрузки:
|
||||
|
||||
```html
|
||||
<div class="loader">
|
||||
<span class="progress">Тут показывается прогресс</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
Состояние индикатора может быть "в процессе" (loading) или "загрузка завершена" (complete). С точки зрения оформления оно может влиять только на показ внутреннего `span`, но ставить его нужно всё равно на внешний элемент, ведь это -- состояние всего компонента:
|
||||
|
||||
```html
|
||||
<div class="loader *!*loading*/!*">
|
||||
<span class="progress">Тут показывается прогресс</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
Из примеров выше можно подумать, что классы, описывающие состояние, всегда ставятся на корневой элемент. Но это не так.
|
||||
|
||||
Возможно и такое, что состояние относится к внутреннему элементу. Например, для дерева состояние открыт/закрыт относится к узлу, соответственно, класс должен быть на узле.
|
||||
|
||||
Например:
|
||||
|
||||
```html
|
||||
<ul class="tree">
|
||||
<li class="*!*closed*/!*">
|
||||
Закрытый узел дерева
|
||||
</li>
|
||||
<li class="*!*open*/!*">
|
||||
Открытый узел дерева
|
||||
</li>
|
||||
...
|
||||
</ul>
|
||||
```
|
||||
|
||||
На практике, даже если в начале разработки поставить класс не там -- то, при правильном понимании CSS, рано или поздно он всё равно переместится куда надо, поскольку стилизация открытого/закрытого меню касается также и заголовка.
|
||||
|
||||
Но оптимальнее -- сразу ставить его на правильное место.
|
||||
|
||||
## Префиксы у классов
|
||||
|
||||
Посмотрите, пожалуйста, вёрстку для виджета диалогового окна.
|
||||
|
||||
```html
|
||||
<div class="dialog">
|
||||
<h2 class="title">Заголовок</h2>
|
||||
<div class="content">
|
||||
Содержимое. Имена классов в этой вёрстке опасны.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.dialog .title { стиль заголовка }
|
||||
.dialog .content { стиль содержимого окна }
|
||||
</style>
|
||||
```
|
||||
|
||||
В этой вёрстке есть серьёзная проблема, которая появится, если в содержимом окна будет элемент с классом `.title`:
|
||||
|
||||
```html
|
||||
<div class="dialog">
|
||||
<h1 class="*!*title*/!*">Заголовок</h1>
|
||||
<div class="content">
|
||||
|
||||
*!*
|
||||
<h2 class="*!*title*/!*">Привет!</h2>
|
||||
... текст диалога ...
|
||||
*/!*
|
||||
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Такое вполне возможно, ведь диалоговое окно может иметь любое содержимое.
|
||||
|
||||
**В этом случае CSS-правило `.dialog .title` будет применено и к `<h2 class="title">` в содержимом с непредсказуемыми последствиями.**
|
||||
|
||||
Конечно, можно попытаться бороться с этим. Например, нейтрализовать его действие, добавив дополнительное правило `.dialog .content .title`, но это скорее "заплатка", нежели полноценное решение проблемы.
|
||||
|
||||
Ещё один вариант -- жёстко задать вложенность. А именно, использовать класс `.dialog > .title`. Это сработает в данном конкретном примере, но как быть в тех местах, где нужен более глубокий потомок?
|
||||
|
||||
**Чтобы избежать возможных проблем, все классы внутри виджета начинают с его имени.**
|
||||
|
||||
Подходящий вариант:
|
||||
|
||||
```html
|
||||
<div class="*!*dialog*/!*">
|
||||
<h1 class="*!*dialog-title*/!*">Заголовок</h1>
|
||||
<div class="*!*dialog-content*/!*">Содержимое</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.dialog-title { стиль загловка }
|
||||
.dialog-content { стиль содержимого окна }
|
||||
</style>
|
||||
```
|
||||
|
||||
В этом случае внутрь `.dialog-content` можно смело помещать другие компоненты со своими классами `..-title`, `..-content` и т.п.
|
||||
|
||||
Кроме всего прочего, обработка такого CSS будет чуть-чуть быстрее ;) Так как один класс вместо каскада.
|
||||
|
||||
[smart header="Когда префиксы не нужны?"]
|
||||
Префиксы делают названия классов длиннее, поэтому иногда не хочется их ставить.
|
||||
|
||||
Без них можно обойтись в тех случаях, когда внутри элемента заведомо не будет произвольного HTML и других компонент. То есть когда конфликты заведомо исключены.
|
||||
|
||||
С другой стороны, требования имеют свойство расти. Компоненты зачастую вставляются туда, где их не предполагалось. Ваш виджет, написанный для одной задачи или проекта, может быть потом использован совсем в другом месте, где потребуются вложенные компоненты. И тогда заранее предусмотренные префиксы сослужат хорошую службу.
|
||||
[/smart]
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>Вёрстка должна быть семантической, использовать соответствующие смыслу информации теги и классы.</li>
|
||||
<li>Класс, описывающий состояние всего компонента, нужно ставить на его корневом элементе, а не на том, который нужно "украсить" в этом состоянии. Если состояние относится не ко всему компоненту, а к его части -- то на соответствующем "по смыслу" DOM-узле.</li>
|
||||
<li>Классы внутри компонента должны начинаться с префикса -- имени компонента.
|
||||
|
||||
Это не всегда строго необходимо, но позволяет избежать проблем в случаях, когда компонент может содержать произвольный DOM, как например диалоговое окно с произвольным HTML-текстом.
|
||||
|
||||
Использование `.dialog-title` вместо `.dialog .title` гарантирует, что CSS не применится по ошибке к какому-нибудь другому `.title` внутри диалога.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue