diff --git a/README.txt b/README.txt
index 94306671360e6f5c90c1949124cf5ed0a67a6e55..0681f785c51d19e742dea4d515774da27f6165e1 100644
--- a/README.txt
+++ b/README.txt
@@ -1,55 +1,13 @@
 Soft Length Limit
 
-This module provides a counter that indicates the max. limit of
-characters in a certain text field. The soft limit means that the user
-will be warned if the content length of the field is exceeded, but he
-will still be able to save all data in the field.
+This module provides a counter that indicates the maximum recommended
+characters in a certain text field. This means the user will be warned if the
+content length of the field is exceeded, but will still be able to pass content
+validation and be persisted in the relevant storage.
 
+This module adds the following features:
 
-It basically does two things:
-
-* Lets the user configure any instance of a text field's widget, to
-  have a specific soft limit. This is done in the usual field settings
-  for entity types. This counter will always be shown on fields with a
-  soft limit, no matter the settings mentioned below.
-
-* Adds a character countdown to form elements that already have a
-  maxlength specified.  This functionality can be customized through
-  the options specified below.
-
-
-
--- Configuration of behavior of elements with "maxlength" attribute --
-
-The following Drupal variables can be set to change the criteria for
-which elements should have the limit counter:
-
-
-
-* (bool) soft_length_limit_maxlength_counter_disabled:
-
-If set to TRUE, no fields with maxlength attribute will have a counter.
-
-
-* (bool) soft_length_limit_maxlength_counter_admin_only:
-
-If set to TRUE, the counter will only be shown when the admin_theme is
-active.
-
-
-* (Array) soft_length_limit_maxlength_counter_themes:
-
-This as an array of theme names for which the counter should be
-shown. If empty, all themes show the counter.
-
-
-* (Array) soft_limit_length_maxlength_exclude_selectors:
-
-This is an array of jQuery selectors (using classes, IDs etc.) for
-elements which should be excluded from having a counter, although they
-have a defined maxlength, i.e. date fields, autocomplete fields
-etc. could be added here.
-
-* The icons for this came from Creative Commons Attribution 3.0
-Unported License, and they came from http://icomoon.io/#icons free
-package.
+* Lets the user configure any instance of a text field's widget, to have a
+  specific soft limit. This is done in the "Manage Form Display" page of the
+  relevant entity type. This counter will always be shown on fields with a soft
+  limit.
diff --git a/config/schema/soft_length_limit.schema.yml b/config/schema/soft_length_limit.schema.yml
new file mode 100644
index 0000000000000000000000000000000000000000..73b547f5cb9235ab7bd2b2e9ec2f95ed0ed498d2
--- /dev/null
+++ b/config/schema/soft_length_limit.schema.yml
@@ -0,0 +1,10 @@
+field.widget.third_party.soft_length_limit:
+  type: mapping
+  label: 'soft_length_limit settings'
+  mapping:
+    max_limit:
+      type: integer
+    minimum_limit:
+      type: integer
+    style_select:
+      type: boolean
diff --git a/css/soft_length_limit.css b/css/soft_length_limit.css
new file mode 100644
index 0000000000000000000000000000000000000000..42c031e352bc583f37185a2c9743828b9347f65f
--- /dev/null
+++ b/css/soft_length_limit.css
@@ -0,0 +1,49 @@
+div.soft-length-limit-tooltip.description {
+  display: inline-block;
+  font-style: italic;
+  position: relative;
+  margin-top: 1em;
+  z-index: 10;
+}
+
+.form-item div.soft-length-limit-tooltip.exceeded,
+.form-item div.soft-length-limit-tooltip.under-min {
+  color: #a44;
+}
+
+/* Min/Enhanced tooltip */
+.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip,
+.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip,
+.form-item.form-type-textfield .soft-length-limit-tooltip.min-style-tooltip,
+.form-item.form-type-textarea .soft-length-limit-tooltip.min-style-tooltip {
+  background-color: transparent;
+  background-image: url(../img/ico_socpub_confirm.png);
+  background-position: left top;
+  background-repeat: no-repeat;
+  background-size: 15px 15px;
+  left: auto !important;
+  padding: 0 0 0 20px;
+}
+
+.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip,
+.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip {
+  right: 10px;
+}
+
+.form-item.form-type-textfield .soft-length-limit-tooltip.min-style-tooltip {
+  margin-left: 5px;
+}
+
+.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip.under-min,
+.form-item.form-type-textfield .soft-length-limit-tooltip.min-style-tooltip.under-min,
+.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip.under-min,
+.form-item.form-type-textarea .soft-length-limit-tooltip.min-style-tooltip.under-min {
+  background-image: url(../img/ico_socpub_alert.png);
+}
+
+.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded,
+.form-item.form-type-textfield .soft-length-limit-tooltip.min-style-tooltip.exceeded,
+.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded,
+.form-item.form-type-textarea .soft-length-limit-tooltip.min-style-tooltip.exceeded {
+  background-image: url(../img/ico_socpub_alert.png);
+}
diff --git a/ico_socpub_alert.png b/img/ico_socpub_alert.png
similarity index 100%
rename from ico_socpub_alert.png
rename to img/ico_socpub_alert.png
diff --git a/ico_socpub_confirm.png b/img/ico_socpub_confirm.png
similarity index 100%
rename from ico_socpub_confirm.png
rename to img/ico_socpub_confirm.png
diff --git a/jquery.textchange.min.js b/js/jquery.textchange.min.js
similarity index 100%
rename from jquery.textchange.min.js
rename to js/jquery.textchange.min.js
diff --git a/js/soft_length_limit.js b/js/soft_length_limit.js
new file mode 100644
index 0000000000000000000000000000000000000000..32fb55765a3b0429ca58e60d92ba5c85064f559d
--- /dev/null
+++ b/js/soft_length_limit.js
@@ -0,0 +1,141 @@
+(function ($) {
+
+  var sll = sll || {};
+
+  Drupal.behaviors.softLengthLimit = {
+    attach: function (context, settings) {
+      // Preparing the input elements by adding a tooltip container.
+      $('.soft-length-limit:not(.soft-length-limit-processed)').each(function(index) {
+        $(this).addClass('soft-length-limit-processed');
+
+        var $parent = $(this).parent();
+        $parent.css('position','relative');
+        $parent.append('<div class="soft-length-limit-tooltip description"></div>');
+        $element = $(this);
+      });
+
+      var $softLengthElement = $('.soft-length-limit');
+
+      // Calculates the correct position of the tooltip and shows it.
+      $softLengthElement.focus(function(event){
+        var $tooltip = $(this).parent().find('.soft-length-limit-tooltip');
+        $(this).trigger('textchange', $(this).val());
+
+        $tooltip.fadeIn('fast');
+      });
+
+      // Hides the tooltip.
+      $softLengthElement.blur(function(event){
+        var $tooltip = $(this).parent().find('.soft-length-limit-tooltip');
+        $tooltip.fadeOut('fast');
+      });
+
+      // Shows the relevant info to the user in the tooltip.
+      $softLengthElement.bind('textchange', sll.refreshTooltip);
+    }
+  };
+
+  sll.refreshTooltip = function(event, prevText) {
+    let limit = $(this).attr('data-soft-length-limit');
+    let minimum = $(this).attr('data-soft-length-minimum');
+    let val = $(this).val();
+    let remaining = limit - val.length;
+    let $tooltip = $(this).parent().find('.soft-length-limit-tooltip');
+    let styleSelect = $(this).attr('data-soft-length-style-select');
+
+    // Removes the "exceeded" class if length is not exceeded.
+    if (prevText.length > limit && val.length <= limit) {
+      $tooltip.removeClass('exceeded');
+      $(this).removeClass('exceeded');
+    }
+
+    // Adds the "exceeded" class if length is exceeded.
+    if (val.length > limit && !$tooltip.hasClass('exceeded')) {
+      $tooltip.addClass('exceeded');
+      $(this).addClass('exceeded');
+    }
+
+    // Adds the "exceeded" class if the length is less than the minimum, in the case where the minimum is set.
+    // Adds the "under-min" class, for under-character-minimum specific behavior. Used for min/enhanced character count.
+    if (minimum > 0) {
+      if (val.length < minimum) {
+        $tooltip.addClass('under-min');
+        $(this).addClass('under-min');
+      }
+      if (val.length >= minimum) {
+        $tooltip.removeClass('under-min');
+        $(this).removeClass('under-min');
+      }
+      if (val.length > limit) {
+        $tooltip.addClass('exceeded');
+        $(this).addClass('exceeded');
+      }
+      if (val.length <= limit) {
+        $tooltip.removeClass('exceeded');
+        $(this).removeClass('exceeded');
+      }
+    }
+
+    // The minimal / enhanced version of character limits.
+    if (styleSelect === '1') {
+      // No minimum treatment.
+      $tooltip.html(Drupal.t('@val/@limit', {
+        '@val': val.length,
+        '@limit': limit
+      }));
+      // Add class to tooltip for different CSS treatment (icons, text alignment, etc).
+      $tooltip.parent().children('.soft-length-limit-tooltip').addClass('min-style-tooltip');
+    }
+
+    // Original character limit treatment.
+    if ((styleSelect === '0') || (styleSelect === undefined)) {
+      // No minimum value is set.
+      if (minimum < 1) {
+        if (val.length === 0) {
+          $tooltip.html(Drupal.t('Content limited to @limit characters',{
+            '@limit': limit
+          }));
+        }
+        else if (remaining < 0) {
+          $tooltip.html(Drupal.t('@limit character limit exceeded by @exceed characters.',{
+            '@limit': limit,
+            '@exceed': -remaining
+          }));
+        }
+        else {
+          $tooltip.html(Drupal.t('Content limited to @limit characters. Remaining: @remaining',{
+            '@limit': limit,
+            '@remaining': remaining
+          }));
+        }
+      }
+      // There is a minimum length set.
+      else {
+        if (val.length === 0) {
+          $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val.  Content limited to @limit characters. ',{
+            '@limit': limit,
+            '@minimum': minimum,
+            '@val': val.length
+          }));
+        }
+        else if (remaining < 0) {
+          $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val.  @limit character limit exceeded by @exceed characters.',{
+            '@limit': limit,
+            '@exceed': -remaining,
+            '@minimum': minimum,
+            '@val': val.length
+          }));
+        }
+        else {
+          $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val.  Content limited to @limit characters. Remaining: @remaining.',{
+            '@limit': limit,
+            '@remaining': remaining,
+            '@minimum': minimum,
+            '@val': val.length
+          }));
+        }
+      }
+    }
+  };
+
+})(jQuery);
diff --git a/soft_length_limit.css b/soft_length_limit.css
deleted file mode 100644
index 8ba6229cd6b79d6dff54184ba42e4c3d476ef5e2..0000000000000000000000000000000000000000
--- a/soft_length_limit.css
+++ /dev/null
@@ -1,32 +0,0 @@
-.soft-length-limit-tooltip {
-  display: inline-block;
-  font-style: italic;
-  position: absolute;
-  z-index: 10;
-}
-
-.form-item div.soft-length-limit-tooltip.exceeded {
-  color: #a44;
-}
-
-/* Min/Enhanced tooltip */
-.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip,
-.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip {
-  background-color: transparent;
-  background-image: url(ico_socpub_confirm.png);
-  background-position: left top;
-  background-repeat: no-repeat;
-  background-size: 15px 15px;
-  left: auto !important;
-  padding: 0 0 0 20px;
-  right: 10px;
-}
-.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded.under-min,
-.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded.under-min {
-  background-image: none;
-}
-
-.fieldset-wrapper .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded,
-.field-type-text .form-item .soft-length-limit-tooltip.min-style-tooltip.exceeded {
-  background-image: url(ico_socpub_alert.png);
-}
diff --git a/soft_length_limit.info b/soft_length_limit.info
deleted file mode 100644
index 4a3f814d1357ddd6aabc62fefa03e6c38bb2318b..0000000000000000000000000000000000000000
--- a/soft_length_limit.info
+++ /dev/null
@@ -1,4 +0,0 @@
-name = Soft length limit
-description = Makes a soft limit on the number of chars in text fields, which warns the user of too long texts
-core = 7.x
-files[] = soft_length_limit.test
diff --git a/soft_length_limit.info.yml b/soft_length_limit.info.yml
new file mode 100644
index 0000000000000000000000000000000000000000..084c162e0519bc6ff9a9ef801abbf0925f9bc202
--- /dev/null
+++ b/soft_length_limit.info.yml
@@ -0,0 +1,5 @@
+name: Soft Length Limit
+type: module
+description: Soft limits number of chars in text fields, warning the user of too long texts
+package: Fields
+core: 8.x
diff --git a/soft_length_limit.install b/soft_length_limit.install
deleted file mode 100644
index 2bfdf339bda5e1ce9207f9c437a452c9cde0b4eb..0000000000000000000000000000000000000000
--- a/soft_length_limit.install
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-/**
- * @file
- * Install/uninstall hooks for soft_length_limits module.
- */
-
-/**
- * Implements hook_uninstall().
- *
- * Deletes variables defined by the module.
- */
-function soft_length_limit_uninstall() {
-  db_delete('variable')
-    ->condition('name', "soft_length_limit_%", "LIKE")
-    ->execute();
-  cache_clear_all('variables', 'cache_bootstrap');
-}
diff --git a/soft_length_limit.js b/soft_length_limit.js
deleted file mode 100644
index 04efe0f202291f497230bc9f90a1c8164b7814d5..0000000000000000000000000000000000000000
--- a/soft_length_limit.js
+++ /dev/null
@@ -1,159 +0,0 @@
-(function ($) {
-  Drupal.behaviors.softLengthLimit = {
-    attach: function (context, settings) {
-
-      // Preparing the input elements by adding a tooltip container.
-      $('.soft-length-limit:not(.soft-length-limit-processed)').each(function(index){
-        $(this).addClass('soft-length-limit-processed');
-
-        var $parent = $(this).parent();
-        $parent.css('position','relative');
-        $parent.append('<div class="soft-length-limit-tooltip description"></div>');
-        $element = $(this);
-
-        // Used for automatically moving the tooltip when resizing the
-        // text area.
-        $parent.find('.grippie').mousedown({
-          element: $(this)
-        }, function(event){
-          var endDrag = function(event) {
-            event.data.element.focus();
-            $(document).unbind('mouseup',endDrag);
-          };
-          $(document).mouseup(event.data,endDrag);
-        });
-      });
-
-      // Calculates the correct position of the tooltip and shows it.
-      $('.soft-length-limit').focus(function(event){
-        var $tooltip = $(this).parent().find('.soft-length-limit-tooltip');
-        var left = $(this).position().left;
-        var top = $(this).position().top;
-        var bottom = top + $(this).outerHeight(true);
-        var right = left + $(this).outerWidth(true);
-        $(this).trigger('textchange', $(this).val());
-
-        if ($(this).get(0).tagName == "TEXTAREA") {
-          $tooltip.css('left', 10).css('top', bottom + 5);
-        }
-        else {
-          $tooltip.css('left', 10).css('top', bottom - 4);
-        }
-
-        $tooltip.fadeIn('fast');
-      });
-
-      // Hides the tooltip.
-      $('.soft-length-limit').blur(function(event){
-        var $tooltip = $(this).parent().find('.soft-length-limit-tooltip');
-        $tooltip.fadeOut('fast');
-      });
-
-      // Shows the relevant info to the user in the tooltip.
-      $('.soft-length-limit').bind('textchange', function(event, prevText){
-        var limit = $(this).attr('data-soft-length-limit');
-        var minimum = $(this).attr('data-soft-length-minimum');
-        var val = $(this).val();
-        var remaining = limit - val.length;
-        var $tooltip = $(this).parent().find('.soft-length-limit-tooltip');
-        var styleSelect = $(this).attr('data-soft-length-style-select');
-
-        // Removes the "exceeded" class if length is not exceeded
-        // anymore.
-        if (prevText.length > limit && val.length <= limit) {
-          $tooltip.removeClass('exceeded');
-          $(this).removeClass('exceeded');
-        }
-
-        // Adds the "exceeded" class if length is exceeded.
-        if (val.length > limit && !$tooltip.hasClass('exceeded')) {
-          $tooltip.addClass('exceeded');
-          $(this).addClass('exceeded');
-        }
-
-        // Adds the "exceeded" class if the length is less than the minimum, in the case where the minimum is set.
-        // Adds the "under-min" class, for under-character-minimum specific behavior. Used for min/enhanced character count.
-        if (minimum > 0) {
-            if (val.length < minimum) {
-                $tooltip.addClass('under-min');
-                $(this).addClass('under-min');
-            }
-            if (val.length >= minimum) {
-                $tooltip.removeClass('under-min');
-                $(this).removeClass('under-min');
-            }
-            if (val.length > limit) {
-                $tooltip.addClass('exceeded');
-                $(this).addClass('exceeded');
-            }
-            if (val.length <= limit) {
-                $tooltip.removeClass('exceeded');
-                $(this).removeClass('exceeded');
-            }
-        }
-
-        // The minimal/enhanced version of character limits
-        if (styleSelect == '1') {
-            // No minimum treatment
-            $tooltip.html(Drupal.t('@val/@limit', {
-                '@val': val.length,
-                '@limit': limit
-            }));
-            // Add class to tooltip for different CSS treatment (icons, text alignment, etc )
-            $tooltip.parent().children('.soft-length-limit-tooltip').addClass('min-style-tooltip');
-        }
-
-        // Original character limit treatment
-        if ((styleSelect == '0') || (styleSelect == undefined)) {
-            // No minimum value is set.
-            if (minimum < 1) {
-              if (val.length === 0) {
-                $tooltip.html(Drupal.t('Content limited to @limit characters',{
-                  '@limit': limit
-                }));
-              }
-              else if (remaining < 0) {
-                $tooltip.html(Drupal.t('@limit character limit exceeded by @exceed characters.',{
-                  '@limit': limit,
-                  '@exceed': -remaining
-                }));
-              }
-              else {
-                $tooltip.html(Drupal.t('Content limited to @limit characters. Remaining: @remaining',{
-                  '@limit': limit,
-                  '@remaining': remaining
-                }));
-              }
-            }
-            // There is a minimum length set.
-            else {
-              if (val.length === 0) {
-                $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val.  Content limited to @limit characters. ',{
-                  '@limit': limit,
-                  '@minimum': minimum,
-                  '@val': val.length
-                }));
-              }
-              else if (remaining < 0) {
-                $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val.  @limit character limit exceeded by @exceed characters.',{
-                  '@limit': limit,
-                  '@exceed': -remaining,
-                  '@minimum': minimum,
-                  '@val': val.length
-                }));
-              }
-              else {
-                $tooltip.html(Drupal.t('Suggested minimum number of characters is @minimum, current count is @val.  Content limited to @limit characters. Remaining: @remaining.',{
-                  '@limit': limit,
-                  '@remaining': remaining,
-                  '@minimum': minimum,
-                  '@val': val.length
-                }));
-              }
-            }
-        }
-      });
-    }
-  };
-
-})(jQuery);
diff --git a/soft_length_limit.libraries.yml b/soft_length_limit.libraries.yml
new file mode 100644
index 0000000000000000000000000000000000000000..34bfb4d2eb2c7ba00f176fcf68f130d47d49f673
--- /dev/null
+++ b/soft_length_limit.libraries.yml
@@ -0,0 +1,10 @@
+soft_length_limit:
+  js:
+    js/soft_length_limit.js: {}
+    js/jquery.textchange.min.js: {}
+  css:
+    component:
+      css/soft_length_limit.css: {}
+  dependencies:
+  - core/jquery
+  - core/jquery.once
diff --git a/soft_length_limit.module b/soft_length_limit.module
index 3b94784ce483d66f3830205d4acffd812f198a90..8504b249024e2030e41ee38ca7735aabb87e0aea 100644
--- a/soft_length_limit.module
+++ b/soft_length_limit.module
@@ -2,257 +2,158 @@
 
 /**
  * @file
- * Soft Length Limit module
+ * Soft Length Limit module.
  */
 
-define('SOFT_LENGTH_LIMIT_TITLE_MAX', 'soft_length_limit_title_max');
-define('SOFT_LENGTH_LIMIT_TITLE_MIN', 'soft_length_limit_title_min');
-define('SOFT_LENGTH_STYLE_SELECT', 'soft_length_style_select');
+declare(strict_types = 1);
+
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\WidgetInterface;
+use Drupal\Core\Form\FormStateInterface;
 
 /**
- * Returns the field widget or form element types that should be affected.
- *
- * @param string $usage
- *   The desired usage of the data, can be one of 'fields' or 'elements'
+ * Returns the widget settings that can be used for a soft_length widget.
  *
  * @return array
- *   An array field widget or form element type names
+ *   An array of settings and default values for each textfield type.
  */
-function _soft_length_limit_types($usage) {
-  $return = array();
-  switch ($usage) {
-    case 'fields':
-      $return = array(
-        'text_textarea' => 'text_textarea',
-        'text_textfield' => 'text_textfield',
-        'text_textarea_with_summary' => 'text_textarea_with_summary',
-      );
-      break;
-
-    case 'elements':
-      $return = array(
-        'textarea' => 'textarea',
-        'textfield' => 'textfield',
-        'text_format' => 'text_format',
-      );
-      break;
-
-    case 'entity_types':
-      $return = array(
-        'node' => 'node',
-      );
-      break;
-  }
-
-  return $return;
+function _soft_length_widget_settings(string $plugin_id): array {
+  $settings = [
+    'string_textfield' => [
+      'max_limit' => TRUE,
+      'minimum_limit' => TRUE,
+      'style_select' => TRUE,
+    ],
+    'string_textarea' => [
+      'max_limit' => TRUE,
+      'minimum_limit' => TRUE,
+      'style_select' => TRUE,
+    ],
+    'text_textfield' => [
+      'max_limit' => TRUE,
+      'minimum_limit' => TRUE,
+      'style_select' => TRUE,
+    ],
+    'text_textarea' => [
+      'max_limit' => TRUE,
+      'minimum_limit' => TRUE,
+      'style_select' => TRUE,
+    ],
+    'text_textarea_with_summary' => [
+      'max_limit' => TRUE,
+      'minimum_limit' => TRUE,
+      'style_select' => TRUE,
+    ],
+  ];
+
+  return $settings[$plugin_id] ?? [];
 }
 
 /**
- * Implements hook_form_FORM_ID_alter().
- *
- * Adds soft length limit fields when editing a content type.
+ * Implements hook_field_widget_third_party_settings_form().
  */
-function soft_length_limit_form_node_type_form_alter(&$form, &$form_state, $form_id) {
-  $form['submission'][SOFT_LENGTH_LIMIT_TITLE_MAX] = array(
-    '#type' => 'textfield',
-    '#title' => t('Soft length limit'),
-    '#default_value' => variable_get(SOFT_LENGTH_LIMIT_TITLE_MAX . '_' . $form['#node_type']->type, NULL),
-    '#description' => t('If any value is given here, a counter will appear next to this field, informing the user of the chosen number of allowed characters. If the number is exceeded, a warning will be shown.'),
-    '#element_validate' => array('element_validate_integer_positive'),
-    '#weight' => -2,
-  );
-
-  $form['submission'][SOFT_LENGTH_LIMIT_TITLE_MIN] = array(
-    '#type' => 'textfield',
-    '#title' => t('Soft length minimum'),
-    '#default_value' => variable_get(SOFT_LENGTH_LIMIT_TITLE_MIN . '_' . $form['#node_type']->type, NULL),
-    '#description' => t('If any value is given here, the minimum number recommended characters will be displayed as the editor enters text in this field.'),
-    '#element_validate' => array('element_validate_integer_positive'),
-    '#weight' => -1,
-  );
-
-  $form['submission'][SOFT_LENGTH_STYLE_SELECT] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Enable enhanced view'),
-    '#default_value' => variable_get(SOFT_LENGTH_STYLE_SELECT . '_' . $form['#node_type']->type, NULL),
-    '#description' => t('Check this to enable an enhanced view of soft length states.'),
-    '#weight' => -1,
-  );
-
-  $form['submission']['title_label']['#weight'] = -3;
-
-  // Add a custom submit handler to validate that the editor didn't set the
-  // maximum length to greater than 255 characters.
-  $form['#validate'][] = 'soft_length_limit_validate_title_length';
-
-  // Add a custom submit handler to save the title maximum and minimum.
-  $form['#submit'][] = 'soft_length_limit_set_title_maxlength';
-}
-
-/**
- * Form validate handler for title length fields.
- */
-function soft_length_limit_validate_title_length($form, &$form_state) {
-  if (($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MAX] < 0) || ($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MAX] > 255)) {
-    form_set_error('title_length', t('The value for soft length limit must be a whole number greater than zero and less than 256'), NULL);
-  }
-
-  if (($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MIN] < 0) || ($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MIN] > 255)) {
-    form_set_error('soft_length_minimum', t('The value for soft length minimum must be a whole number greater than zero and less than 256'));
-  }
-
-  if ($form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MAX] < $form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MIN]) {
-    form_set_error('soft_length_minimum', t('The value for soft length minimum must be less than or equal to the soft length limit'));
+function soft_length_limit_field_widget_third_party_settings_form(WidgetInterface $plugin, FieldDefinitionInterface $field_definition, $form_mode, $form, FormStateInterface $form_state) {
+  $plugin_id = $plugin->getPluginId();
+  if (!$allowed_settings = _soft_length_widget_settings($plugin_id)) {
+    return NULL;
   }
-}
-
-/**
- * Save the values of max and minimum for title.
- * 
- * So they can be applied later as an override to #maxlength.
- */
-function soft_length_limit_set_title_maxlength($form, &$form_state) {
-  variable_set(SOFT_LENGTH_LIMIT_TITLE_MAX . '_' . $form_state['values']['type'], $form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MAX]);
-  variable_set(SOFT_LENGTH_LIMIT_TITLE_MIN . '_' . $form_state['values']['type'], $form_state['values'][SOFT_LENGTH_LIMIT_TITLE_MIN]);
-  variable_set(SOFT_LENGTH_STYLE_SELECT . '_' . $form_state['values']['type'], $form_state['values'][SOFT_LENGTH_STYLE_SELECT]);
-}
 
-/**
- * Implements hook_form_FORM_ID_alter().
- *
- * Adds soft length limit fields when a field form field is rendered.
- */
-function soft_length_limit_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
-  $types = _soft_length_limit_types('fields');
-
-  if (isset($types[$form['#instance']['widget']['type']])) {
-
-    $form['instance']['widget']['settings']['soft_length_limit'] = array(
-      '#type' => 'textfield',
+  $element = [];
+  if ($allowed_settings['max_limit']) {
+    $element['max_limit'] = [
+      '#type' => 'number',
+      '#min' => 0,
       '#title' => t('Soft length limit'),
-      '#default_value' => isset($form['#instance']['widget']['settings']['soft_length_limit']) ? $form['#instance']['widget']['settings']['soft_length_limit'] : NULL,
+      '#default_value' => $plugin->getThirdPartySetting('soft_length_limit', 'max_limit'),
       '#description' => t('If any value is given here, a counter will appear next to this field, informing the user of the chosen number of allowed characters. If the number is exceeded, a warning will be shown.'),
-      '#element_validate' => array('element_validate_integer_positive'),
       '#weight' => -3,
-    );
+    ];
+  }
 
-    $form['instance']['widget']['settings']['soft_length_minimum'] = array(
-      '#type' => 'textfield',
+  if ($allowed_settings['minimum_limit']) {
+    $element['minimum_limit'] = [
+      '#type' => 'number',
+      '#min' => 0,
       '#title' => t('Soft length minimum'),
-      '#default_value' => isset($form['#instance']['widget']['settings']['soft_length_minimum']) ? $form['#instance']['widget']['settings']['soft_length_minimum'] : NULL,
+      '#default_value' => $plugin->getThirdPartySetting('soft_length_limit', 'minimum_limit'),
       '#description' => t('If any value is given here, the minimum number recommended characters will be displayed as the editor enters text in this field.'),
-      '#element_validate' => array('element_validate_integer_positive'),
       '#weight' => -2,
-    );
+    ];
+  }
 
-    $form['instance']['widget']['settings']['soft_length_style_select'] = array(
+  if ($allowed_settings['style_select']) {
+    $element['style_select'] = [
       '#type' => 'checkbox',
       '#title' => t('Enable enhanced view'),
-      '#default_value' => isset($form['#instance']['widget']['settings']['soft_length_style_select']) ? $form['#instance']['widget']['settings']['soft_length_style_select'] : 0,
+      '#default_value' => $plugin->getThirdPartySetting('soft_length_limit', 'style_select'),
       '#description' => t('Check this to enable an enhanced view of soft length states.'),
       '#weight' => -1,
-    );
+    ];
   }
+
+  return $element;
 }
 
 /**
- * Implements hook_field_attach_form().
+ * Implements hook_field_widget_settings_summary_alter().
  */
-function soft_length_limit_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
-  $entity_types = _soft_length_limit_types('entity_types');
+function soft_length_limit_field_widget_settings_summary_alter(&$summary, $context) {
+  /* @var \Drupal\Core\Field\WidgetInterface $widget */
+  $widget = $context['widget'];
+  $plugin_id = $widget->getPluginId();
 
-  if (!isset($entity_types[$entity_type])) {
-    return;
+  if (!$allowed_settings = _soft_length_widget_settings($plugin_id)) {
+    return NULL;
   }
 
-  $fields = field_info_instances($entity_type, $form['#bundle']);
-  $elements = array();
-
-  foreach ($fields as $key => $value) {
-    if (isset($value['widget']['settings']['soft_length_limit']) && $value['widget']['settings']['soft_length_limit'] > 0) {
-      $elements[$key] = $value;
-    }
+  $max_limit = $allowed_settings['max_limit']
+    ? $widget->getThirdPartySetting('soft_length_limit', 'max_limit')
+    : FALSE;
+  $minimum_limit = $allowed_settings['minimum_limit']
+    ? $widget->getThirdPartySetting('soft_length_limit', 'minimum_limit')
+    : FALSE;
+  $style_select = $allowed_settings['minimum_limit']
+    ? $widget->getThirdPartySetting('soft_length_limit', 'style_select')
+    : FALSE;
+
+  if ($max_limit) {
+    $summary[] = t('Maximum recommended length: @count', ['@count' => $max_limit]);
   }
-
-  if (count($elements) || isset($form['title'])) {
-    soft_length_limit_set_attr($form, $elements);
-    // Adds the javascript and CSS files as form attachments, in case the init
-    // hook does not add them due to the context or settings.
-    $form['#attached']['js'][] = drupal_get_path('module', 'soft_length_limit') . '/jquery.textchange.min.js';
-    $form['#attached']['js'][] = drupal_get_path('module', 'soft_length_limit') . '/soft_length_limit.js';
-    $form['#attached']['css'][] = drupal_get_path('module', 'soft_length_limit') . '/soft_length_limit.css';
+  if ($minimum_limit) {
+    $summary[] = t('Minimum recommended length: @count', ['@count' => $minimum_limit]);
+  }
+  if ($style_select) {
+    $summary[] = t('Style select: @style', ['@style' => $style_select ? 'Enabled' : 'Disabled']);
   }
 }
 
 /**
- * Recurse through form and set variables.
- *
- * Recursive helper function that sets the correct attributes for the form
- * elements with a specific soft limit specified, and continues through child
- * elements.
- *
- * @param array $element
- *   The form element to iterate through
- *
- * @param array $sub_elements
- *   Array of the elements which should have a soft limit attribute
+ * Implements hook_field_widget_form_alter().
  */
-function soft_length_limit_set_attr(&$element, $sub_elements) {
-  $children = element_get_visible_children($element);
-  $types = _soft_length_limit_types('elements');
-
-  foreach ($children as $value) {
-    if (isset($element[$value]['#type']) && isset($types[$element[$value]['#type']])) {
-      if (isset($element[$value]['#field_name']) && isset($sub_elements[$element[$value]['#field_name']])) {
-
-        $widget_settings = isset($sub_elements[$element[$value]['#field_name']]['widget']['settings']) ? $sub_elements[$element[$value]['#field_name']]['widget']['settings'] : FALSE;
+function soft_length_limit_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
+  $third_party_settings = $context['widget']->getThirdPartySettings();
 
-        if ($widget_settings) {
-          // Soft limit length.
-          $soft_limit = $widget_settings['soft_length_limit'];
-          $element[$value]['#soft_length_limit'] = (isset($element[$value]['#maxlength']) && $soft_limit > $element[$value]['#maxlength']) ? $element[$value]['#maxlength'] : $soft_limit;
-          $element[$value]['#attributes']['data-soft-length-limit'] = $soft_limit;
-          $element[$value]['#attributes']['class'][] = 'soft-length-limit';
-
-          // Soft minimum.
-          $soft_min = isset($widget_settings['soft_length_minimum']) ? $widget_settings['soft_length_minimum'] : '';
-          $element[$value]['#attributes']['data-soft-length-minimum'] = $soft_min;
+  if (empty($third_party_settings['soft_length_limit'])) {
+    return NULL;
+  }
 
-          // Length style select.
-          if (isset($widget_settings['soft_length_style_select'])) {
-            $element[$value]['#attributes']['data-soft-length-style-select'] = $widget_settings['soft_length_style_select'];
-          }
-        }
-      }
-    }
+  $sll_config = $third_party_settings['soft_length_limit'];
 
-    soft_length_limit_set_attr($element[$value], $sub_elements);
+  if (isset($sll_config['max_limit'])) {
+    $element['value']['#attributes']['data-soft-length-limit'] = $sll_config['max_limit'];
+    $element['value']['#attributes']['class'][] = 'soft-length-limit';
   }
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- *
- * Adds CSS and JS to display soft_length_limits when they are set for the
- * title field.
- */
-function soft_length_limit_form_node_form_alter(&$form, $form_state) {
-  $type = $form['#node']->type;
-  $max_length = variable_get(SOFT_LENGTH_LIMIT_TITLE_MAX . '_' . $type, NULL);
-  $min_length = variable_get(SOFT_LENGTH_LIMIT_TITLE_MIN . '_' . $type, NULL);
-  if (!$min_length) {
-    $min_length = 0;
+  if (isset($sll_config['minimum_limit'])) {
+    $element['value']['#attributes']['data-soft-length-minimum'] = $sll_config['minimum_limit'];
+  }
+  // Length style select.
+  if (isset($sll_config['style_select']) && $sll_config['style_select']) {
+    $element['value']['#attributes']['data-soft-length-style-select'] = (int) $sll_config['style_select'];
   }
-  $style_select = variable_get(SOFT_LENGTH_STYLE_SELECT . '_' . $type, NULL);
 
-  // Play nice with title module ensuring that core's title is present.
-  if (isset($form['title']) && (!empty($max_length) || !empty($min_length))) {
-    $form['title']['#attributes']['class'][] = 'soft-length-limit';
-    $form['title']['#attributes']['data-soft-length-limit'] = $max_length;
-    $form['title']['#attributes']['data-soft-length-minimum'] = $min_length;
-    $form['#attached']['js'][] = drupal_get_path('module', 'soft_length_limit') . '/jquery.textchange.min.js';
-    $form['#attached']['js'][] = drupal_get_path('module', 'soft_length_limit') . '/soft_length_limit.js';
-    $form['#attached']['css'][] = drupal_get_path('module', 'soft_length_limit') . '/soft_length_limit.css';
-    $form['title']['#attributes']['data-soft-length-style-select'] = $style_select;
+  if (isset($element['#type']) && $element['#type'] === 'text_format') {
+    $element['#attributes'] = array_merge_recursive($element['#attributes'], $element['value']['#attributes']);
   }
+
+  $element['#attached']['library'][] = 'soft_length_limit/soft_length_limit';
 }
diff --git a/soft_length_limit.test b/soft_length_limit.test
deleted file mode 100644
index 43ae08ad9a647bfa43a462d937755a64ae10e46d..0000000000000000000000000000000000000000
--- a/soft_length_limit.test
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-/**
- * @file
- * Test case for soft_length_limit module
- */
-
-/**
- * Tests the relevant functionality provided by the soft_length_limit module.
- */
-class SoftLengthLimitWebTestCase extends DrupalWebTestCase {
-
-  /**
-   * Info for simpletest.module.
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Soft Length Limit',
-      'description' => 'Ensure that the soft limit is applied to the chosen fields',
-      'group' => 'Soft Length Limit',
-    );
-  }
-
-  /**
-   * Setup for individual tests.
-   */
-  public function setUp() {
-    // Enable any modules required for the test.
-    parent::setUp(array('soft_length_limit'));
-
-    // Creating the needed user.
-    $this->privileged_user = $this->drupalCreateUser(
-      array(
-        'bypass node access',
-        'administer nodes',
-        'administer taxonomy',
-        'edit terms in 1',
-      ));
-    $this->drupalLogin($this->privileged_user);
-
-    // Creating the needed field on the article node type.
-    $field = array(
-      'field_name' => 'field_test',
-      'type' => 'text_long',
-      'cardinality' => 1,
-      'settings' => array(),
-    );
-    field_create_field($field);
-
-    $instance = array(
-      'field_name' => 'field_test',
-      'entity_type' => 'node',
-      'label' => 'Test field',
-      'required' => 0,
-      'bundle' => 'article',
-      'description' => '',
-      'widget' => array(
-        'type' => 'text_textarea',
-        'weight' => -4,
-        'settings' => array(
-          'rows' => '5',
-          'soft_length_limit' => '666',
-        ),
-      ),
-      'display' => array(
-        'default' => array(
-          'type' => 'text_default',
-          'weight' => 10,
-        ),
-        'teaser' => array(
-          'type' => 'text_default',
-          'weight' => 10,
-        ),
-      ),
-    );
-    field_create_instance($instance);
-
-    $this->drupalGet('node/add/article');
-  }
-
-  /**
-   * Tests that input element has the attributes that the javascript should use.
-   */
-  public function testAll() {
-    $this->assertPattern('/<textarea .*class="[^"]*soft-length-limit[^"]*".*data-soft-length-limit="666"/', 'HTML element should contain the necessary attributesfor the soft length limiter javascript');
-  }
-}
diff --git a/tests/src/FunctionalJavascript/SoftLengthLimitJavascriptTest.php b/tests/src/FunctionalJavascript/SoftLengthLimitJavascriptTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..29235765e2e77cc3f5e0d708a07f4fd2d1284624
--- /dev/null
+++ b/tests/src/FunctionalJavascript/SoftLengthLimitJavascriptTest.php
@@ -0,0 +1,132 @@
+<?php
+
+namespace Drupal\Tests\soft_length_limit\FunctionalJavascript;
+
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+
+/**
+ * Tests soft length limit JavaScript behavior for text fields.
+ */
+class SoftLengthLimitJavascriptTest extends WebDriverTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'node',
+    'field',
+    'entity_test',
+    'soft_length_limit',
+    'text',
+  ];
+
+  /**
+   * Tests that the soft length limit widget works.
+   */
+  public function testSoftLengthLimitWithStyleSelect() {
+    $this->setupContentType();
+    $entity = EntityTest::create(['type' => 'entity_test', 'name' => 'Test Soft Length Limit.']);
+    $entity->save();
+
+    $admin = $this->drupalCreateUser(['administer entity_test content']);
+    $this->drupalLogin($admin);
+    $this->drupalGet($entity->toUrl('edit-form'));
+
+    $page = $this->getSession()->getPage();
+    $foo_field = $page->findField('Foo');
+    $foo_field->focus();
+    $foo_field->setValue('text');
+    $tooltip_text = $page->find('css', '.soft-length-limit-tooltip')->getText();
+
+    $foo_field->focus();
+    $this->assertEquals('4/20', $tooltip_text);
+  }
+
+  /**
+   * Tests that the soft length limit widget works.
+   */
+  public function testSoftLengthLimitWithoutStyleSelect() {
+    $this->setupContentType(FALSE);
+    $entity = EntityTest::create(['type' => 'entity_test', 'name' => 'Test Soft Length Limit.']);
+    $entity->save();
+
+    $admin = $this->drupalCreateUser(['administer entity_test content']);
+    $this->drupalLogin($admin);
+    $this->drupalGet($entity->toUrl('edit-form'));
+
+    $page = $this->getSession()->getPage();
+    $foo_field = $page->findField('Foo');
+    $foo_field->focus();
+    $foo_field->setValue('text');
+    $tooltip_text = $page->find('css', '.soft-length-limit-tooltip')->getText();
+
+    $foo_field->focus();
+    $this->assertEquals('Suggested minimum number of characters is 10, current count is 4. Content limited to 20 characters. Remaining: 16.',
+      $tooltip_text);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function drupalLogin(AccountInterface $account) {
+    // This is a clone of the UiHelperTrait::drupalLogin() method,
+    // which seems to make the test fail no matter what, when asserting the
+    // user session.
+    if ($this->loggedInUser) {
+      $this->drupalLogout();
+    }
+
+    $this->drupalGet('user/login');
+    $this->submitForm([
+      'name' => $account->getAccountName(),
+      'pass' => $account->passRaw,
+    ], t('Log in'));
+
+    // @see ::drupalUserIsLoggedIn()
+    $account->sessionId = $this->getSession()->getCookie(\Drupal::service('session_configuration')->getOptions(\Drupal::request())['name']);
+    $this->loggedInUser = $account;
+    $this->container->get('current_user')->setAccount($account);
+  }
+
+  /**
+   * Sets up the entity_test entity type with a field using soft_length_limit.
+   *
+   * @param bool $style_select
+   *   Whether to use style_select option of soft_length_limit.
+   *
+   * @throws \Drupal\Core\Entity\EntityStorageException
+   */
+  private function setupContentType($style_select = TRUE): void {
+    FieldStorageConfig::create([
+      'type'        => 'text_long',
+      'entity_type' => 'entity_test',
+      'field_name'  => 'foo',
+    ])->save();
+    FieldConfig::create([
+      'entity_type' => 'entity_test',
+      'bundle'      => 'entity_test',
+      'field_name'  => 'foo',
+      'label'       => 'Foo',
+      'description' => 'Description of a text field',
+    ])->save();
+    $widget = [
+      'type'                 => 'text_textarea',
+      'third_party_settings' => [
+        'soft_length_limit' => [
+          'max_limit'     => 20,
+          'minimum_limit' => 10,
+          'style_select'  => $style_select,
+        ],
+      ],
+    ];
+    EntityFormDisplay::load('entity_test.entity_test.default')
+      ->setComponent('foo', $widget)
+      ->save();
+  }
+
+}