diff --git a/core/modules/locale/src/Tests/LocaleContentTest.php b/core/modules/locale/src/Tests/LocaleContentTest.php
index 3ec3c71..5229eb7 100644
--- a/core/modules/locale/src/Tests/LocaleContentTest.php
+++ b/core/modules/locale/src/Tests/LocaleContentTest.php
@@ -177,26 +177,26 @@ public function testContentTypeDirLang() {
 
     // Check if English node does not have lang tag.
     $this->drupalGet('node/' . $nodes['en']->id());
-    $pattern = '|class="[^"]*node[^"]*"[^<>]*lang="en"|';
-    $this->assertNoPattern($pattern, 'The lang tag has not been assigned to the English node.');
+    $element = $this->xpath('//article[@class and contains(concat(" ", normalize-space(@class), " "), " node ") and @lang="en"]');
+    $this->assertTrue(empty($element), 'The lang tag has not been assigned to the English node.');
 
     // Check if English node does not have dir tag.
-    $pattern = '|class="[^"]*node[^"]*"[^<>]*dir="ltr"|';
-    $this->assertNoPattern($pattern, 'The dir tag has not been assigned to the English node.');
+    $element = $this->xpath('//article[@class and contains(concat(" ", normalize-space(@class), " "), " node ") and @dir="ltr"]');
+    $this->assertTrue(empty($element), 'The dir tag has not been assigned to the English node.');
 
     // Check if Arabic node has lang="ar" & dir="rtl" tags.
     $this->drupalGet('node/' . $nodes['ar']->id());
-    $pattern = '|class="[^"]*node[^"]*"[^<>]*lang="ar" dir="rtl"|';
-    $this->assertPattern($pattern, 'The lang and dir tags have been assigned correctly to the Arabic node.');
+    $element = $this->xpath('//article[@class and contains(concat(" ", normalize-space(@class), " "), " node ") and @lang="ar" and @dir="rtl"]');
+    $this->assertTrue(!empty($element), 'The lang and dir tags have been assigned correctly to the Arabic node.');
 
     // Check if Spanish node has lang="es" tag.
     $this->drupalGet('node/' . $nodes['es']->id());
-    $pattern = '|class="[^"]*node[^"]*"[^<>]*lang="es"|';
-    $this->assertPattern($pattern, 'The lang tag has been assigned correctly to the Spanish node.');
+    $element = $this->xpath('//article[@class and contains(concat(" ", normalize-space(@class), " "), " node ") and @lang="es"]');
+    $this->assertTrue(!empty($element), 'The lang tag has been assigned correctly to the Spanish node.');
 
     // Check if Spanish node does not have dir="ltr" tag.
-    $pattern = '|class="[^"]*node[^"]*"[^<>]*lang="es" dir="ltr"|';
-    $this->assertNoPattern($pattern, 'The dir tag has not been assigned to the Spanish node.');
+    $element = $this->xpath('//article[@class and contains(concat(" ", normalize-space(@class), " "), " node ") and @lang="es" and @dir="ltr"]');
+    $this->assertTrue(empty($element), 'The dir tag has not been assigned to the Spanish node.');
   }
 
 }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index fefd6d5..f782246 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -653,25 +653,6 @@ function template_preprocess_node(&$variables) {
 
   // Add article ARIA role.
   $variables['attributes']['role'] = 'article';
-
-  // Gather node classes.
-  $variables['attributes']['class'][] = 'node';
-  $variables['attributes']['class'][] = drupal_html_class('node--type-' . $node->bundle());
-  if ($node->isPromoted()) {
-    $variables['attributes']['class'][] = 'node--promoted';
-  }
-  if ($node->isSticky()) {
-    $variables['attributes']['class'][] = 'node--sticky';
-  }
-  if (!$node->isPublished()) {
-    $variables['attributes']['class'][] = 'node--unpublished';
-  }
-  if ($variables['view_mode']) {
-    $variables['attributes']['class'][] = drupal_html_class('node--view-mode-' . $variables['view_mode']);
-  }
-  if (isset($variables['preview'])) {
-    $variables['attributes']['class'][] = 'node--preview';
-  }
 }
 
 /**
diff --git a/core/modules/node/templates/node.html.twig b/core/modules/node/templates/node.html.twig
index c80eecb..1d4b16b 100644
--- a/core/modules/node/templates/node.html.twig
+++ b/core/modules/node/templates/node.html.twig
@@ -77,34 +77,45 @@
  * @ingroup themeable
  */
 #}
-<article{{ attributes }}>
+{%
+  set classes = [
+    'node',
+    'node--type-' ~ node.bundle|clean_class,
+    node.promoted ? 'node--promoted',
+    node.sticky ? 'node--sticky',
+    not node.published ? 'node--unpublished',
+    'node--view-mode-' ~ view_mode|clean_class,
+    preview ? 'node--preview',
+  ]
+%}
+<article{{ attributes.addClass(classes) }}>
 
-  {{ title_prefix }}
-  {% if not page %}
-    <h2{{ title_attributes }}>
-      <a href="{{ url }}" rel="bookmark">{{ label }}</a>
-    </h2>
-  {% endif %}
-  {{ title_suffix }}
+    {{ title_prefix }}
+    {% if not page %}
+        <h2{{ title_attributes }}>
+            <a href="{{ url }}" rel="bookmark">{{ label }}</a>
+        </h2>
+    {% endif %}
+    {{ title_suffix }}
 
-  {% if display_submitted %}
-    <footer class="node__meta">
-      {{ author_picture }}
-      <div class="node__submitted {{ author_attributes.class }}"{{ author_attributes|without('class') }}>
-        {% trans %}Submitted by {{ author_name|passthrough }} on {{ date }}{% endtrans %}
-        {{ metadata }}
-      </div>
-    </footer>
-  {% endif %}
+    {% if display_submitted %}
+        <footer class="node__meta">
+            {{ author_picture }}
+            <div{{ author_attributes.addClass('node__submitted') }}>
+                {% trans %}Submitted by {{ author_name|passthrough }} on {{ date }}{% endtrans %}
+                {{ metadata }}
+            </div>
+        </footer>
+    {% endif %}
 
-  <div class="node__content {{ content_attributes.class }}"{{ content_attributes|without('class') }}>
-    {{ content|without('links') }}
-  </div>
-
-  {% if content.links %}
-    <div class="node__links">
-      {{ content.links }}
+    <div{{ content_attributes.addClass('node__content') }}>
+        {{ content|without('links') }}
     </div>
-  {% endif %}
+
+    {% if content.links %}
+        <div class="node__links">
+            {{ content.links }}
+        </div>
+    {% endif %}
 
 </article>
diff --git a/core/themes/bartik/templates/node.html.twig b/core/themes/bartik/templates/node.html.twig
index d45b0f0..588733e 100644
--- a/core/themes/bartik/templates/node.html.twig
+++ b/core/themes/bartik/templates/node.html.twig
@@ -71,38 +71,50 @@
  * @see template_preprocess_node()
  */
 #}
-<article class="{{ attributes.class }} clearfix"{{ attributes|without('class') }}>
+{%
+  set classes = [
+    'node',
+    'node--type-' ~ node.bundle|clean_class,
+    node.promoted ? 'node--promoted',
+    node.sticky ? 'node--sticky',
+    not node.published ? 'node--unpublished',
+    'node--view-mode-' ~ view_mode|clean_class,
+    preview ? 'node--preview',
+    'clearfix',
+  ]
+%}
+<article{{ attributes.addClass(classes) }}>
 
-  <header>
-    {{ title_prefix }}
-    {% if not page %}
-      <h2 class="node__title {{ title_attributes.class }}"{{ title_attributes|without('class') }}>
-        <a href="{{ url }}" rel="bookmark">{{ label }}</a>
-      </h2>
-    {% endif %}
-    {{ title_suffix }}
+    <header>
+        {{ title_prefix }}
+        {% if not page %}
+            <h2{{ title_attributes.addClass('node__title') }}>
+                <a href="{{ url }}" rel="bookmark">{{ label }}</a>
+            </h2>
+        {% endif %}
+        {{ title_suffix }}
 
-    {% if display_submitted %}
-      <div class="node__meta">
-        {{ author_picture }}
-        <span{{ author_attributes }}>
+        {% if display_submitted %}
+            <div class="node__meta">
+                {{ author_picture }}
+                <span{{ author_attributes }}>
           {% trans %}Submitted by {{ author_name|passthrough }} on {{ date }}{% endtrans %}
         </span>
-        {{ metadata }}
-      </div>
-    {% endif %}
-  </header>
-
-  <div class="node__content clearfix {{ content_attributes.class }}"{{ content_attributes|without('class') }}>
-    {{ content|without('comment', 'links') }}
-  </div>
+                {{ metadata }}
+            </div>
+        {% endif %}
+    </header>
 
-  {% if content.links %}
-    <div class="node__links">
-      {{ content.links }}
+    <div{{ content_attributes.addClass('node__content clearfix') }}>
+        {{ content|without('comment', 'links') }}
     </div>
-  {% endif %}
 
-  {{ content.comment }}
+    {% if content.links %}
+        <div class="node__links">
+            {{ content.links }}
+        </div>
+    {% endif %}
+
+    {{ content.comment }}
 
 </article>
