diff --git a/README.md b/README.md
index e3a8e3560670ceed39869b8467b2ea7d88c88fa7..c6449a9f101162c132314dce250a7828ca503b67 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,7 @@ This module requires no modules outside of Drupal core.
     this dump runs inside the form_alter, it can result in incomplete page
     load. This is expected.
 
+
 ## Using Label Help to create form fields programmatically
 
 The Label Help field defines a theme option named 'description at top' which
@@ -63,9 +64,7 @@ function my_module_form($form, &$form_state) {
   $form['example'] = [
     '#type' => 'textfield',
     '#title' => t('Example'),
-    '#theme_options' => [
-      'description at top' => t('Label help text for the example field.'),
-    ],
+    '#label_help' => t('Label help text for the example field.'),
   ];
   return $form;
 }
@@ -73,28 +72,17 @@ function my_module_form($form, &$form_state) {
 
 ## Modifying form fields using hook_form_alter()
 
-Drupal's hook_form_alter() functions may be used in a custom module or theme
-to add Label Help text to existing form fields. For example, the following
-function uses hook_form_user_login_alter() to change Drupal's user login form
-so that it displays label text top of field 'example':
+Drupal's hook_form_alter() and hook_FORM_ID_alter() functions may be used
+in a custom module or theme to add Label Help text to existing form fields.
+The following example adds help text to the Article content type's Title field.
 
 ```php
-function my_module_form_user_login_alter(&$form, &$form_state, $form_id) {
-  $form['name']['#theme_options'] = [
-    'description at top' => t('Enter your @s username.', [
-      '@s' => variable_get('site_name', 'Drupal')
-    ]),
-  ];
-  unset($form['name']['#description']);
-  $form['pass']['#theme_options'] = [
-    'description at top' => t(
-      'Enter the password that accompanies your username.'
-    ),
-  ];
-  unset($form['pass']['#description']);
+function my_module_form_node_article_alter(&$form, &$form_state, $form_id) {
+  $form['title']['#label_help'] = t('Label help message for the Title field.');
 }
 ```
 
+
 ## Contributing
 
 Local development is done via DDEV and the `ddev-drupal-contrib` add-on.
diff --git a/label_help.module b/label_help.module
index 1ec1bb6346498ec2209342e43f8f06116f69ea32..9027abb8f005b426bea99fe8a4784e3d94673bab 100644
--- a/label_help.module
+++ b/label_help.module
@@ -47,36 +47,49 @@ function label_help_theme() {
  * Implements hook_form_alter().
  */
 function label_help_form_alter(&$form, &$form_state, $form_id) {
-  $children = array_intersect_key($form, array_flip(Element::children($form)));
-  $form_object = $form_state->getFormObject();
-  if (!method_exists($form_object, 'getEntity')) {
-    return;
-  }
-  $method = new ReflectionMethod($form_object, 'getEntity');
-  if (!$method->isPublic()) {
-    return;
-  }
-
-  $form_entity = $form_object->getEntity();
-  if (!method_exists($form_entity, 'getFieldDefinition')) {
-    return;
-  }
-  $method = new ReflectionMethod($form_entity, 'getFieldDefinition');
-  if (!$method->isPublic()) {
-    return;
-  }
+  $form['#process'][] = 'label_help_process_form';
+}
 
+/**
+ * Custom process callback for modifying form elements with Label Help.
+ */
+function label_help_process_form($element, FormStateInterface $form_state, &$form) {
   $debug = Settings::get('label_help_debug', FALSE);
   $debug_dump = Settings::get('label_help_debug_dump', FALSE);
   $one_time_debug_warning = &drupal_static(__FUNCTION__);
+  $children = array_intersect_key($form, array_flip(Element::children($form)));
   foreach ($children as $key => $item) {
     $use_case = 0;
-    $field = $form_object->getEntity()->getFieldDefinition($key);
-
     $content = NULL;
     $fallback_use_case = FALSE;
-    if ($field && method_exists($field, 'getThirdPartySetting')) {
-      $content = $field->getThirdPartySetting('label_help', 'label_help_description');
+
+    // There are two possible ways to add text content for Label Help:
+    // Option 1) via code, using a custom #label_help Form API property.
+    if (!empty($item['#label_help'])) {
+      $content = $item['#label_help'];
+    }
+    // Option 2) via the Field UI module in the Drupal web interface.
+    else {
+      $form_object = $form_state->getFormObject();
+      if (!method_exists($form_object, 'getEntity')) {
+        return $element;
+      }
+      $method = new ReflectionMethod($form_object, 'getEntity');
+      if (!$method->isPublic()) {
+        return $element;
+      }
+      $form_entity = $form_object->getEntity();
+      if (!method_exists($form_entity, 'getFieldDefinition')) {
+        return $element;
+      }
+      $method = new ReflectionMethod($form_entity, 'getFieldDefinition');
+      if (!$method->isPublic()) {
+        return $element;
+      }
+      $field = $form_entity->getFieldDefinition($key);
+      if ($field && method_exists($field, 'getThirdPartySetting')) {
+        $content = $field->getThirdPartySetting('label_help', 'label_help_description');
+      }
     }
     if (is_null($content) || strlen($content) === 0) {
       continue;
@@ -260,8 +273,16 @@ function label_help_form_alter(&$form, &$form_state, $form_id) {
         $fallback_use_case = TRUE;
       }
     }
-    else {
+
+    // Custom fields may not be defined with a container or a widget wrapper.
+    // To keep things simple, place the label in the field prefix.
+    elseif (isset($item['#type']) && empty($item['widget'])) {
       $use_case = 16;
+      $element = &$form[$key];
+      _label_help_append_label_suffix($element, $content, $use_case);
+    }
+    else {
+      $use_case = 17;
       $fallback_use_case = TRUE;
     }
 
@@ -291,6 +312,8 @@ function label_help_form_alter(&$form, &$form_state, $form_id) {
       dump($item);
     }
   }
+
+  return $form;
 }
 
 /**
diff --git a/modules/label_help_test/label_help_test.module b/modules/label_help_test/label_help_test.module
index b1a11f577194df9e2274293b55d699332a3757c5..046d0ea5740f03fbe8851e503f07afd45a16f3fb 100644
--- a/modules/label_help_test/label_help_test.module
+++ b/modules/label_help_test/label_help_test.module
@@ -4,3 +4,50 @@
  * @file
  * Primary module hooks for Label Help Test module.
  */
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Adds label help text to the core Article node form title field.
+ */
+function label_help_test_form_node_article_form_alter(&$form, &$form_state, $form_id) {
+  $form['title']['#label_help'] = t('Label help message for the Title field.');
+}
+
+/**
+ * Implements hook_form_alter().
+ *
+ * Adds label help text to fields in our Test content type form.
+ *
+ * This hook implementation targets the test_label_help_core_fields content type
+ * forms and adds label help text to the core title field and custom fields
+ * (textfield and checkbox).
+ */
+function label_help_test_form_alter(&$form, &$form_state, $form_id) {
+  // Only affect the specified content type's add/edit forms.
+  if (!in_array($form_id, [
+    'node_test_label_help_core_fields_form',
+    'node_test_label_help_core_fields_edit_form',
+  ])) {
+    return;
+  }
+
+  // Add Label Help text to the Node Title field.
+  if (isset($form['title'])) {
+    $form['title']['#label_help'] = t('Label help message for the Title field.');
+  }
+
+  // Create a custom text field with Label Help text.
+  $form['field_lh_textfield'] = [
+    '#type' => 'textfield',
+    '#title' => t('Custom text field'),
+    '#label_help' => t('Label help message for the Custom text field.'),
+  ];
+
+  // Create a custom checkbox field with Label Help text.
+  $form['field_lh_checkbox'] = [
+    '#type' => 'checkbox',
+    '#title' => t('Custom checkbox field'),
+    '#label_help' => t('Label help message for the Custom checkbox field.'),
+  ];
+}