diff --git a/src/content/ru/fundamentals/web-components/customelements.md b/src/content/ru/fundamentals/web-components/customelements.md
new file mode 100644
index 00000000000..6b418198375
--- /dev/null
+++ b/src/content/ru/fundamentals/web-components/customelements.md
@@ -0,0 +1,1127 @@
+project_path: "/web/fundamentals/_project.yaml"
+book_path: "/web/fundamentals/_book.yaml"
+description: Пользовательские элементы позволяют разработчикам определять новые HTML
+ тэги, расширять существующие, а также создавать повторно используемые веб-компоненты.
+
+{# wf_updated_on: 2018-09-20 #}
+{# wf_published_on: 2016-06-28 #}
+{# wf_blink_components: Blink>DOM #}
+
+# Custom Elements v1: веб-компоненты для повторного использования {: .page-title }
+
+{% include "web/_shared/contributors/ericbidelman.html" %}
+
+### Краткое изложение {: #tldr .hide-from-toc }
+
+При помощи [Custom
+Elements](https://html.spec.whatwg.org/multipage/scripting.html#custom-elements)
+(* пользовательские элементы. Здесь и далее примеч. пер.) веб-разработчики могут
+** создавать новые теги HTML **, совершенствовать существующие или улучшать
+созданные другими разработчиками компоненты. Этот API – фундамент [ Web
+Components ](http://webcomponents.org/). За счет него у нас имеется основанный
+на веб-стандартах способ создания компонентов для повторного использования при
+помощи лишь чистого кода JS/HTML/CSS. Благодаря нему нам необходимо писать
+меньше кода и мы получаем модульный код, который можем повторно использовать в
+нашем приложении.
+
+## Введение
+
+Обратите внимание: В данной статье описывается новая спецификация Custom Elements . Если вы использовали
+пользовательские элементы, то, вероятно, знакомы с версией 0,
+поддержка которой реализована в Chrome 33 . Принцип работы тот же, однако в
+спецификацию версии 1 внесены важные поправки в API. Читайте далее, чтобы
+узнать, что нового появилось в этой версии или ознакомьтесь с разделом История и поддержка браузером для получения
+дополнительной информации.
+
+Браузер предоставляет нам великолепный инструмент для структурирования
+веб-приложений. Этот инструмент называется HTML. Вы, должно быть, слышали о нем!
+Это декларативный, портируемый язык с хорошей поддержкой и с ним легко работать.
+Каким бы великолепным не казался HTML, его словарный состав и расширяемость
+ограничены. В [ живом стандарте HTML ](https://html.spec.whatwg.org/multipage/)
+всегда не хватало способа автоматического объединения поведения, реализуемого
+при помощи JS, с вашей разметкой... до сих пор.
+
+За счет Пользовательских элементов осуществляется модернизация HTML, заполнение
+недостающих кусочков мозаики и объединение структуры и поведения. Если мы не
+можем решить проблему за счет имеющихся средств HTML, то можем создать для ее
+решения пользовательский элемент. ** Благодаря Пользовательским элементам
+расширяются функциональные возможности браузера и в то же время сохраняются
+преимущества использования HTML **.
+
+## Определение нового элемента {: #define}
+
+Для того чтобы определить новый элемент HTML, нам необходимо воспользоваться
+возможностями JavaScript!
+
+Свойство ` customElements ` глобального объекта window используется для
+определения нового пользовательского элемента и обучения браузера тому, как его
+отображать. Вызовите ` customElements.define() `, передав в качестве параметров
+имя тега, который хотите создать, и `класс` JavaScript, который наследуется от
+базового класса `HTMLElement`.
+
+**Пример:** - определение боковой выдвижной навигационной панели для мобильных
+устройств, ``:
+
+```
+class AppDrawer extends HTMLElement {...}
+window.customElements.define('app-drawer', AppDrawer);
+
+// Or use an anonymous class if you don't want a named constructor in current scope.
+window.customElements.define('app-drawer', class extends HTMLElement {...});
+```
+
+Пример использования:
+
+```
+
+```
+
+Важно помнить, что использование пользовательского элемента ничем не отличается
+от использования `
` или любого другого элемента. Его образцы могут быть
+объявлены на странице, созданы динамически при помощи кода JavaScript, могут
+быть добавлены обработчики событий и т.д. Читайте далее для ознакомления с
+большим количеством примеров.
+
+### Определение JavaScript API элемента {: #jsapi}
+
+Функциональные возможности пользовательского элемента определяются при помощи
+[`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes)
+ES2015, который расширяет `HTMLElement`. За счет наследования от `HTMLElement`
+гарантируется, что пользовательский элемент перенимает весь DOM API, и
+обеспечивается то, что любые добавленные к классу свойства/методы становятся
+частью интерфейса DOM элемента. По сути, используйте класс для создания
+**публичного JavaScript API**.
+
+**Пример**: определение интерфейса DOM ``:
+
+```
+class AppDrawer extends HTMLElement {
+
+ // A getter/setter for an open property.
+ get open() {
+ return this.hasAttribute('open');
+ }
+
+ set open(val) {
+ // Reflect the value of the open property as an HTML attribute.
+ if (val) {
+ this.setAttribute('open', '');
+ } else {
+ this.removeAttribute('open');
+ }
+ this.toggleDrawer();
+ }
+
+ // A getter/setter for a disabled property.
+ get disabled() {
+ return this.hasAttribute('disabled');
+ }
+
+ set disabled(val) {
+ // Reflect the value of the disabled property as an HTML attribute.
+ if (val) {
+ this.setAttribute('disabled', '');
+ } else {
+ this.removeAttribute('disabled');
+ }
+ }
+
+ // Can define constructor arguments if you wish.
+ constructor() {
+ // If you define a constructor, always call super() first!
+ // This is specific to CE and required by the spec.
+ super();
+
+ // Setup a click listener on itself.
+ this.addEventListener('click', e => {
+ // Don't toggle the drawer if it's disabled.
+ if (this.disabled) {
+ return;
+ }
+ this.toggleDrawer();
+ });
+ }
+
+ toggleDrawer() {
+ ...
+ }
+}
+
+customElements.define('app-drawer', AppDrawer);
+```
+
+В этом примере мы создаем выдвижную панель со свойствами `open`, `disabled` и
+методом `toggleDrawer()`. Он также [отражает свойства как атрибуты
+HTML](#reflectattr).
+
+Удобная особенность пользовательских элементов - то, что **`this` внутри
+определения класса относится к самому элементу DOM** , то есть к экземпляру
+класса. В нашем примере `this` относится к `` . За счет этого
+элемент может подключить слушатель `click` к себе самому! И вы не ограничены
+слушателями событий! Весь API DOM доступен внутри кода элемента. Используйте
+`this` для доступа к свойствам элемента, обращения к его дочерним элементам (
+`this.children` ), запроса узлов ( `this.querySelectorAll('.items')` ) и т.д.
+
+**Правила создания пользовательских элементов**
+
+1. Имя пользовательского элемента **должен содержать дефис (-)**. Таким образом,
+`` , `` и `` - допустимые имена, а ``
+и `` - нет. Благодаря этому парсер HTML может отличить пользовательские
+элементы от стандартных. Это также обеспечивает прямую совместимость при
+добавлении новых тегов в HTML.
+2. Вы не можете зарегистрировать один и тот же тэг более одного раза. При
+попытке это выполнить будет выкинута ошибка `DOMException`. Как только вы
+сообщили браузеру о новом тэге, то все. Назад дороги нет.
+3. Пользовательские элементы не могут быть самозакрывающимися, поскольку
+согласно стандарту HTML только [несколько
+элементов](https://html.spec.whatwg.org/multipage/syntax.html#void-elements)
+могут быть самозакрывающимися. Всегда добавляйте закрывающийся тэг (
+ drawer
+ ).
+
+## Реакции (ответные действия) пользовательского элемента {: #reactions}
+
+Пользовательский элемент может определять специальные хуки жизненного цикла для
+запуска кода в интересные периоды его существования. Они называются **реакциями
+пользовательского элемента.**
+
+
+
+
+
Имя
+
Вызывается
+
+
+
+
+
constructor
+
Экземпляр элемента создан или обновлён.
+Полезна для инициализации состояния, установки слушаетелей событий или
+создания теневого dom (shadow dom). Смотри спецификацию
+чтобы узнать об ограничениях использования constructor.
+
+
+
connectedCallback
+
Вызывается каждый раз когда элемент добавляется в DOM. Полезна для
+запуска кода установки, такого как получение ресурсов или отрисовки. Как
+правило, вы должны попытаться отложить работу до этого времени.
+
+
+
disconnectedCallback
+
Вызывается каждый раз когда элемент удаляется из DOM. Удобна для
+запуска кода очистки.
Вызывается когда наблюдаемый атрибут был
+добавлен, удалён, обновлён или заменён. Так же вызывается для установки
+значений, когда элемент создаётся парсером, или улучшается
+(upgrade).
+Примечание: только атрибуты из списка в свойстве
+observedAttributes получат этот вызов.
+
+
+
adoptedCallback
+
Пользовательский элемент был перемещён в новый document
+(например кто-то вызвал document.adoptNode(el))
+
+
+
+
+Обратите внимание: Браузер вызывает `attributeChangedCallback()` при изменении
+значений любого атрибут, указанный в массиве `observedAttributes` (обратитесь к
+разделу, посвященное [Отслеживание изменений значений атрибутов](#attrchanges)
+). По сути, это делается для оптимизации производительности. Когда пользователи
+изменяют общие свойства, например, `style` или `class`, вы наверняка не захотите
+получить тонны вызовов этих функций.
+
+**Вызовы реакций синхронны**. Если кто-либо вызывает `el.setAttribute()` для
+вашего элемента, то браузер тут же вызывает `attributeChangedCallback()`.
+Подобным образом будет вызван `disconnectedCallback()` сразу после того, как ваш
+элемент удален из DOM (например пользователь вызвал `el.remove()`).
+
+**Пример**: добавление реакций пользовательского элемента для ``:
+
+```
+class AppDrawer extends HTMLElement {
+ constructor() {
+ super(); // always call super() first in the constructor.
+ ...
+ }
+ connectedCallback() {
+ ...
+ }
+ disconnectedCallback() {
+ ...
+ }
+ attributeChangedCallback(attrName, oldVal, newVal) {
+ ...
+ }
+}
+```
+
+Определяйте реакции, если/когда это имеет смысл. Если ваш элемент достаточно
+сложен и выполняет подключение к IndexedDB в `connectedCallback()`, то выполните
+необходимый для завершения работы с элементом код в disconnectedCallback(). Но
+будьте бдительны! Вы не можете полагаться во всех ситуациях исключительно на
+код, выполняемый при удалении элемента из DOM. Например,
+`disconnectedCallback()` никогда не будет вызван при закрытии пользователем
+вкладки.
+
+## Свойства и атрибуты
+
+### Преобразование значений свойств в значения атрибутов HTML {: #reflectattr}
+
+Преобразование значения свойств HTML обратно в DOM в значение атрибута HTML –
+обычное дело. Например, когда значения `hidden` или `id` изменяются в коде JS:
+
+```
+div.id = 'my-id';
+div.hidden = true;
+```
+
+значения применяются к существущему DOM в качестве атрибутов:
+
+```
+
+```
+
+Это явление называется «[преобразование значений свойств в значения
+атрибутов](https://html.spec.whatwg.org/multipage/infrastructure.html#reflecting-content-attributes-in-idl-attributes)».
+Почти все свойства в HTML способны на это. Почему? Атрибуты также полезны для
+декларативного конфигурирования элемента, и некоторые API, такие как API для
+обеспечения доступности пользовательского интерфейса или API для работы с
+селекторами CSS, в своей работе полагаются на атрибуты.
+
+Преобразование значений свойств полезно везде, где вы хотите **синхронизировать
+представление элемента в DOM с его состоянием в коде JavaScript**. Одна из
+причин, по которой вам могло бы захотеться преобразовать значение свойства – то,
+что благодаря этому определенные пользователем стилевые правила применяются при
+изменении состояния элемента в коде JavaScript.
+
+Вспомните наш ``. Разработчик, который использует этот компонент,
+может захотеть, чтобы он постепенно исчез, и/или предотвратить взаимодействие
+пользователя при блокировке доступа к элементу:
+
+```
+app-drawer[disabled] {
+ opacity: 0.5;
+ pointer-events: none;
+}
+```
+
+При изменении в коде JS значения свойства `disabled` мы хотим, чтобы этот
+атрибут был добавлен в DOM, за счет чего были применены правила определённого
+пользователем селектора. Такое поведение может быть обеспечено за счет
+преобразования значений свойств в значение атрибута c тем же именем.
+
+```
+...
+
+get disabled() {
+ return this.hasAttribute('disabled');
+}
+
+set disabled(val) {
+ // Reflect the value of `disabled` as an attribute.
+ if (val) {
+ this.setAttribute('disabled', '');
+ } else {
+ this.removeAttribute('disabled');
+ }
+ this.toggleDrawer();
+}
+```
+
+### Отслеживание изменений значений атрибутов {: #attrchanges}
+
+HTML атрибуты это удобный для пользователей способ объявления начального
+состояния элемента:
+
+```
+
+```
+
+Элементы могут отреагировать на изменения атрибутов за счет определения
+`attributeChangedCallback`. Браузер вызовет этот метод при изменении любых
+значений атрибутов, перечисленных в массиве `observedAttributes`.
+
+```
+class AppDrawer extends HTMLElement {
+ ...
+
+ static get observedAttributes() {
+ return ['disabled', 'open'];
+ }
+
+ get disabled() {
+ return this.hasAttribute('disabled');
+ }
+
+ set disabled(val) {
+ if (val) {
+ this.setAttribute('disabled', '');
+ } else {
+ this.removeAttribute('disabled');
+ }
+ }
+
+ // Only called for the disabled and open attributes due to observedAttributes
+ attributeChangedCallback(name, oldValue, newValue) {
+ // When the drawer is disabled, update keyboard/screen reader behavior.
+ if (this.disabled) {
+ this.setAttribute('tabindex', '-1');
+ this.setAttribute('aria-disabled', 'true');
+ } else {
+ this.setAttribute('tabindex', '0');
+ this.setAttribute('aria-disabled', 'false');
+ }
+ // TODO: also react to the open attribute changing.
+ }
+}
+```
+
+В этом примере мы задаем значения дополнительных атрибутов для ``
+при изменении значения атрибута `disabled`. Хотя мы этого здесь не делаем, вы
+могли бы также **использовать `attributeChangedCallback` для синхронизации
+свойства элемента в JS с его атрибутом**.
+
+## Обновление элемента {: #upgrades}
+
+### Прогрессивно улучшенный HTML
+
+Мы уже узнали, что пользовательские элементы определяются при помощи вызова
+`customElements.define()`. Однако это не означает, что вы должны определить +
+зарегистрировать пользовательский элемент сразу.
+
+**Пользовательские элементы могут быть использованы *до* регистрации их
+определения** .
+
+Прогрессивное улучшение – возможность пользовательских элементов. Другими
+словами, вы можете объявить ряд элементов `` на странице и вызвать
+`customElements.define('app-drawer', ...)` намного позже. Это так, поскольку
+браузер обрабатывает потенциальные пользовательские элементы иначе благодаря
+из-за возможности добавления тэгов с [неизвестными именами](#unknown). Процесс
+вызова `define()` и наделения существующего элемента определением класса
+называется «обновления элемента».
+
+Чтобы узнать, когда имя тэга становится определённым, вы можете использовать
+`window.customElements.whenDefined()`. Метод вернёт Promise, который выполнится
+когда элемент определится.
+
+```
+customElements.whenDefined('app-drawer').then(() => {
+ console.log('app-drawer defined');
+});
+```
+
+**Пример**: отложение выполнение кода до обновления дочерних элементов
+
+```
+
+ Twitter
+ Facebook
+ G+
+
+
+
+
+// Fetch all the children of that are not defined yet.
+let undefinedButtons = buttons.querySelectorAll(':not(:defined)');
+
+let promises = [...undefinedButtons].map(socialButton => {
+ return customElements.whenDefined(socialButton.localName);
+));
+
+// Wait for all the social-buttons to be upgraded.
+Promise.all(promises).then(() => {
+ // All social-button children are ready.
+});
+```
+
+Примечание: Я думаю о пользовательских элементах как о пребывающих в лимбе пока
+они не определены.
+[Спецификация](https://dom.spec.whatwg.org/#concept-element-custom-element-state)
+определяет состояние элемента как `undefined`, `uncustomized`, или `custom`.
+Встроенные элементы, такие как `
` всегда `defined`.
+
+## Контент, определенный в элементе {: #addingmarkup}
+
+Контентом пользовательских элементов можно управлять за счет использования API
+DOM в коде элемента. При этом нам оказываются полезными [реакции](#reactions).
+
+**Пример** : создание элемента с некоторым HTML-кодом по умолчанию:
+
+```
+customElements.define('x-foo-with-markup', class extends HTMLElement {
+ connectedCallback() {
+ this.innerHTML = "I'm an x-foo-with-markup!";
+ }
+ ...
+});
+```
+
+При объявлении этого тэга получим:
+
+```
+
+ I'm an x-foo-with-markup!
+
+```
+
+{% framebox height="100px" %}
+
+
+
+
+
+
+
+
+
+{% endframebox %}
+
+Обратите внимание: Перезаписывание дочерних элементов компонента новым контентом
+обычно не является удачной идеей, поскольку это неожиданно. Пользователи будут
+удивлены, что их разметка удалена. Более удачный вариант добавления контента,
+определенного в элементе, – использование Shadow DOM, что мы далее и рассмотрим.
+
+### Создание элемента, в котором используется Shadow DOM {: #shadowdom}
+
+Обратите внимание: я не буду рассматривать возможности [Shadow
+DOM](http://w3c.github.io/webcomponents/spec/shadow/) в этом руководстве, но
+скажу, что это мощный API для совместного использования с пользовательскими
+элементами. Сама по себе технология Shadow DOM – инструмент для создания дерева
+узлов. При использовании этой технологии совместно с пользовательскими
+элементами получается потрясающий результат.
+
+За счет Shadow DOM в элементе можно хранить, отображать фрагмент DOM, который
+существует отдельно от остальных элементов страницы, и задавать для него
+стилевое оформление. Знаете, да вы могли бы даже поместить целое приложение в
+единственный элемент:
+
+```
+
+
+```
+
+Для того чтобы использовать Shadow DOM в пользовательском элементе, вызовите
+`this.attachShadow` внутри вашего `constructor`:
+
+```
+let tmpl = document.createElement('template');
+tmpl.innerHTML = `
+
+ I'm in shadow dom!
+
+`;
+
+customElements.define('x-foo-shadowdom', class extends HTMLElement {
+ constructor() {
+ super(); // always call super() first in the constructor.
+
+ // Attach a shadow root to the element.
+ let shadowRoot = this.attachShadow({mode: 'open'});
+ shadowRoot.appendChild(tmpl.content.cloneNode(true));
+ }
+ ...
+});
+```
+
+Обратите внимание: В примере выше мы используем `template` для клонирования DOM,
+а не `shadowRoot` `innerHTML` `shadowRoot` . Благодаря этому маневру сокращается
+время, используемое для парсинга HTML, поскольку контент шаблона подвергается
+парсингу только один раз, в то время как при вызове `innerHTML` для `shadowRoot`
+парсинг HTML будет выполняться при добавлении каждого образца элемента. Мы
+поговорим подробнее о шаблонах в следующем разделе.
+
+Пример использования:
+
+```
+
+
+
+
+
+{% endframebox %}
+
+### Создание элементов из `` {: #fromtemplate}
+
+Для тех, кто не знаком, [ элемент
+``](https://html.spec.whatwg.org/multipage/scripting.html#the-template-element)
+позволяет Вам объявлять фрагменты DOM, которые анализируются, инертны при
+загрузке страницы и могут быть активированы позже во время выполнения. Это
+другой API примитив в семействе веб-компонентов. **Шаблоны являются идеальным
+заполнителем для объявления структуры пользовательского элемента.**
+
+**Пример** : регистрация элемента с контентом Shadow DOM, созданным из
+`` :
+
+```
+
+
+
I'm in Shadow DOM. My markup was stamped from a <template>.
+
+
+
+```
+
+За счет этих нескольких строк кода многое происходит. Давайте рассмотрим
+ключевые моменты:
+
+1. Мы определяем новый элемент в HTML - ``
+2. Создаем Shadow DOM элемента из ``
+3. DOM элемента локален для элемента благодаря Shadow DOM
+4. Внутренний CSS элемента ограничен самим элементом благодаря Shadow DOM
+
+{% framebox height="120px" %}
+
+
+
+
+
+
+
+
+
+
I'm in Shadow DOM. My markup was stamped from a .
+
+
+
+
+{% endframebox %}
+
+## Добавление стилевого оформления для пользовательского элемента {: #styling}
+
+Даже если бы такое оформление для элемента задано в нем самом при помощи Shadow
+DOM, пользователи могут добавить для вашего пользовательского элемента свое
+стилевое оформление. Эти стилевые правила называются «стилевые правила, заданные
+пользователем».
+
+```
+
+
+
+
+ Do
+ Re
+ Mi
+
+```
+
+Вы могли бы задать себе вопрос, как работает специфичность CSS, если для
+элемента добавлено стилевое оформление внутри Shadow DOM. С точки зрения
+специфичности стилевые правила, заданные пользователем, имеют преимущество. Они
+всегда переопределяют стилевые правила, заданные в самом элементе. Обратитесь к
+разделу «[Создание элемента, в котором используется Shadow
+DOM](#shadowdom)[».](#shadowdom)
+
+### Добавление соответствующего стиля для незарегистрированного элемента {: #prestyle}
+
+До [обновления](#upgrades) элемента вы можете выбрать его в CSS при помощи
+псевдокласса `:defined`. Это полезно при добавлении предварительного стилевого
+оформления для компонента. Например: вы можете захотеть предотвратить FOUC (*
+Flash of unstyled content – появление контента без стилевого оформления) за счет
+скрытия неопределенных компонентов и их постепенного проявления после их
+определения.
+
+**Пример** : скрываем `` drawer `` до его определения:
+
+```
+app-drawer:not(:defined) {
+ /* Pre-style, give layout, replicate app-drawer's eventual styles, etc. */
+ display: inline-block;
+ height: 100vh;
+ opacity: 0;
+ transition: opacity 0.3s ease-in-out;
+}
+```
+
+После того, как `` определен, селектор ( `app-drawer:not(:defined)`)
+для него более не подходит.
+
+## Расширение возможностей элементов {: #extend}
+
+Пользовательские элементы API полезны для создания новых элементов HTML, однако
+он также полезен для расширения возможностей других пользовательских элементов
+или даже встроенных в браузера элементов HTML.
+
+### Расширение возможностей пользовательского элемента {: #extendcustomeel}
+
+Расширение возможностей другого пользовательского элемента осуществляется за
+счет унаследования определения его класса.
+
+**Пример** : создание `` , в котором расширяются возможности
+``:
+
+```
+class FancyDrawer extends AppDrawer {
+ constructor() {
+ super(); // always call super() first in the constructor. This also calls the extended class' constructor.
+ ...
+ }
+
+ toggleDrawer() {
+ // Possibly different toggle implementation?
+ // Use ES2015 if you need to call the parent method.
+ // super.toggleDrawer()
+ }
+
+ anotherMethod() {
+ ...
+ }
+}
+
+customElements.define('fancy-app-drawer', FancyDrawer);
+```
+
+### Расширение возможностей собственных элементов HTML {: #extendhtml}
+
+Давайте предположим, что вы хотели бы создать более изящный элемент `