diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/result.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/result.phtml
index b63acf736f99f4102c9bdd89568959cd1e36ba7f..3238f68401f178530ea3dc684d518bca619ab847 100644
--- a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/result.phtml
+++ b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/result.phtml
@@ -21,7 +21,7 @@
         <?php endif; ?>
     </div>
 <?php else: ?>
-    <div class="message error">
+    <div role="alert" class="message error">
         <div>
             <?php /* @escapeNotVerified */ echo __('We can\'t find any items matching these search criteria.');?> <a href="<?php /* @escapeNotVerified */ echo $block->getFormUrl(); ?>"><?php /* @escapeNotVerified */ echo __('Modify your search.'); ?></a>
         </div>
diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js
index 60691ab0e7b5a73bc4d3e58746be70f5ae5b070e..ddb62ceb37b37ef6bc7735c78f81367c5c6d4343 100644
--- a/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js
+++ b/app/code/Magento/Checkout/view/frontend/web/js/view/shipping.js
@@ -260,11 +260,13 @@ define(
                         this.source.trigger('shippingAddress.custom_attributes.data.validate');
                     }
 
-                    if (this.source.get('params.invalid') ||
+                    if (emailValidationResult &&
+                        this.source.get('params.invalid') ||
                         !quote.shippingMethod().method_code ||
-                        !quote.shippingMethod().carrier_code ||
-                        !emailValidationResult
+                        !quote.shippingMethod().carrier_code
                     ) {
+                        this.focusInvalid();
+
                         return false;
                     }
 
diff --git a/app/code/Magento/Checkout/view/frontend/web/template/shipping.html b/app/code/Magento/Checkout/view/frontend/web/template/shipping.html
index 9078731a8558f6fd2e1fde6a9201fbffc0d1fce0..5049aac2bbc9b056be66344e8b21542a2323dd68 100644
--- a/app/code/Magento/Checkout/view/frontend/web/template/shipping.html
+++ b/app/code/Magento/Checkout/view/frontend/web/template/shipping.html
@@ -120,7 +120,7 @@
                         <!-- ko if:  method.error_message -->
                         <tr class="row row-error">
                             <td class="col col-error" colspan="4">
-                                <div class="message error">
+                                <div role="alert" class="message error">
                                     <div data-bind="text: method.error_message"></div>
                                 </div>
                                 <span class="no-display">
@@ -141,7 +141,7 @@
                     <!-- /ko -->
                 </div>
                 <!-- ko if: errorValidationMessage().length > 0 -->
-                <div class="message notice">
+                <div role="alert" class="message notice">
                     <span><!-- ko text: errorValidationMessage()--><!-- /ko --></span>
                 </div>
                 <!-- /ko -->
diff --git a/app/code/Magento/Cookie/view/frontend/templates/html/notices.phtml b/app/code/Magento/Cookie/view/frontend/templates/html/notices.phtml
index cbf1d3ad2b231633b142127bdcf90df83d9f7bcf..f7aaf538ae96c210ae4aa7fdff0c84412207f948 100644
--- a/app/code/Magento/Cookie/view/frontend/templates/html/notices.phtml
+++ b/app/code/Magento/Cookie/view/frontend/templates/html/notices.phtml
@@ -9,12 +9,17 @@
 /** @var \Magento\Cookie\Block\Html\Notices $block */
 ?>
 <?php if ($this->helper(\Magento\Cookie\Helper\Cookie::class)->isUserNotAllowSaveCookie()): ?>
-    <div class="message global cookie" id="notice-cookie-block" style="display: none">
-        <div class="content">
+    <div role="alertdialog"
+         tabindex="-1"
+         class="message global cookie"
+         id="notice-cookie-block"
+         style="display: none;">
+        <div role="document" class="content" tabindex="0">
             <p>
                 <strong><?php /* @escapeNotVerified */ echo __('We use cookies to make your experience better.') ?></strong>
                 <span><?php /* @escapeNotVerified */ echo __('To comply with the new e-Privacy directive, we need to ask for your consent to set the cookies.') ?></span>
-                <?php /* @escapeNotVerified */ echo __('<a href="%1">Learn more</a>.', $block->getPrivacyPolicyLink()) ?></p>
+                <?php /* @escapeNotVerified */ echo __('<a href="%1">Learn more</a>.', $block->getPrivacyPolicyLink()) ?>
+            </p>
             <div class="actions">
                 <button id="btn-cookie-allow" class="action allow primary">
                     <span><?php /* @escapeNotVerified */ echo __('Allow Cookies');?></span>
diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/busy.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/busy.phtml
index cd77de6c78d861c318077a6a258f81fdada7ea63..5e6e0b8e9abf4e44aed7a92a58d603b5bde0d3f6 100644
--- a/app/code/Magento/ImportExport/view/adminhtml/templates/busy.phtml
+++ b/app/code/Magento/ImportExport/view/adminhtml/templates/busy.phtml
@@ -10,7 +10,7 @@
     </div><br>
     <div class="messages">
         <div class="message message-success success">
-            <div><?php /* @escapeNotVerified */ echo $block->getStatusMessage(); ?>hh</div>
+            <div><?php /* @escapeNotVerified */ echo $block->getStatusMessage(); ?></div>
         </div>
     </div>
 </div>
diff --git a/app/code/Magento/Theme/view/frontend/templates/messages.phtml b/app/code/Magento/Theme/view/frontend/templates/messages.phtml
index 2bd2357a27e1adbf79a9dbf7c34cc9c6d2d0da29..7ef534ab5d65c5b1df7014509e4ab7b3b41c3884 100644
--- a/app/code/Magento/Theme/view/frontend/templates/messages.phtml
+++ b/app/code/Magento/Theme/view/frontend/templates/messages.phtml
@@ -5,7 +5,8 @@
  */
 ?>
 <div data-bind="scope: 'messages'">
-    <div data-bind="foreach: { data: cookieMessages, as: 'message' }" class="messages">
+    <!-- ko if: cookieMessages && cookieMessages.length > 0 -->
+    <div role="alert" data-bind="foreach: { data: cookieMessages, as: 'message' }" class="messages">
         <div data-bind="attr: {
             class: 'message-' + message.type + ' ' + message.type + ' message',
             'data-ui-id': 'message-' + message.type
@@ -13,7 +14,9 @@
             <div data-bind="html: message.text"></div>
         </div>
     </div>
-    <div data-bind="foreach: { data: messages().messages, as: 'message' }" class="messages">
+    <!-- /ko -->
+    <!-- ko if: messages().messages && messages().messages.length > 0 -->
+    <div role="alert" data-bind="foreach: { data: messages().messages, as: 'message' }" class="messages">
         <div data-bind="attr: {
             class: 'message-' + message.type + ' ' + message.type + ' message',
             'data-ui-id': 'message-' + message.type
@@ -21,6 +24,7 @@
             <div data-bind="html: message.text"></div>
         </div>
     </div>
+    <!-- /ko -->
 </div>
 <script type="text/x-magento-init">
     {
diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js
index 4596569a7b6ac4a98bd727e8a7c1ad273a8889cc..151219b87abc5d540a35cc4e0198688bb6f6aca8 100755
--- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js
@@ -24,7 +24,7 @@ define([
             tooltipTpl: 'ui/form/element/helper/tooltip',
             fallbackResetTpl: 'ui/form/element/helper/fallback-reset',
             'input_type': 'input',
-            placeholder: '',
+            placeholder: false,
             description: '',
             labelVisible: true,
             label: '',
@@ -74,6 +74,15 @@ define([
             return this;
         },
 
+        /**
+         * Checks if component has error.
+         *
+         * @returns {Object}
+         */
+        checkInvalid: function () {
+            return this.error() && this.error().length ? this : null;
+        },
+
         /**
          * Initializes observable properties of instance
          *
@@ -84,7 +93,7 @@ define([
 
             this._super();
 
-            this.observe('error disabled focused preview visible value warn isDifferedFromDefault')
+            this.observe('error disabled focused preview visible value warn notice isDifferedFromDefault')
                 .observe('isUseDefault')
                 .observe({
                     'required': !!rules['required-entry']
@@ -114,6 +123,7 @@ define([
             _.extend(this, {
                 uid: uid,
                 noticeId: 'notice-' + uid,
+                errorId: 'error-' + uid,
                 inputName: utils.serializeName(name.join('.')),
                 valueUpdate: valueUpdate
             });
@@ -440,6 +450,23 @@ define([
          */
         userChanges: function () {
             this.valueChangedByUser = true;
+        },
+
+        /**
+         * Returns correct id for 'aria-describedby' accessibility attribute
+         *
+         * @returns {Boolean|String}
+         */
+        getDescriptionId: function () {
+            var id = false;
+
+            if (this.error()) {
+                id = this.errorId;
+            } else if (this.notice()) {
+                id = this.noticeId;
+            }
+
+            return id;
         }
     });
 });
diff --git a/app/code/Magento/Ui/view/base/web/js/form/form.js b/app/code/Magento/Ui/view/base/web/js/form/form.js
index 672af8a7ed845a12efe19f0801325e23c06932a6..0a3d6ee5850c425b861cb084761cde1cf49df2fb 100644
--- a/app/code/Magento/Ui/view/base/web/js/form/form.js
+++ b/app/code/Magento/Ui/view/base/web/js/form/form.js
@@ -248,19 +248,31 @@ define([
          * @param {Object} data
          */
         save: function (redirect, data) {
-            var scrollTop;
-
             this.validate();
 
             if (!this.additionalInvalid && !this.source.get('params.invalid')) {
                 this.setAdditionalData(data)
                     .submit(redirect);
             } else {
-                scrollTop = $(this.errorClass).offset().top - window.innerHeight / 2;
-                window.scrollTo(0, scrollTop);
+                this.focusInvalid();
             }
         },
 
+        /**
+         * Tries to set focus on first invalid form field.
+         *
+         * @returns {Object}
+         */
+        focusInvalid: function () {
+            var invalidField = _.find(this.delegate('checkInvalid'));
+
+            if (!_.isUndefined(invalidField) && _.isFunction(invalidField.focused)) {
+                invalidField.focused(true);
+            }
+
+            return this;
+        },
+
         /**
          * Set additional data to source before form submit and after validation.
          *
diff --git a/app/code/Magento/Ui/view/base/web/js/grid/editing/editor.js b/app/code/Magento/Ui/view/base/web/js/grid/editing/editor.js
index b39fb1a371801f7f4a06c786341943ee0d245c41..0fc16010af36321c382d906a3312207d53d5e135 100644
--- a/app/code/Magento/Ui/view/base/web/js/grid/editing/editor.js
+++ b/app/code/Magento/Ui/view/base/web/js/grid/editing/editor.js
@@ -486,7 +486,7 @@ define([
         },
 
         /**
-         * Counts number of invalid fields accros all active records.
+         * Counts number of invalid fields across all active records.
          *
          * @returns {Number}
          */
@@ -502,6 +502,16 @@ define([
             return errorsCount;
         },
 
+        /**
+         * Translatable error message text.
+         *
+         * @returns {String}
+         */
+        countErrorsMessage: function () {
+            return $t('There are {placeholder} messages requires your attention.')
+                .replace('{placeholder}', this.countErrors());
+        },
+
         /**
          * Checks if editor has any errors.
          *
diff --git a/app/code/Magento/Ui/view/base/web/templates/form/field.html b/app/code/Magento/Ui/view/base/web/templates/form/field.html
index adc9814a91d0ee3cbe5a1c955b532d385536fd8f..4a9d8f8c75a77d266297dfec620d548a7a12ee76 100644
--- a/app/code/Magento/Ui/view/base/web/templates/form/field.html
+++ b/app/code/Magento/Ui/view/base/web/templates/form/field.html
@@ -40,4 +40,4 @@
 
         <render args="$data.service.template" if="$data.hasService()"/>
     </div>
-</div>
\ No newline at end of file
+</div>
diff --git a/app/code/Magento/Ui/view/base/web/templates/grid/editing/header-buttons.html b/app/code/Magento/Ui/view/base/web/templates/grid/editing/header-buttons.html
index ab0131ed667cfefb4074e47c5aa69727e039d16f..fd4e61108aae0ef8e34e6a713207cb94a4560049 100644
--- a/app/code/Magento/Ui/view/base/web/templates/grid/editing/header-buttons.html
+++ b/app/code/Magento/Ui/view/base/web/templates/grid/editing/header-buttons.html
@@ -4,19 +4,21 @@
  * See COPYING.txt for license details.
  */
 -->
-<div class="data-grid-info-panel" visible="isMultiEditing || (hasActive() && (hasMessages() || hasErrors() ))">
-    <div class="messages" visible="hasMessages() || hasErrors()">
-        <div class="message message-warning" visible="hasErrors()">
-            <strong>There are <text args="countErrors()"/> messages requires your attention.</strong>
-            Please make corrections to the errors in the table below and re-submit.
+<div if="isMultiEditing || (hasActive() && (hasMessages() || hasErrors() ))"
+     attr="role: (isMultiEditing && multiEditingButtons) ? 'alertdialog' : 'alert'"
+     class="data-grid-info-panel">
+    <div if="hasMessages() || hasErrors()" class="messages">
+        <div if="hasErrors()" class="message message-warning">
+            <strong><text args="countErrorsMessage()"/></strong>
+            <span translate="'Please make corrections to the errors in the table below and re-submit.'"/>
         </div>
         <div class="message" outereach="messages" text="message"
-            css="
-                'message-warning': type === 'warning',
-                'message-error': type === 'error',
-                'message-success': type === 'success'"/>
+             css="
+                 'message-warning': type === 'warning',
+                 'message-error': type === 'error',
+                 'message-success': type === 'success'"/>
     </div>
-    <div class="data-grid-info-panel-actions" visible="isMultiEditing && multiEditingButtons">
+    <div if="isMultiEditing && multiEditingButtons" class="data-grid-info-panel-actions">
         <button class="action-tertiary" type="button" click="cancel">
             <span translate="'Cancel'"/>
         </button>
diff --git a/app/code/Magento/Ui/view/frontend/web/template/messages.html b/app/code/Magento/Ui/view/frontend/web/template/messages.html
index 5966523ec89206d829f2da18784ea756d83e9492..f0f03ec2e53257d2965a3acbed96d95faa1ba860 100644
--- a/app/code/Magento/Ui/view/frontend/web/template/messages.html
+++ b/app/code/Magento/Ui/view/frontend/web/template/messages.html
@@ -6,13 +6,13 @@
 -->
 <div data-role="checkout-messages" class="messages" data-bind="visible: isVisible(), click: removeAll">
     <!-- ko foreach: messageContainer.getErrorMessages() -->
-    <div class="message message-error error">
+    <div role="alert" class="message message-error error">
         <div data-ui-id="checkout-cart-validationmessages-message-error" data-bind="text: $data"></div>
     </div>
     <!--/ko-->
     <!-- ko foreach: messageContainer.getSuccessMessages() -->
-    <div class="message message-success success">
+    <div role="alert" class="message message-success success">
         <div data-ui-id="checkout-cart-validationmessages-message-success" data-bind="text: $data"></div>
     </div>
     <!--/ko-->
-</div>
\ No newline at end of file
+</div>
diff --git a/app/code/Magento/Ui/view/frontend/web/templates/form/element/checkbox.html b/app/code/Magento/Ui/view/frontend/web/templates/form/element/checkbox.html
index 50a27e6902528938e66ab2e632aa98ef23993701..c22df869a0013a9dd8960cada1ab7fb1b2601587 100644
--- a/app/code/Magento/Ui/view/frontend/web/templates/form/element/checkbox.html
+++ b/app/code/Magento/Ui/view/frontend/web/templates/form/element/checkbox.html
@@ -5,7 +5,19 @@
  */
 -->
 <div class="choice field">
-    <input type="checkbox" class="checkbox" data-bind="checked: value, attr: { id: uid, disabled: disabled, name: inputName }, hasFocus: focused">
+    <input type="checkbox"
+           class="checkbox"
+           data-bind="
+           checked: value,
+           attr: {
+                id: uid,
+                disabled: disabled,
+                name: inputName,
+                'aria-describedby': getDescriptionId(),
+                'aria-required': required,
+                'aria-invalid': error() ? true : 'false'
+                },
+           hasFocus: focused">
 
     <label class="label" data-bind="checked: value, attr: { for: uid }">
         <span data-bind="text: description || label"></span>
diff --git a/app/code/Magento/Ui/view/frontend/web/templates/form/element/date.html b/app/code/Magento/Ui/view/frontend/web/templates/form/element/date.html
index be2034a6a0b9710b0bc044a2334bf0744c5c1781..48faf4b73a5f5e4a4987bbc9c4b33595e74bed3c 100644
--- a/app/code/Magento/Ui/view/frontend/web/templates/form/element/date.html
+++ b/app/code/Magento/Ui/view/frontend/web/templates/form/element/date.html
@@ -11,6 +11,8 @@
         value: value,
         name: inputName,
         placeholder: placeholder,
-        'aria-describedby': noticeId,
+        'aria-describedby': getDescriptionId(),
+        'aria-required': required,
+        'aria-invalid': error() ? true : 'false',
         disabled: disabled
     }" />
diff --git a/app/code/Magento/Ui/view/frontend/web/templates/form/element/email.html b/app/code/Magento/Ui/view/frontend/web/templates/form/element/email.html
index dfd75630e08bc086d7cf87dedf55000cb9ffc10c..57f94670323ad25c2cd5b8038eee5a7d4f984c3f 100644
--- a/app/code/Magento/Ui/view/frontend/web/templates/form/element/email.html
+++ b/app/code/Magento/Ui/view/frontend/web/templates/form/element/email.html
@@ -10,7 +10,9 @@
     attr: {
         name: inputName,
         placeholder: placeholder,
-        'aria-describedby': noticeId,
+        'aria-describedby': getDescriptionId(),
+        'aria-required': required,
+        'aria-invalid': error() ? true : 'false',
         id: uid,
         disabled: disabled
     }"/>
diff --git a/app/code/Magento/Ui/view/frontend/web/templates/form/element/input.html b/app/code/Magento/Ui/view/frontend/web/templates/form/element/input.html
index 01234333d5816573d1c2ae291446229e8a317e9b..a09e0383ef81d65103bbc3c53182c6740b7d3587 100644
--- a/app/code/Magento/Ui/view/frontend/web/templates/form/element/input.html
+++ b/app/code/Magento/Ui/view/frontend/web/templates/form/element/input.html
@@ -11,7 +11,9 @@
     attr: {
         name: inputName,
         placeholder: placeholder,
-        'aria-describedby': noticeId,
+        'aria-describedby': getDescriptionId(),
+        'aria-required': required,
+        'aria-invalid': error() ? true : 'false',
         id: uid,
         disabled: disabled
     }" />
diff --git a/app/code/Magento/Ui/view/frontend/web/templates/form/element/password.html b/app/code/Magento/Ui/view/frontend/web/templates/form/element/password.html
index 825a9869ec2efbf9be3ad4492b972cb30946ec9f..0d93d48aa96f6d7d10a7b02160aded2a489cf442 100644
--- a/app/code/Magento/Ui/view/frontend/web/templates/form/element/password.html
+++ b/app/code/Magento/Ui/view/frontend/web/templates/form/element/password.html
@@ -10,7 +10,9 @@
     attr: {
         name: inputName,
         placeholder: placeholder,
-        'aria-describedby': noticeId,
+        'aria-describedby': getDescriptionId(),
+        'aria-required': required,
+        'aria-invalid': error() ? true : 'false',
         id: uid,
         disabled: disabled
     }"/>
diff --git a/app/code/Magento/Ui/view/frontend/web/templates/form/element/select.html b/app/code/Magento/Ui/view/frontend/web/templates/form/element/select.html
index edd1395c5a719a0050b93afdcc16628b3df40f97..37d87d183e023574f4a1485649fd5046aa43f8fd 100644
--- a/app/code/Magento/Ui/view/frontend/web/templates/form/element/select.html
+++ b/app/code/Magento/Ui/view/frontend/web/templates/form/element/select.html
@@ -9,7 +9,9 @@
         name: inputName,
         id: uid,
         disabled: disabled,
-        'aria-describedby': noticeId,
+        'aria-describedby': getDescriptionId(),
+        'aria-required': required,
+        'aria-invalid': error() ? true : 'false',
         placeholder: placeholder
     },
     hasFocus: focused,
diff --git a/app/code/Magento/Ui/view/frontend/web/templates/form/field.html b/app/code/Magento/Ui/view/frontend/web/templates/form/field.html
index f3be62f116b8358e9424934951dc9c40a4a2d22a..d0bc45c74dbb41173454e0fff3f315b06841505c 100644
--- a/app/code/Magento/Ui/view/frontend/web/templates/form/field.html
+++ b/app/code/Magento/Ui/view/frontend/web/templates/form/field.html
@@ -36,16 +36,22 @@
         <!-- /ko -->
 
         <!-- ko if: element.notice -->
-            <div class="field-note" data-bind="attr: { id: element.noticeId }"><span data-bind="text: element.notice"></span></div>
+            <div class="field-note" data-bind="attr: { id: element.noticeId }">
+                <span data-bind="text: element.notice"/>
+            </div>
         <!-- /ko -->
 
         <!-- ko if: element.error() -->
-            <div class="mage-error" data-bind="attr: { for: element.uid }, text: element.error" generated="true"></div>
+            <div class="field-error" data-bind="attr: { id: element.errorId }" generated="true">
+                <span data-bind="text: element.error"/>
+            </div>
         <!-- /ko -->
 
         <!-- ko if: element.warn() -->
-            <div class="message warning" generated="true"><span data-bind="text: element.warn"></span></div>
+            <div role="alert" class="message warning" data-bind="attr: { id: element.warningId }" generated="true">
+                <span data-bind="text: element.warn"/>
+            </div>
         <!-- /ko -->
     </div>
 </div>
-<!-- /ko -->
\ No newline at end of file
+<!-- /ko -->
diff --git a/app/design/frontend/Magento/blank/web/css/source/_forms.less b/app/design/frontend/Magento/blank/web/css/source/_forms.less
index 72e014b8305aaf86e0405df59654c8faf1297834..8c7258c390ab723e4f55492f42cffccfc8f60a09 100644
--- a/app/design/frontend/Magento/blank/web/css/source/_forms.less
+++ b/app/design/frontend/Magento/blank/web/css/source/_forms.less
@@ -92,10 +92,15 @@
         }
     }
 
+    .field-error,
     div.mage-error[generated] {
         margin-top: 7px;
     }
 
+    .field-error {
+        .lib-form-validation-note();
+    }
+
     .field .tooltip {
         .lib-tooltip(right);
         .tooltip-content {
diff --git a/app/design/frontend/Magento/luma/web/css/source/_forms.less b/app/design/frontend/Magento/luma/web/css/source/_forms.less
index 8d22eba9ca51f892425a60b73a43cedc6a149fa1..d29e53d7fefe68622af55b4004c3742df2be93d5 100644
--- a/app/design/frontend/Magento/luma/web/css/source/_forms.less
+++ b/app/design/frontend/Magento/luma/web/css/source/_forms.less
@@ -113,10 +113,15 @@
         .select-styling();
     }
 
+    .field-error,
     div.mage-error[generated] {
         margin-top: 7px;
     }
 
+    .field-error {
+        .lib-form-validation-note();
+    }
+
     //  TEMP
 
     .field .tooltip {