From 9d522de0543fd8f674873988012994730e403b03 Mon Sep 17 00:00:00 2001
From: hooroomoo <57237-hooroomoo@users.noreply.drupalcode.org>
Date: Fri, 30 Jun 2023 19:27:14 +0000
Subject: [PATCH] Issue #3356894 by hooroomoo, lauriii, srishtiiee,
 Rajeshreeputra, Utkarsh_33, bnjmnm, narendraR, tim.plunkett, smustgrave,
 ckrina, yoroy, amateescu, Wim Leers: Make field selection less overwhelming
 by introducing groups

---
 .../Core/Field/FieldTypePluginManager.php     |  39 ++-
 .../Plugin/Field/FieldType/BooleanItem.php    |   2 +-
 .../Plugin/Field/FieldType/DecimalItem.php    |   8 +-
 .../Plugin/Field/FieldType/EmailItem.php      |   2 +-
 .../Field/FieldType/EntityReferenceItem.php   |   2 +-
 .../Plugin/Field/FieldType/FloatItem.php      |   8 +-
 .../Plugin/Field/FieldType/IntegerItem.php    |   7 +-
 .../Plugin/Field/FieldType/StringItem.php     |   9 +-
 .../Plugin/Field/FieldType/StringLongItem.php |   8 +-
 .../Plugin/Field/FieldType/TimestampItem.php  |   7 +-
 core/misc/cspell/dictionary.txt               |   1 +
 core/misc/icons/55565b/boolean.svg            |   4 +
 core/misc/icons/55565b/comment.svg            |   3 +
 core/misc/icons/55565b/date_and_time.svg      |   5 +
 core/misc/icons/55565b/daterange.svg          |   3 +
 core/misc/icons/55565b/email.svg              |   3 +
 core/misc/icons/55565b/file_upload.svg        |   4 +
 core/misc/icons/55565b/formatted_text.svg     |   4 +
 core/misc/icons/55565b/link.svg               |   4 +
 core/misc/icons/55565b/media.svg              |  11 +
 core/misc/icons/55565b/number.svg             |   4 +
 core/misc/icons/55565b/plain_text.svg         |   3 +
 core/misc/icons/55565b/reference.svg          |   3 +
 core/misc/icons/55565b/selection_list.svg     |   8 +
 core/misc/icons/55565b/telephone.svg          |   3 +
 .../icons/cacbd2/puzzle_piece_placeholder.svg |   1 +
 core/modules/comment/comment.libraries.yml    |   8 +
 core/modules/comment/comment.module           |  25 +-
 .../comment/css/comment.icon.theme.css        |  10 +
 .../comment/css/comment.icon.theme.pcss.css   |   4 +
 .../src/Functional/CommentNonNodeTest.php     |   8 +-
 .../ContentTranslationSettingsTest.php        |  10 +-
 .../Plugin/Field/FieldType/DateTimeItem.php   |   7 +-
 .../css/datetime_range.icon.theme.css         |   9 +
 .../css/datetime_range.icon.theme.pcss.css    |   3 +
 .../datetime_range.libraries.yml              |   7 +
 .../datetime_range/datetime_range.module      |   7 +
 .../FieldType/EditorTestTextLongItem.php      |   1 -
 core/modules/field/field.module               |  28 +++
 .../TestItemWithPreconfiguredOptions.php      |   1 -
 .../EntityReferenceAdminTest.php              |   8 +-
 .../EntityReferenceAdminTest.php              |  17 +-
 .../field_ui/css/field_ui.icons.theme.css     |  37 +++
 .../css/field_ui.icons.theme.pcss.css         |  38 +++
 .../css/field_ui_add_field.module.css         | 131 ++++++++++
 .../field_ui/css/field_ui_add_field.theme.css |  27 ++
 core/modules/field_ui/field_ui.api.php        |  26 ++
 core/modules/field_ui/field_ui.js             |  31 +++
 core/modules/field_ui/field_ui.libraries.yml  |   9 +
 core/modules/field_ui/field_ui.module         |  37 +++
 .../field_ui/src/Form/FieldStorageAddForm.php | 238 +++++++++++++++---
 .../form-element--new-storage-type.html.twig  |  46 ++++
 .../Functional/ManageFieldsFunctionalTest.php |  34 +--
 .../FunctionalJavascript/ManageFieldsTest.php |  76 ++++++
 .../tests/src/Traits/FieldUiJSTestTrait.php   |  80 ++++--
 .../tests/src/Traits/FieldUiTestTrait.php     | 110 ++++++--
 core/modules/file/css/file.icon.theme.css     |   9 +
 .../modules/file/css/file.icon.theme.pcss.css |   3 +
 core/modules/file/file.libraries.yml          |   8 +
 core/modules/file/file.module                 |  20 ++
 .../src/Plugin/Field/FieldType/FileItem.php   |   7 +-
 .../src/Functional/FileFieldDisplayTest.php   |  17 +-
 .../src/Plugin/Field/FieldType/ImageItem.php  |   8 +-
 .../Plugin/Field/FieldType/DummyAjaxItem.php  |   1 -
 .../src/Functional/LayoutBuilderTest.php      |  13 +-
 core/modules/link/css/link.icon.theme.css     |   9 +
 .../modules/link/css/link.icon.theme.pcss.css |   3 +
 core/modules/link/link.libraries.yml          |   7 +
 core/modules/link/link.module                 |   7 +
 core/modules/media/css/media.icon.theme.css   |  10 +
 .../media/css/media.icon.theme.pcss.css       |   4 +
 core/modules/media/media.libraries.yml        |   8 +
 core/modules/media/media.module               |  29 +--
 .../src/Functional/MediaUiFunctionalTest.php  |  20 +-
 .../MediaReferenceFieldHelpTest.php           |  44 ++--
 .../media_library/media_library.module        |   4 +
 .../FieldType/EntityReferenceItemSubclass.php |   4 +-
 .../options/css/options.icon.theme.css        |   9 +
 .../options/css/options.icon.theme.pcss.css   |   3 +
 core/modules/options/options.libraries.yml    |   7 +
 core/modules/options/options.module           |  20 ++
 .../Plugin/Field/FieldType/ListFloatItem.php  |   7 +-
 .../Field/FieldType/ListIntegerItem.php       |   7 +-
 .../Plugin/Field/FieldType/ListStringItem.php |   7 +-
 .../ResponsiveImageFieldUiTest.php            |  24 +-
 .../Functional/SearchPageCacheTagsTest.php    |  14 +-
 .../FieldType/AutoIncrementingTestItem.php    |   1 -
 .../ComputedTestCacheableStringItem.php       |   1 -
 .../Plugin/Field/FieldType/FieldTestItem.php  |   1 -
 .../InternalPropertyTestFieldItem.php         |   1 -
 .../Plugin/Field/FieldType/SerializedItem.php |   1 -
 .../FieldType/SerializedPropertyItem.php      |   1 -
 .../SingleInternalPropertyTestFieldItem.php   |   1 -
 .../EntityReferenceFieldCreationTest.php      |  10 +-
 .../src/Functional/System/DateTimeTest.php    |  21 +-
 .../telephone/css/telephone.icon.theme.css    |   9 +
 .../css/telephone.icon.theme.pcss.css         |   3 +
 .../Plugin/Field/FieldType/TelephoneItem.php  |   3 +-
 .../modules/telephone/telephone.libraries.yml |   7 +
 core/modules/telephone/telephone.module       |   7 +
 core/modules/text/css/text.icon.theme.css     |   9 +
 .../modules/text/css/text.icon.theme.pcss.css |   3 +
 .../src/Plugin/Field/FieldType/TextItem.php   |   9 +-
 .../Plugin/Field/FieldType/TextLongItem.php   |   8 +-
 .../Field/FieldType/TextWithSummaryItem.php   |   9 +-
 core/modules/text/text.libraries.yml          |   8 +
 core/modules/text/text.module                 |  20 ++
 .../tests/src/Functional/WorkspaceTest.php    |  13 +-
 core/phpstan-baseline.neon                    |  10 -
 .../Core/Theme/Stable9LibraryOverrideTest.php |   9 +
 .../form-element--new-storage-type.html.twig  |  46 ++++
 111 files changed, 1456 insertions(+), 317 deletions(-)
 create mode 100644 core/misc/icons/55565b/boolean.svg
 create mode 100644 core/misc/icons/55565b/comment.svg
 create mode 100644 core/misc/icons/55565b/date_and_time.svg
 create mode 100644 core/misc/icons/55565b/daterange.svg
 create mode 100644 core/misc/icons/55565b/email.svg
 create mode 100644 core/misc/icons/55565b/file_upload.svg
 create mode 100644 core/misc/icons/55565b/formatted_text.svg
 create mode 100644 core/misc/icons/55565b/link.svg
 create mode 100644 core/misc/icons/55565b/media.svg
 create mode 100644 core/misc/icons/55565b/number.svg
 create mode 100644 core/misc/icons/55565b/plain_text.svg
 create mode 100644 core/misc/icons/55565b/reference.svg
 create mode 100644 core/misc/icons/55565b/selection_list.svg
 create mode 100644 core/misc/icons/55565b/telephone.svg
 create mode 100644 core/misc/icons/cacbd2/puzzle_piece_placeholder.svg
 create mode 100644 core/modules/comment/css/comment.icon.theme.css
 create mode 100644 core/modules/comment/css/comment.icon.theme.pcss.css
 create mode 100644 core/modules/datetime_range/css/datetime_range.icon.theme.css
 create mode 100644 core/modules/datetime_range/css/datetime_range.icon.theme.pcss.css
 create mode 100644 core/modules/datetime_range/datetime_range.libraries.yml
 create mode 100644 core/modules/field_ui/css/field_ui.icons.theme.css
 create mode 100644 core/modules/field_ui/css/field_ui.icons.theme.pcss.css
 create mode 100644 core/modules/field_ui/css/field_ui_add_field.module.css
 create mode 100644 core/modules/field_ui/css/field_ui_add_field.theme.css
 create mode 100644 core/modules/field_ui/templates/form-element--new-storage-type.html.twig
 create mode 100644 core/modules/file/css/file.icon.theme.css
 create mode 100644 core/modules/file/css/file.icon.theme.pcss.css
 create mode 100644 core/modules/link/css/link.icon.theme.css
 create mode 100644 core/modules/link/css/link.icon.theme.pcss.css
 create mode 100644 core/modules/link/link.libraries.yml
 create mode 100644 core/modules/media/css/media.icon.theme.css
 create mode 100644 core/modules/media/css/media.icon.theme.pcss.css
 create mode 100644 core/modules/options/css/options.icon.theme.css
 create mode 100644 core/modules/options/css/options.icon.theme.pcss.css
 create mode 100644 core/modules/options/options.libraries.yml
 create mode 100644 core/modules/telephone/css/telephone.icon.theme.css
 create mode 100644 core/modules/telephone/css/telephone.icon.theme.pcss.css
 create mode 100644 core/modules/telephone/telephone.libraries.yml
 create mode 100644 core/modules/text/css/text.icon.theme.css
 create mode 100644 core/modules/text/css/text.icon.theme.pcss.css
 create mode 100644 core/themes/stable9/templates/admin/form-element--new-storage-type.html.twig

diff --git a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
index 6fb75fe1d721..ee68da04e430 100644
--- a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
+++ b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
@@ -8,6 +8,7 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
 use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\TypedData\TypedDataManagerInterface;
 
 /**
@@ -17,7 +18,14 @@
  */
 class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePluginManagerInterface {
 
-  use CategorizingPluginManagerTrait;
+  /**
+   * Default category for field types.
+   */
+  const DEFAULT_CATEGORY = 'general';
+
+  use CategorizingPluginManagerTrait {
+    getGroupedDefinitions as protected getGroupedDefinitionsTrait;
+  }
 
   /**
    * The typed data manager.
@@ -91,9 +99,13 @@ public function processDefinition(&$definition, $plugin_id) {
       $definition['list_class'] = '\Drupal\Core\Field\FieldItemList';
     }
 
-    // Ensure that every field type has a category.
-    if (empty($definition['category'])) {
-      $definition['category'] = $this->t('General');
+    if ($definition['category'] instanceof TranslatableMarkup) {
+      @trigger_error('Using a translatable string as a category for field type is deprecated in drupal:10.2.0 and is removed from drupal:11.0.0. See https://www.drupal.org/node/3364271', E_USER_DEPRECATED);
+      $definition['category'] = static::DEFAULT_CATEGORY;
+    }
+    elseif (empty($definition['category'])) {
+      // Ensure that every field type has a category.
+      $definition['category'] = static::DEFAULT_CATEGORY;
     }
   }
 
@@ -145,6 +157,23 @@ public function getFieldSettingsSummary(FieldDefinitionInterface $field_definiti
     return [];
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getGroupedDefinitions(array $definitions = NULL, $label_key = 'label') {
+    $grouped_categories = $this->getGroupedDefinitionsTrait($definitions, $label_key);
+    $category_info = \Drupal::moduleHandler()->invokeAll('field_type_category_info');
+    foreach ($grouped_categories as $group => $definitions) {
+      if (!isset($category_info[$group]) && $group !== static::DEFAULT_CATEGORY) {
+        assert(FALSE, "\"$group\" must be defined in hook_field_type_category_info().");
+        $grouped_categories[static::DEFAULT_CATEGORY] += $definitions;
+        unset($grouped_categories[$group]);
+      }
+    }
+
+    return $grouped_categories;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -162,7 +191,7 @@ public function getUiDefinitions() {
         foreach ($this->getPreconfiguredOptions($definition['id']) as $key => $option) {
           $definitions["field_ui:$id:$key"] = array_intersect_key(
             $option,
-            ['label' => 0, 'category' => 1]
+            ['label' => 0, 'category' => 1, 'weight' => 1, 'description' => 0]
           ) + $definition;
         }
       }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
index 51a29d860b87..b7b03cfd7d4e 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/BooleanItem.php
@@ -17,7 +17,7 @@
  * @FieldType(
  *   id = "boolean",
  *   label = @Translation("Boolean"),
- *   description = @Translation("An entity field containing a boolean value."),
+ *   description = @Translation("Field to store a true or false value."),
  *   default_widget = "boolean_checkbox",
  *   default_formatter = "boolean",
  * )
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.php
index 15b7b65545fb..3de0666d35b1 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.php
@@ -14,8 +14,12 @@
  * @FieldType(
  *   id = "decimal",
  *   label = @Translation("Number (decimal)"),
- *   description = @Translation("This field stores a number in the database in a fixed decimal format."),
- *   category = @Translation("Number"),
+ *   description = {
+ *     @Translation("Ideal for exact counts and measures (prices, temperatures, distances, volumes, etc.)"),
+ *     @Translation("Stores a number in the database in a fixed decimal format"),
+ *     @Translation("For example, 12.34 km or € when used for further detailed calculations (such as summing many of these)"),
+ *   },
+ *   category = "number",
  *   default_widget = "number",
  *   default_formatter = "number_decimal"
  * )
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
index 59e918ae94e3..639d8d5e2864 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EmailItem.php
@@ -16,7 +16,7 @@
  * @FieldType(
  *   id = "email",
  *   label = @Translation("Email"),
- *   description = @Translation("An entity field containing an email value."),
+ *   description = @Translation("Field to store an email address."),
  *   default_widget = "email_default",
  *   default_formatter = "basic_string"
  * )
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index b630ea56059a..c9b2623233f6 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -36,7 +36,7 @@
  *   id = "entity_reference",
  *   label = @Translation("Entity reference"),
  *   description = @Translation("An entity field containing an entity reference."),
- *   category = @Translation("Reference"),
+ *   category = "reference",
  *   default_widget = "entity_reference_autocomplete",
  *   default_formatter = "entity_reference_label",
  *   list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
index 739a62c76e67..797543255ee7 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/FloatItem.php
@@ -13,8 +13,12 @@
  * @FieldType(
  *   id = "float",
  *   label = @Translation("Number (float)"),
- *   description = @Translation("This field stores a number in the database in a floating point format."),
- *   category = @Translation("Number"),
+ *   description = {
+ *     @Translation("In most instances, it is best to use Number (decimal) instead, as decimal numbers stored as floats may contain errors in precision"),
+ *     @Translation("This type of field offers faster processing and more compact storage, but the differences are typically negligible on modern sites"),
+ *     @Translation("For example, 123.4 km when used in imprecise contexts such as a walking trail distance"),
+ *   },
+ *   category = "number",
  *   default_widget = "number",
  *   default_formatter = "number_decimal"
  * )
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index 1ce7a4463f37..b927a4de8f1c 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -13,8 +13,11 @@
  * @FieldType(
  *   id = "integer",
  *   label = @Translation("Number (integer)"),
- *   description = @Translation("This field stores a number in the database as an integer."),
- *   category = @Translation("Number"),
+ *   description = {
+ *     @Translation("Number without decimals"),
+ *     @Translation("For example, 123"),
+ *   },
+ *   category = "number",
  *   default_widget = "number",
  *   default_formatter = "number_integer"
  * )
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
index 10e216e1dbc3..ebb867f37021 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringItem.php
@@ -13,8 +13,13 @@
  * @FieldType(
  *   id = "string",
  *   label = @Translation("Text (plain)"),
- *   description = @Translation("A field containing a plain string value."),
- *   category = @Translation("Text"),
+ *   description = {
+ *     @Translation("Ideal for titles and names"),
+ *     @Translation("Efficient storage for short text"),
+ *     @Translation("Requires specifying a maximum length"),
+ *     @Translation("Good for fields with known or predictable length"),
+ *   },
+ *   category = "plain_text",
  *   default_widget = "string_textfield",
  *   default_formatter = "string"
  * )
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
index 93c6b27bcb67..b98f76fbfb8a 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/StringLongItem.php
@@ -12,8 +12,12 @@
  * @FieldType(
  *   id = "string_long",
  *   label = @Translation("Text (plain, long)"),
- *   description = @Translation("A field containing a long string value."),
- *   category = @Translation("Text"),
+ *   description = {
+ *     @Translation("Ideal for longer texts, like body or description"),
+ *     @Translation("Supports long text without specifying a maximum length"),
+ *     @Translation("May use more storage and be slower for searching and sorting"),
+ *   },
+ *   category = "plain_text",
  *   default_widget = "string_textarea",
  *   default_formatter = "basic_string",
  * )
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
index 5706ed72f846..44ae5a7f1880 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/TimestampItem.php
@@ -13,7 +13,12 @@
  * @FieldType(
  *   id = "timestamp",
  *   label = @Translation("Timestamp"),
- *   description = @Translation("An entity field containing a UNIX timestamp value."),
+ *   description = {
+ *     @Translation("Ideal for using date and time calculations or comparisons"),
+ *     @Translation("Date and time stored in the form of seconds since January 1, 1970 (UTC)"),
+ *     @Translation("Compact and efficient for storage, sorting and calculations"),
+ *   },
+ *   category = "date_time",
  *   default_widget = "datetime_timestamp",
  *   default_formatter = "timestamp",
  *   constraints = {
diff --git a/core/misc/cspell/dictionary.txt b/core/misc/cspell/dictionary.txt
index bc93d98426f8..c9e0b9e9b698 100644
--- a/core/misc/cspell/dictionary.txt
+++ b/core/misc/cspell/dictionary.txt
@@ -1139,6 +1139,7 @@ subforms
 subjectkeyword
 subkey
 subkeys
+suboption
 subparse
 subplugins
 subproject
diff --git a/core/misc/icons/55565b/boolean.svg b/core/misc/icons/55565b/boolean.svg
new file mode 100644
index 000000000000..f6cb6782f06a
--- /dev/null
+++ b/core/misc/icons/55565b/boolean.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="16" viewBox="0 0 24 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="1" y="1" width="22" height="14" rx="7" stroke="#55565B" stroke-width="2"/>
+<rect x="12" y="4" width="8" height="8" rx="4" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/comment.svg b/core/misc/icons/55565b/comment.svg
new file mode 100644
index 000000000000..9548d11fee43
--- /dev/null
+++ b/core/misc/icons/55565b/comment.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="21" fill="none">
+  <path d="M13 20.5L10.2 17H5a1 1 0 0 1-1-1V5.103a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1V16a1 1 0 0 1-1 1h-5.2L13 20.5zm1.839-5.5H20V6.103H6V15h5.161L13 17.298 14.839 15zM1 0h17v2H2v11H0V1a1 1 0 0 1 1-1z" fill="#55565b"/>
+</svg>
diff --git a/core/misc/icons/55565b/date_and_time.svg b/core/misc/icons/55565b/date_and_time.svg
new file mode 100644
index 000000000000..42f5dc730c96
--- /dev/null
+++ b/core/misc/icons/55565b/date_and_time.svg
@@ -0,0 +1,5 @@
+<svg width="23" height="21" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M19 14.857h-2V12h-1v4h3v-1.143Z" fill="#55565B"/>
+  <path d="M17.125 1.75h-4.004V0h-1.75v1.75H6.123V0h-1.75v1.75H.876A.875.875 0 0 0 0 2.623v14.501c0 .483.392.875.875.875h11.248a5.603 5.603 0 0 1-.954-2H2V6.123h14v2.911a5.69 5.69 0 0 1 2 .135V2.624a.875.875 0 0 0-.875-.875Z" fill="#55565B"/>
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M16.626 18.503a3.877 3.877 0 1 0 0-7.753 3.877 3.877 0 0 0 0 7.753Zm0 1.75a5.627 5.627 0 1 0 0-11.253 5.627 5.627 0 0 0 0 11.253Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/daterange.svg b/core/misc/icons/55565b/daterange.svg
new file mode 100644
index 000000000000..53481d2bfadf
--- /dev/null
+++ b/core/misc/icons/55565b/daterange.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none">
+  <path d="M15 2h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h4V0h2v2h6V0h2v2ZM2 8v10h16V8H2Zm2 2h2v2H4v-2Zm5 0h2v2H9v-2Zm5 0h2v2h-2v-2Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/email.svg b/core/misc/icons/55565b/email.svg
new file mode 100644
index 000000000000..143b2b51d92e
--- /dev/null
+++ b/core/misc/icons/55565b/email.svg
@@ -0,0 +1,3 @@
+<svg width="17" height="17" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M8.954.748C6.498.738 3.977 1.815 2.538 3.84 1.041 5.917.663 8.658 1.158 11.135c.35 1.653 1.316 3.216 2.796 4.08 1.688 1.02 3.747 1.173 5.671.953a9.483 9.483 0 0 0 2.8-.78v-1.83c-1.985.823-4.254 1.224-6.364.633-1.482-.418-2.687-1.696-3.013-3.2-.39-1.533-.295-3.18.198-4.675.528-1.494 1.674-2.787 3.178-3.33a7.097 7.097 0 0 1 4.756-.134c1.364.5 2.477 1.679 2.817 3.091.354 1.293.314 2.721-.186 3.965-.22.525-.72 1.06-1.34.94a.783.783 0 0 1-.647-.692c-.183-.867.042-1.753.028-2.63.047-.886.094-1.772.143-2.659-1.4-.419-2.911-.688-4.355-.35-1.462.4-2.583 1.716-2.827 3.206-.214 1.11-.121 2.308.392 3.317.474.865 1.393 1.48 2.389 1.519 1.098.103 2.269-.356 2.893-1.284.407.928 1.495 1.456 2.483 1.268 1.18-.162 2.131-1.085 2.547-2.167.687-1.605.656-3.453.18-5.112-.568-1.831-1.982-3.398-3.806-4.035A7.905 7.905 0 0 0 8.954.748ZM8.838 6.07c.389-.001.778.03 1.155.112-.11 1.265.025 2.59-.461 3.788-.233.506-.741.899-1.308.891-.594.059-1.253-.29-1.392-.902-.265-.812-.187-1.711.056-2.517.271-.74.963-1.355 1.779-1.363a2.63 2.63 0 0 1 .171-.01Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/file_upload.svg b/core/misc/icons/55565b/file_upload.svg
new file mode 100644
index 000000000000..39bbdeccbda6
--- /dev/null
+++ b/core/misc/icons/55565b/file_upload.svg
@@ -0,0 +1,4 @@
+<svg width="18" height="20" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M12 2H2v16h14V6h-4V2ZM0 .992C0 .444.447 0 .999 0H13l5 5v13.992A1 1 0 0 1 17.007 20H.993A1 1 0 0 1 0 19.008V.992Z" fill="#55565B"/>
+  <path d="M10.25 13v3h-2.5v-3H5l4-5 4 5h-2.75Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/formatted_text.svg b/core/misc/icons/55565b/formatted_text.svg
new file mode 100644
index 000000000000..916f8c7dc328
--- /dev/null
+++ b/core/misc/icons/55565b/formatted_text.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="14" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M24 2H14V0h10v2ZM24 8H11V6h13v2ZM24 14H9v-2h15v2Z" fill="#55565B"/>
+  <path d="M0 13c.612 0 1.272.123 1.5 0 .365-.198.399-.352.581-1.034L5.003 1C4.007.993 3.21.989 2.5 1.5c-.71.504-1.054 1.487-1.523 2.475H0L.977 0H12v4l-1.25-.025c-.033-1.362-.398-2.292-1.095-2.79C9.35.968 8.5 1 8 1L5.17 11.435l-.167.726a1.738 1.738 0 0 0-.049.419c0 .36 0 .325.215.42.215.089 1.147-.048 1.831 0v1H0v-1Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/link.svg b/core/misc/icons/55565b/link.svg
new file mode 100644
index 000000000000..7935ca9c6c0c
--- /dev/null
+++ b/core/misc/icons/55565b/link.svg
@@ -0,0 +1,4 @@
+<svg width="21" height="20" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M8.197 10.906a4.529 4.529 0 0 0 6.832.49l2.718-2.719a4.53 4.53 0 0 0-6.406-6.405L9.783 3.82" stroke="#55565B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M11.821 9.094a4.53 4.53 0 0 0-6.831-.49l-2.718 2.719a4.53 4.53 0 0 0 6.405 6.405l1.55-1.549" stroke="#55565B" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/core/misc/icons/55565b/media.svg b/core/misc/icons/55565b/media.svg
new file mode 100644
index 000000000000..8e675d2a41bc
--- /dev/null
+++ b/core/misc/icons/55565b/media.svg
@@ -0,0 +1,11 @@
+<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <g clip-path="url(#a)" fill-rule="evenodd" clip-rule="evenodd" fill="#55565B">
+    <path d="m19.187 4.864-8.936 2v.16h-.001v8.485a2.401 2.401 0 0 0-1.91-.195c-1.22.376-1.93 1.583-1.587 2.696.343 1.113 1.61 1.71 2.83 1.334 1.084-.334 1.765-1.324 1.664-2.32h.003V9.801l6.93-1.551v6.136a2.401 2.401 0 0 0-1.909-.196c-1.22.376-1.93 1.583-1.587 2.696.343 1.113 1.61 1.71 2.83 1.335 1.083-.334 1.765-1.324 1.663-2.32h.003V8.026l.007-.002v-3.16Z"/>
+    <path d="M13.504.744H.387V12.16h13.117V.744Zm-1.166 1.014H1.553v9.387h.104l2.35-2.281 2.056 1.997L9.25 5.913l2.131 3.309.395-.554.562.788V1.758ZM6.22 4.508a1.943 1.943 0 1 1-3.886 0 1.943 1.943 0 0 1 3.886 0Z"/>
+  </g>
+  <defs>
+    <clipPath id="a">
+      <path fill="#fff" d="M0 0h20v20H0z"/>
+    </clipPath>
+  </defs>
+</svg>
diff --git a/core/misc/icons/55565b/number.svg b/core/misc/icons/55565b/number.svg
new file mode 100644
index 000000000000..709a04a8d8e5
--- /dev/null
+++ b/core/misc/icons/55565b/number.svg
@@ -0,0 +1,4 @@
+<svg width="24" height="14" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path fill-rule="evenodd" clip-rule="evenodd" d="M8.654 4.697H6.848v-.292C6.848 2.303 8.6.6 10.76.6c2.16 0 3.913 1.703 3.913 3.805 0 .907-.327 1.74-.872 2.394l-.003.004-3.992 4.625h4.867v1.757H6.848v-1.06l5.527-6.404c.307-.356.493-.815.493-1.316 0-1.132-.944-2.049-2.107-2.049-1.164 0-2.107.917-2.107 2.049v.292ZM23.216.976v1.2l-2.703 3.332C22.23 5.962 23.5 7.572 23.5 9.49c0 2.27-1.78 4.11-3.975 4.11-1.863 0-3.426-1.325-3.857-3.113l-.068-.284 1.652-.428.07.285c.245 1.022 1.14 1.778 2.203 1.778 1.254 0 2.271-1.051 2.271-2.348S20.78 7.14 19.525 7.14a2.2 2.2 0 0 0-1.008.244l-.37.204-.626-1.135 3.016-3.717h-4.419V.976h7.098Z" fill="#55565B"/>
+  <path d="M4.891.976v12.209H2.935V2.88L0 3.566V1.802L3.544.976h1.347Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/plain_text.svg b/core/misc/icons/55565b/plain_text.svg
new file mode 100644
index 000000000000..24f1f075b016
--- /dev/null
+++ b/core/misc/icons/55565b/plain_text.svg
@@ -0,0 +1,3 @@
+<svg width="12" height="15" viewBox="0 0 12 15" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 2V0H12V2H7V15H5V2H0Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/reference.svg b/core/misc/icons/55565b/reference.svg
new file mode 100644
index 000000000000..92f5b08b7265
--- /dev/null
+++ b/core/misc/icons/55565b/reference.svg
@@ -0,0 +1,3 @@
+<svg width="18" height="18" fill="none" xmlns="http://www.w3.org/2000/svg">
+  <path d="M12 0a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-2v2h4a1 1 0 0 1 1 1v3h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-6a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2v-2H5v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V9a1 1 0 0 1 1-1h4V6H6a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1h6ZM6 14H2v2h4v-2Zm10 0h-4v2h4v-2ZM11 2H7v2h4V2Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/selection_list.svg b/core/misc/icons/55565b/selection_list.svg
new file mode 100644
index 000000000000..6949da6a0242
--- /dev/null
+++ b/core/misc/icons/55565b/selection_list.svg
@@ -0,0 +1,8 @@
+<svg width="18" height="15" viewBox="0 0 18 15" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5 6.5H18V8.5H5V6.5Z" fill="#55565B"/>
+<path d="M3 6H0V9H3V6Z" fill="#55565B"/>
+<path d="M18 12.5H5V14.5H18V12.5Z" fill="#55565B"/>
+<path d="M3 12H0V15H3V12Z" fill="#55565B"/>
+<path d="M18 0.5H5V2.5H18V0.5Z" fill="#55565B"/>
+<path d="M3 0H0V3H3V0Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/55565b/telephone.svg b/core/misc/icons/55565b/telephone.svg
new file mode 100644
index 000000000000..32bb1a7b77cf
--- /dev/null
+++ b/core/misc/icons/55565b/telephone.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none">
+  <path d="M4.548 7.943a14.422 14.422 0 0 0 7.55 7.397c.552-.4.863-1.095 1.235-1.66.278-.275.834-.81 1.39-.55 1.381.65 2.04.877 3.61 1.1.155.021.579.164.834.19.383.037.833.36.833 1.136v3.07c0 .55-.451 1.282-1.033 1.323-.486.034-.882.051-1.19.051C7.918 20 0 11.862 0 2.198c0-.303.017-.696.052-1.176C.092.446.833 0 1.389 0h3.104c.785 0 1.11.445 1.149.824.025.252.17.672.191.825.225 1.552.455 2.205 1.111 3.572.265.55-.277 1.099-.555 1.374-.6.387-1.476.733-1.84 1.348Z" fill="#55565B"/>
+</svg>
diff --git a/core/misc/icons/cacbd2/puzzle_piece_placeholder.svg b/core/misc/icons/cacbd2/puzzle_piece_placeholder.svg
new file mode 100644
index 000000000000..8c341ee4420f
--- /dev/null
+++ b/core/misc/icons/cacbd2/puzzle_piece_placeholder.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="21" fill="none"><path stroke="#CACBD2" stroke-width="2" d="M1 4.012h6c-.5-3.5 5-4.5 5 0h5v5c4-.5 6 6 0 6v4.5H1v-16.5"/></svg>
\ No newline at end of file
diff --git a/core/modules/comment/comment.libraries.yml b/core/modules/comment/comment.libraries.yml
index 25c815116197..d1106ca8fc05 100644
--- a/core/modules/comment/comment.libraries.yml
+++ b/core/modules/comment/comment.libraries.yml
@@ -36,3 +36,11 @@ drupal.node-new-comments-link:
     - core/once
     - core/drupal
     - history/api
+
+drupal.comment-icon:
+  version: VERSION
+  css:
+    theme:
+      css/comment.icon.theme.css: {}
+  dependencies:
+    - field_ui/drupal.field_ui.manage_fields
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 0cbc859155a4..8caa332efca4 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -238,12 +238,24 @@ function comment_form_field_ui_field_storage_add_form_alter(&$form, FormStateInt
     $form['#title'] = \Drupal::service('comment.manager')->getFieldUIPageTitle($route_match->getParameter('commented_entity_type'), $route_match->getParameter('field_name'));
   }
   if (!_comment_entity_uses_integer_id($form_state->get('entity_type_id'))) {
-    $optgroup = (string) t('General');
-    // You cannot use comment fields on entity types with non-integer IDs.
-    unset($form['add']['new_storage_type']['#options'][$optgroup]['comment']);
+    $form['add']['new_storage_type']['#process'][] = 'comment_new_storage_type_process_callback';
   }
 }
 
+/**
+ * Process callback to remove comment type field option.
+ */
+function comment_new_storage_type_process_callback($element, &$form_state, $form) {
+  foreach ($element as $key => $value) {
+    if (isset($value['radio']['#return_value']) && $value['radio']['#return_value'] === 'comment') {
+      // You cannot use comment fields on entity types with non-integer IDs.
+      unset($element[$key]);
+    }
+  }
+
+  return $element;
+}
+
 /**
  * Implements hook_form_FORM_ID_alter().
  */
@@ -771,3 +783,10 @@ function comment_entity_view_display_presave(EntityViewDisplayInterface $display
     }
   }
 }
+
+/**
+ * Implements hook_preprocess_form_element__new_storage_type().
+ */
+function comment_preprocess_form_element__new_storage_type(&$variables) {
+  $variables['#attached']['library'][] = 'comment/drupal.comment-icon';
+}
diff --git a/core/modules/comment/css/comment.icon.theme.css b/core/modules/comment/css/comment.icon.theme.css
new file mode 100644
index 000000000000..bc82aa674a08
--- /dev/null
+++ b/core/modules/comment/css/comment.icon.theme.css
@@ -0,0 +1,10 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+.field-icon-comment {
+  color: red;
+  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='22' height='21' fill='none'%3e  %3cpath d='M13 20.5L10.2 17H5a1 1 0 0 1-1-1V5.103a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1V16a1 1 0 0 1-1 1h-5.2L13 20.5zm1.839-5.5H20V6.103H6V15h5.161L13 17.298 14.839 15zM1 0h17v2H2v11H0V1a1 1 0 0 1 1-1z' fill='%2355565b'/%3e%3c/svg%3e");
+}
diff --git a/core/modules/comment/css/comment.icon.theme.pcss.css b/core/modules/comment/css/comment.icon.theme.pcss.css
new file mode 100644
index 000000000000..18201178f32d
--- /dev/null
+++ b/core/modules/comment/css/comment.icon.theme.pcss.css
@@ -0,0 +1,4 @@
+.field-icon-comment {
+  color:red;
+  background-image: url(../../../misc/icons/55565b/comment.svg);
+}
diff --git a/core/modules/comment/tests/src/Functional/CommentNonNodeTest.php b/core/modules/comment/tests/src/Functional/CommentNonNodeTest.php
index 16b809f3b174..9b24d387e5b7 100644
--- a/core/modules/comment/tests/src/Functional/CommentNonNodeTest.php
+++ b/core/modules/comment/tests/src/Functional/CommentNonNodeTest.php
@@ -501,9 +501,9 @@ public function testsNonIntegerIdEntities() {
     // Visit the Field UI field add page.
     $this->drupalGet('entity_test_string_id/structure/entity_test/fields/add-field');
     // Ensure field isn't shown for string IDs.
-    $this->assertSession()->optionNotExists('edit-new-storage-type', 'comment');
+    $this->assertSession()->elementNotExists('css', "[name='new_storage_type'][value='comment']");
     // Ensure a core field type shown.
-    $this->assertSession()->optionExists('edit-new-storage-type', 'boolean');
+    $this->assertSession()->elementExists('css', "[name='new_storage_type'][value='boolean']");
 
     // Attempt to add a comment-type referencing this entity-type.
     $this->drupalGet('admin/structure/comment/types/add');
@@ -518,9 +518,9 @@ public function testsNonIntegerIdEntities() {
     // Visit the Field UI field add page.
     $this->drupalGet('entity_test_no_id/structure/entity_test/fields/add-field');
     // Ensure field isn't shown for empty IDs.
-    $this->assertSession()->optionNotExists('edit-new-storage-type', 'comment');
+    $this->assertSession()->elementNotExists('css', "[name='new_storage_type'][value='comment']");
     // Ensure a core field type shown.
-    $this->assertSession()->optionExists('edit-new-storage-type', 'boolean');
+    $this->assertSession()->elementExists('css', "[name='new_storage_type'][value='boolean']");
   }
 
 }
diff --git a/core/modules/content_translation/tests/src/Functional/ContentTranslationSettingsTest.php b/core/modules/content_translation/tests/src/Functional/ContentTranslationSettingsTest.php
index 73c2ad319398..88c4576a020c 100644
--- a/core/modules/content_translation/tests/src/Functional/ContentTranslationSettingsTest.php
+++ b/core/modules/content_translation/tests/src/Functional/ContentTranslationSettingsTest.php
@@ -10,6 +10,7 @@
 use Drupal\field\Entity\FieldConfig;
 use Drupal\language\Entity\ContentLanguageSettings;
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 
 /**
  * Tests the content translation settings UI.
@@ -19,6 +20,7 @@
 class ContentTranslationSettingsTest extends BrowserTestBase {
 
   use CommentTestTrait;
+  use FieldUiTestTrait;
 
   /**
    * Modules to enable.
@@ -290,13 +292,7 @@ public function testFieldTranslatableSettingsUI() {
     // At least one field needs to be translatable to enable article for
     // translation. Create an extra field to be used for this purpose. We use
     // the UI to test our form alterations.
-    $edit = [
-      'new_storage_type' => 'text',
-      'label' => 'Test',
-      'field_name' => 'article_text',
-    ];
-    $this->drupalGet('admin/structure/types/manage/article/fields/add-field');
-    $this->submitForm($edit, 'Save and continue');
+    $this->fieldUIAddNewField('admin/structure/types/manage/article', 'article_text', 'Test', 'text');
 
     // Tests that field doesn't have translatable setting if bundle is not
     // translatable.
diff --git a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
index 5e17728b99bc..1a5b215a3978 100644
--- a/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
+++ b/core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
@@ -15,7 +15,12 @@
  * @FieldType(
  *   id = "datetime",
  *   label = @Translation("Date"),
- *   description = @Translation("Create and store date values."),
+ *   description = {
+ *     @Translation("Ideal when date and time needs to be input by users, like event dates and times"),
+ *     @Translation("Date or date and time stored in a readable string format"),
+ *     @Translation("Easy to read and understand for humans"),
+ *   },
+ *   category = "date_time",
  *   default_widget = "datetime_default",
  *   default_formatter = "datetime_default",
  *   list_class = "\Drupal\datetime\Plugin\Field\FieldType\DateTimeFieldItemList",
diff --git a/core/modules/datetime_range/css/datetime_range.icon.theme.css b/core/modules/datetime_range/css/datetime_range.icon.theme.css
new file mode 100644
index 000000000000..786ef59a15e4
--- /dev/null
+++ b/core/modules/datetime_range/css/datetime_range.icon.theme.css
@@ -0,0 +1,9 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+.field-icon-daterange {
+  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='none'%3e  %3cpath d='M15 2h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h4V0h2v2h6V0h2v2ZM2 8v10h16V8H2Zm2 2h2v2H4v-2Zm5 0h2v2H9v-2Zm5 0h2v2h-2v-2Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
diff --git a/core/modules/datetime_range/css/datetime_range.icon.theme.pcss.css b/core/modules/datetime_range/css/datetime_range.icon.theme.pcss.css
new file mode 100644
index 000000000000..01b7955cccf3
--- /dev/null
+++ b/core/modules/datetime_range/css/datetime_range.icon.theme.pcss.css
@@ -0,0 +1,3 @@
+.field-icon-daterange {
+  background-image: url(../../../misc/icons/55565b/daterange.svg);
+}
diff --git a/core/modules/datetime_range/datetime_range.libraries.yml b/core/modules/datetime_range/datetime_range.libraries.yml
new file mode 100644
index 000000000000..178d96067195
--- /dev/null
+++ b/core/modules/datetime_range/datetime_range.libraries.yml
@@ -0,0 +1,7 @@
+drupal.datetime_range-icon:
+  version: VERSION
+  css:
+    theme:
+      css/datetime_range.icon.theme.css: {}
+  dependencies:
+    - field_ui/drupal.field_ui.manage_fields
diff --git a/core/modules/datetime_range/datetime_range.module b/core/modules/datetime_range/datetime_range.module
index 0dbbd8d70d16..65a9e692dff9 100644
--- a/core/modules/datetime_range/datetime_range.module
+++ b/core/modules/datetime_range/datetime_range.module
@@ -27,3 +27,10 @@ function datetime_range_help($route_name, RouteMatchInterface $route_match) {
       return $output;
   }
 }
+
+/**
+ * Implements hook_preprocess_form_element__new_storage_type().
+ */
+function datetime_range_preprocess_form_element__new_storage_type(&$variables) {
+  $variables['#attached']['library'][] = 'datetime_range/drupal.datetime_range-icon';
+}
diff --git a/core/modules/editor/tests/modules/editor_test/src/Plugin/Field/FieldType/EditorTestTextLongItem.php b/core/modules/editor/tests/modules/editor_test/src/Plugin/Field/FieldType/EditorTestTextLongItem.php
index 14d22cab7fcb..5c0c964a6bcb 100644
--- a/core/modules/editor/tests/modules/editor_test/src/Plugin/Field/FieldType/EditorTestTextLongItem.php
+++ b/core/modules/editor/tests/modules/editor_test/src/Plugin/Field/FieldType/EditorTestTextLongItem.php
@@ -11,7 +11,6 @@
  *   id = "editor_test_text_long",
  *   label = @Translation("Filter test text (formatted, long)"),
  *   description = @Translation("This field stores a long text with a text format."),
- *   category = @Translation("Text"),
  *   default_widget = "text_textarea",
  *   default_formatter = "text_default"
  * )
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index d5a8182048a6..3b637910a88f 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -426,3 +426,31 @@ function field_field_config_presave(FieldConfigInterface $field) {
     ]);
   }
 }
+
+/**
+ * Returns field category properties.
+ */
+function field_field_type_category_info() {
+  return [
+    'plain_text' => [
+      'label' => t('Plain text'),
+      'description' => t('Text field that does not support markup.'),
+      'weight' => -50,
+    ],
+    'number' => [
+      'label' => t('Number'),
+      'description' => t('Field to store number. I.e. id, price, or quantity.'),
+      'weight' => -40,
+    ],
+    'reference' => [
+      'label' => t('Reference'),
+      'description' => t('Field to reference other content.'),
+      'weight' => -30,
+    ],
+    'date_time' => [
+      'label' => t('Date and time'),
+      'description' => t('Field to store date and time values.'),
+      'weight' => -10,
+    ],
+  ];
+}
diff --git a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithPreconfiguredOptions.php b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithPreconfiguredOptions.php
index 1c5754238af4..a35642ac134d 100644
--- a/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithPreconfiguredOptions.php
+++ b/core/modules/field/tests/modules/field_test/src/Plugin/Field/FieldType/TestItemWithPreconfiguredOptions.php
@@ -26,7 +26,6 @@ public static function getPreconfiguredOptions() {
     return [
       'custom_options' => [
         'label' => new TranslatableMarkup('All custom options'),
-        'category' => new TranslatableMarkup('Custom category'),
         'field_storage_config' => [
           'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
           'settings' => [
diff --git a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
index 328fcd641e7f..5c2d299fcfae 100644
--- a/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
+++ b/core/modules/field/tests/src/Functional/EntityReference/EntityReferenceAdminTest.php
@@ -113,13 +113,7 @@ public function testFieldAdminHandler() {
 
     // Create a test entity reference field.
     $field_name = 'test_entity_ref_field';
-    $edit = [
-      'new_storage_type' => 'field_ui:entity_reference:node',
-      'label' => 'Test Entity Reference Field',
-      'field_name' => $field_name,
-    ];
-    $this->drupalGet($bundle_path . '/fields/add-field');
-    $this->submitForm($edit, 'Save and continue');
+    $this->fieldUIAddNewField($bundle_path, $field_name, 'Test Entity Reference Field', 'field_ui:entity_reference:node', [], [], FALSE);
 
     // Set to unlimited.
     $edit = [
diff --git a/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php
index bc7a4aa91320..6b32c4eedc02 100644
--- a/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php
+++ b/core/modules/field/tests/src/FunctionalJavascript/EntityReference/EntityReferenceAdminTest.php
@@ -6,6 +6,7 @@
 use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Core\Url;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+use Drupal\Tests\field_ui\Traits\FieldUiJSTestTrait;
 use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
@@ -18,6 +19,7 @@
 class EntityReferenceAdminTest extends WebDriverTestBase {
 
   use FieldUiTestTrait;
+  use FieldUiJSTestTrait;
 
   /**
    * Modules to install.
@@ -113,20 +115,19 @@ public function testFieldAdminHandler() {
     $bundle_path = 'admin/structure/types/manage/' . $this->type;
 
     $page = $this->getSession()->getPage();
+    /** @var \Drupal\FunctionalJavascriptTests\JSWebAssert $assert_session */
     $assert_session = $this->assertSession();
 
     // First step: 'Add new field' on the 'Manage fields' page.
     $this->drupalGet($bundle_path . '/fields/add-field');
 
     // Check if the commonly referenced entity types appear in the list.
-    $this->assertSession()->optionExists('edit-new-storage-type', 'field_ui:entity_reference:node');
-    $this->assertSession()->optionExists('edit-new-storage-type', 'field_ui:entity_reference:user');
-
-    $page->findField('new_storage_type')->setValue('entity_reference');
-    $assert_session->waitForField('label')->setValue('Test');
-    $machine_name = $assert_session->waitForElement('xpath', '//*[@id="edit-label-machine-name-suffix"]/span[contains(text(), "field_test")]');
-    $this->assertNotEmpty($machine_name);
-    $page->pressButton('Save and continue');
+    $page->find('css', "[name='new_storage_type'][value='reference']")->click();
+    $assert_session->waitForText('Choose an option below');
+    $this->assertSession()->elementExists('css', "[name='group_field_options_wrapper'][value='field_ui:entity_reference:node']");
+    $this->assertSession()->elementExists('css', "[name='group_field_options_wrapper'][value='field_ui:entity_reference:user']");
+
+    $this->fieldUIAddNewFieldJS(NULL, 'test', 'Test', 'entity_reference', FALSE);
 
     // Node should be selected by default.
     $this->assertSession()->fieldValueEquals('settings[target_type]', 'node');
diff --git a/core/modules/field_ui/css/field_ui.icons.theme.css b/core/modules/field_ui/css/field_ui.icons.theme.css
new file mode 100644
index 000000000000..d45a5158d15c
--- /dev/null
+++ b/core/modules/field_ui/css/field_ui.icons.theme.css
@@ -0,0 +1,37 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+/* cspell:ignore cacbd */
+.field-option__icon {
+  position: relative;
+  height: 100%;
+  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='22' height='21' fill='none'%3e%3cpath stroke='%23CACBD2' stroke-width='2' d='M1 4.012h6c-.5-3.5 5-4.5 5 0h5v5c4-.5 6 6 0 6v4.5H1v-16.5'/%3e%3c/svg%3e");
+  background-repeat: no-repeat;
+  background-position: center;
+  background-size: 60%;
+}
+.field-icon-boolean {
+  background-image: url("data:image/svg+xml,%3csvg width='24' height='16' viewBox='0 0 24 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3crect x='1' y='1' width='22' height='14' rx='7' stroke='%2355565B' stroke-width='2'/%3e%3crect x='12' y='4' width='8' height='8' rx='4' fill='%2355565B'/%3e%3c/svg%3e");
+}
+.field-icon-plain_text {
+  background-image: url("data:image/svg+xml,%3csvg width='12' height='15' viewBox='0 0 12 15' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M0 2V0H12V2H7V15H5V2H0Z' fill='%2355565B'/%3e%3c/svg%3e");
+  background-size: 38%;
+}
+.field-icon-date_time {
+  background-image: url("data:image/svg+xml,%3csvg width='23' height='21' fill='none' xmlns='http://www.w3.org/2000/svg'%3e  %3cpath d='M19 14.857h-2V12h-1v4h3v-1.143Z' fill='%2355565B'/%3e  %3cpath d='M17.125 1.75h-4.004V0h-1.75v1.75H6.123V0h-1.75v1.75H.876A.875.875 0 0 0 0 2.623v14.501c0 .483.392.875.875.875h11.248a5.603 5.603 0 0 1-.954-2H2V6.123h14v2.911a5.69 5.69 0 0 1 2 .135V2.624a.875.875 0 0 0-.875-.875Z' fill='%2355565B'/%3e  %3cpath fill-rule='evenodd' clip-rule='evenodd' d='M16.626 18.503a3.877 3.877 0 1 0 0-7.753 3.877 3.877 0 0 0 0 7.753Zm0 1.75a5.627 5.627 0 1 0 0-11.253 5.627 5.627 0 0 0 0 11.253Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
+.field-icon-email {
+  background-image: url("data:image/svg+xml,%3csvg width='17' height='17' fill='none' xmlns='http://www.w3.org/2000/svg'%3e  %3cpath d='M8.954.748C6.498.738 3.977 1.815 2.538 3.84 1.041 5.917.663 8.658 1.158 11.135c.35 1.653 1.316 3.216 2.796 4.08 1.688 1.02 3.747 1.173 5.671.953a9.483 9.483 0 0 0 2.8-.78v-1.83c-1.985.823-4.254 1.224-6.364.633-1.482-.418-2.687-1.696-3.013-3.2-.39-1.533-.295-3.18.198-4.675.528-1.494 1.674-2.787 3.178-3.33a7.097 7.097 0 0 1 4.756-.134c1.364.5 2.477 1.679 2.817 3.091.354 1.293.314 2.721-.186 3.965-.22.525-.72 1.06-1.34.94a.783.783 0 0 1-.647-.692c-.183-.867.042-1.753.028-2.63.047-.886.094-1.772.143-2.659-1.4-.419-2.911-.688-4.355-.35-1.462.4-2.583 1.716-2.827 3.206-.214 1.11-.121 2.308.392 3.317.474.865 1.393 1.48 2.389 1.519 1.098.103 2.269-.356 2.893-1.284.407.928 1.495 1.456 2.483 1.268 1.18-.162 2.131-1.085 2.547-2.167.687-1.605.656-3.453.18-5.112-.568-1.831-1.982-3.398-3.806-4.035A7.905 7.905 0 0 0 8.954.748ZM8.838 6.07c.389-.001.778.03 1.155.112-.11 1.265.025 2.59-.461 3.788-.233.506-.741.899-1.308.891-.594.059-1.253-.29-1.392-.902-.265-.812-.187-1.711.056-2.517.271-.74.963-1.355 1.779-1.363a2.63 2.63 0 0 1 .171-.01Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
+.field-icon-number {
+  background-image: url("data:image/svg+xml,%3csvg width='24' height='14' fill='none' xmlns='http://www.w3.org/2000/svg'%3e  %3cpath fill-rule='evenodd' clip-rule='evenodd' d='M8.654 4.697H6.848v-.292C6.848 2.303 8.6.6 10.76.6c2.16 0 3.913 1.703 3.913 3.805 0 .907-.327 1.74-.872 2.394l-.003.004-3.992 4.625h4.867v1.757H6.848v-1.06l5.527-6.404c.307-.356.493-.815.493-1.316 0-1.132-.944-2.049-2.107-2.049-1.164 0-2.107.917-2.107 2.049v.292ZM23.216.976v1.2l-2.703 3.332C22.23 5.962 23.5 7.572 23.5 9.49c0 2.27-1.78 4.11-3.975 4.11-1.863 0-3.426-1.325-3.857-3.113l-.068-.284 1.652-.428.07.285c.245 1.022 1.14 1.778 2.203 1.778 1.254 0 2.271-1.051 2.271-2.348S20.78 7.14 19.525 7.14a2.2 2.2 0 0 0-1.008.244l-.37.204-.626-1.135 3.016-3.717h-4.419V.976h7.098Z' fill='%2355565B'/%3e  %3cpath d='M4.891.976v12.209H2.935V2.88L0 3.566V1.802L3.544.976h1.347Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
+.field-icon-reference {
+  background-image: url("data:image/svg+xml,%3csvg width='18' height='18' fill='none' xmlns='http://www.w3.org/2000/svg'%3e  %3cpath d='M12 0a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-2v2h4a1 1 0 0 1 1 1v3h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1h-6a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2v-2H5v2h2a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1v-4a1 1 0 0 1 1-1h2V9a1 1 0 0 1 1-1h4V6H6a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1h6ZM6 14H2v2h4v-2Zm10 0h-4v2h4v-2ZM11 2H7v2h4V2Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
+.field-icon-daterange {
+  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='none'%3e  %3cpath d='M15 2h4a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h4V0h2v2h6V0h2v2ZM2 8v10h16V8H2Zm2 2h2v2H4v-2Zm5 0h2v2H9v-2Zm5 0h2v2h-2v-2Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
diff --git a/core/modules/field_ui/css/field_ui.icons.theme.pcss.css b/core/modules/field_ui/css/field_ui.icons.theme.pcss.css
new file mode 100644
index 000000000000..a0acd96ec315
--- /dev/null
+++ b/core/modules/field_ui/css/field_ui.icons.theme.pcss.css
@@ -0,0 +1,38 @@
+/* cspell:ignore cacbd */
+.field-option__icon {
+  position: relative;
+  height: 100%;
+  background-image: url(../../../misc/icons/cacbd2/puzzle_piece_placeholder.svg);
+  background-repeat: no-repeat;
+  background-position: center;
+  background-size: 60%;
+}
+
+.field-icon-boolean {
+  background-image: url(../../../misc/icons/55565b/boolean.svg);
+}
+
+.field-icon-plain_text {
+  background-image: url(../../../misc/icons/55565b/plain_text.svg);
+  background-size: 38%;
+}
+
+.field-icon-date_time {
+  background-image: url(../../../misc/icons/55565b/date_and_time.svg);
+}
+
+.field-icon-email {
+  background-image: url(../../../misc/icons/55565b/email.svg);
+}
+
+.field-icon-number {
+  background-image: url(../../../misc/icons/55565b/number.svg);
+}
+
+.field-icon-reference {
+  background-image: url(../../../misc/icons/55565b/reference.svg);
+}
+
+.field-icon-daterange {
+  background-image: url(../../../misc/icons/55565b/daterange.svg);
+}
diff --git a/core/modules/field_ui/css/field_ui_add_field.module.css b/core/modules/field_ui/css/field_ui_add_field.module.css
new file mode 100644
index 000000000000..4d6f2df53494
--- /dev/null
+++ b/core/modules/field_ui/css/field_ui_add_field.module.css
@@ -0,0 +1,131 @@
+/**
+ * @file field_ui_add_field.module.css
+ */
+
+.field-ui-field-storage-add-form {
+  --option-padding: 0.625rem;
+  --suboption-padding: 1rem;
+
+  max-width: 80rem;
+}
+
+.field-ui-new-storage-wrapper {
+  margin-bottom: 0.75rem;
+}
+
+.field-option__item {
+  display: grid;
+  grid-template-rows: auto 2fr;
+  grid-template-columns: auto 2rem;
+  width: 100%;
+  margin: 0;
+}
+.field-option__item label,
+.field-option__item div {
+  grid-column: 1;
+}
+.field-option__item input {
+  grid-row: 1 / span 2;
+  grid-column: 2;
+  margin-block: auto;
+  margin-left: 7px;
+}
+
+.field-option__thumb {
+  width: 4.5rem;
+  min-height: 2em;
+  margin-block: calc(var(--option-padding) / -2);
+  margin-inline: calc(var(--option-padding) / -2) 0.75rem;
+  background: var(--color-gray-050, #f3f4f9);
+}
+
+.group-field-options-wrapper {
+  margin-block: 1.5em;
+}
+
+.field-option {
+  display: flex;
+  margin-block: 10px;
+  padding: var(--option-padding);
+  border: 1px solid var(--color-gray-100, #dedfe4);
+  border-radius: 4px;
+}
+
+.subfield-option {
+  margin-block: 0.625rem;
+  padding: 1rem;
+  padding-inline-end: 2rem;
+  border: 1px solid var(--color-gray-200, #d3d4d9);
+  border-radius: 4px;
+}
+.subfield-option .item-list ul {
+  margin-inline: 0;
+}
+
+.field-option:has(.field-option-radio:checked),
+.subfield-option:has(.field-option-radio:checked) {
+  border: 3px solid var(--color-blue-600, #003ecc);
+}
+
+.field-option:has(.field-option-radio:checked) {
+  padding: calc(var(--option-padding) - 2px);
+}
+.subfield-option:has(.field-option-radio:checked) {
+  padding: calc(1rem - 2px);
+  padding-inline-end: calc(2rem - 2px);
+}
+
+.field-option:hover,
+.subfield-option:hover {
+  padding: calc(var(--option-padding) - 1px);
+  border: 2px solid #232429;
+}
+
+.subfield-option:hover {
+  padding: calc(1rem - 1px);
+  padding-inline-end: calc(2rem - 1px);
+}
+
+.field-option:has(.field-option-radio.error),
+.subfield-option:has(.field-option-radio.error) {
+  border: 2px solid #dc2323;
+}
+
+.field-option:has(.field-option-radio.error) {
+  padding: calc(var(--option-padding) - 1px);
+}
+.subfield-option:has(.field-option-radio.error) {
+  padding: calc(var(--suboption-padding) - 1px);
+}
+
+
+/**
+ * Add default focus styles to container.
+ */
+.field-option:has(.field-option-radio:focus),
+.subfield-option:has(.field-option-radio:focus) {
+  outline: var(--focus-outline, -webkit-focus-ring-color) !important;
+  box-shadow: var(--focus-box-shadow) !important;
+}
+
+@media (min-width: 45rem) {
+  .add-field-container,
+  .group-field-options {
+    display: grid;
+    grid-template-columns: repeat(2, auto);
+  }
+
+  .subfield-option.subfield-option.subfield-option,
+  .field-option {
+    margin-inline-end: 1.25rem;
+  }
+}
+@media (min-width: 75rem) {
+  .add-field-container {
+    grid-template-columns: repeat(3, auto);
+  }
+}
+
+.subfield-option > .field-option-radio {
+  margin-right: 5px;
+}
diff --git a/core/modules/field_ui/css/field_ui_add_field.theme.css b/core/modules/field_ui/css/field_ui_add_field.theme.css
new file mode 100644
index 000000000000..e10346fa01a4
--- /dev/null
+++ b/core/modules/field_ui/css/field_ui_add_field.theme.css
@@ -0,0 +1,27 @@
+/**
+ * @file field_ui_add_field.theme.css
+ */
+:root {
+  --shadow-z1: 0 2px 4px rgba(0, 0, 0, 0.1);
+  --shadow-z2: 0 4px 10px rgba(0, 0, 0, 0.1);
+}
+
+.add-field-container .field-option:not(:focus),
+.group-field-options .subfield-option:not(:focus) {
+  box-shadow: var(--shadow-z2);
+}
+
+.field-option .form-item__label,
+.subfield-option.subfield-option .form-item__label {
+  font-size: 16px;
+  font-weight: revert;
+}
+
+.subfield-option.subfield-option .form-item__label {
+  padding-bottom: 10px;
+}
+
+.field-option .field-option__description,
+.subfield-option .description .item-list {
+  font-size: 14px;
+}
diff --git a/core/modules/field_ui/field_ui.api.php b/core/modules/field_ui/field_ui.api.php
index 87f55ae6e1a5..9437339a85f6 100644
--- a/core/modules/field_ui/field_ui.api.php
+++ b/core/modules/field_ui/field_ui.api.php
@@ -122,6 +122,32 @@ function hook_field_widget_settings_summary_alter(array &$summary, array $contex
   }
 }
 
+/**
+ * Provides information about field categories used for the UI.
+ *
+ * @return array
+ *   Returns an array with the field category information.
+ */
+function hook_field_type_category_info() {
+  return [
+    'selection_list' => [
+      'label' => t('Selection list'),
+      'description' => t('Field to select from predefined options.'),
+      'weight' => -30,
+    ],
+    'number' => [
+      'label' => t('Number'),
+      'description' => t('Field to store number. I.e. id, price, or quantity.'),
+      'weight' => -20,
+    ],
+    'date_time' => [
+      'label' => t('Date and time'),
+      'description' => t('Field to store date and time values.'),
+      'weight' => -12,
+    ],
+  ];
+}
+
 /**
  * @} End of "addtogroup field_types".
  */
diff --git a/core/modules/field_ui/field_ui.js b/core/modules/field_ui/field_ui.js
index 2f13ea8770e9..6364dbd84513 100644
--- a/core/modules/field_ui/field_ui.js
+++ b/core/modules/field_ui/field_ui.js
@@ -508,4 +508,35 @@
       }
     },
   };
+
+  /**
+   * Allows users to select an element which checks a radio button.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches behavior for selecting field.
+   */
+  Drupal.behaviors.clickToSelect = {
+    attach(context) {
+      $(once('field-click-to-select', '.js-click-to-select', context)).on(
+        'click',
+        (event) => {
+          const clickToSelect = event.target.closest('.js-click-to-select');
+          const input = clickToSelect.querySelector('input');
+          input.checked = true;
+          // Ensure focus is added at the end of the process so wrap in
+          // a timeout.
+          setTimeout(() => {
+            // Remove the disabled attribute added by Drupal ajax so the
+            // element is focusable. This is safe as clicking the element
+            // multiple times causes no problems.
+            input.removeAttribute('disabled');
+            input.focus();
+          }, 0);
+          $(input).trigger('updateOptions');
+        },
+      );
+    },
+  };
 })(jQuery, Drupal, drupalSettings, Drupal.debounce);
diff --git a/core/modules/field_ui/field_ui.libraries.yml b/core/modules/field_ui/field_ui.libraries.yml
index 591cb8d8f240..ffecdb2f9e20 100644
--- a/core/modules/field_ui/field_ui.libraries.yml
+++ b/core/modules/field_ui/field_ui.libraries.yml
@@ -12,3 +12,12 @@ drupal.field_ui:
     - core/once
     - core/drupal.ajax
     - core/drupal.dialog
+
+drupal.field_ui.manage_fields:
+  version: VERSION
+  css:
+    component:
+      css/field_ui_add_field.module.css: {}
+    theme:
+      css/field_ui_add_field.theme.css: {}
+      css/field_ui.icons.theme.css: {}
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index 679aed70e9fd..be9f8c79cbbf 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -63,6 +63,15 @@ function field_ui_theme() {
         'empty' => '',
       ],
     ],
+    // Provide a dedicated template for new storage options as their styling
+    // is quite different from a typical form element, so it works best to not
+    // include default form element classes.
+    'form_element__new_storage_type' => [
+      'base hook' => 'form_element',
+      'variables' => [
+        'variant' => NULL,
+      ],
+    ],
   ];
 }
 
@@ -248,3 +257,31 @@ function field_ui_form_field_ui_field_storage_add_form_alter(array &$form) {
   unset($form['add']['new_storage_type']['#options'][$optgroup]['entity_reference']);
   $form['add']['new_storage_type']['#options'][$optgroup]['entity_reference'] = t('Other…');
 }
+
+/**
+ * Implements hook_theme_suggestions_HOOK_alter().
+ */
+function field_ui_theme_suggestions_form_element_alter(array &$suggestions, array $variables, $hook) {
+  // Create a template suggestions for radio items in the add field form.
+  if ($variables['element']['#type'] !== 'radio' || !isset($variables['element']['#name'])) {
+    return;
+  }
+  $names = [
+    'new_storage_type',
+    'group_field_options_wrapper',
+  ];
+  if (!in_array($variables['element']['#name'], $names, TRUE)) {
+    return;
+  }
+
+  $suggestions[] = $hook . '__new_storage_type';
+}
+
+/**
+ * Implements hook_preprocess_HOOK().
+ */
+function field_ui_preprocess_form_element__new_storage_type(&$variables) {
+  // Add support for a variant string so radios in the add field form can be
+  // programmatically distinguished.
+  $variables['variant'] = $variables['element']['#variant'] ?? NULL;
+}
diff --git a/core/modules/field_ui/src/Form/FieldStorageAddForm.php b/core/modules/field_ui/src/Form/FieldStorageAddForm.php
index a2b19ad877e0..6c7a17217cc5 100644
--- a/core/modules/field_ui/src/Form/FieldStorageAddForm.php
+++ b/core/modules/field_ui/src/Form/FieldStorageAddForm.php
@@ -2,10 +2,13 @@
 
 namespace Drupal\field_ui\Form;
 
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\SortArray;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Field\FieldTypePluginManager;
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
@@ -123,33 +126,15 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
     if (!$form_state->get('bundle')) {
       $form_state->set('bundle', $bundle);
     }
-
     $this->entityTypeId = $form_state->get('entity_type_id');
     $this->bundle = $form_state->get('bundle');
 
-    // Gather valid field types.
-    $field_type_options = [];
-    foreach ($this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getUiDefinitions()) as $category => $field_types) {
-      foreach ($field_types as $name => $field_type) {
-        $field_type_options[$category][$name] = $field_type['label'];
-      }
-    }
-
-    $form['add'] = [
-      '#type' => 'container',
-      '#attributes' => ['class' => ['form--inline', 'clearfix']],
-    ];
-
-    $form['add']['new_storage_type'] = [
-      '#type' => 'select',
-      '#title' => $this->t('Add a new field'),
-      '#options' => $field_type_options,
-      '#empty_option' => $this->t('- Select a field type -'),
-    ];
-
     // Field label and field_name.
     $form['new_storage_wrapper'] = [
       '#type' => 'container',
+      '#attributes' => [
+        'class' => ['field-ui-new-storage-wrapper'],
+      ],
       '#states' => [
         '!visible' => [
           ':input[name="new_storage_type"]' => ['value' => ''],
@@ -159,9 +144,171 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
     $form['new_storage_wrapper']['label'] = [
       '#type' => 'textfield',
       '#title' => $this->t('Label'),
-      '#size' => 15,
+      '#size' => 30,
+    ];
+
+    $field_type_options = $unique_definitions = [];
+    $grouped_definitions = $this->fieldTypePluginManager->getGroupedDefinitions($this->fieldTypePluginManager->getUiDefinitions());
+    // Invoke a hook to get category properties.
+    $category_info = \Drupal::moduleHandler()->invokeAll('field_type_category_info');
+    foreach ($grouped_definitions as $category => $field_types) {
+      foreach ($field_types as $name => $field_type) {
+        $unique_definitions[$category][$name] = ['unique_identifier' => $name] + $field_type;
+        if (isset($category_info[$category])) {
+          // Get the category label from the hook if it is defined in the hook.
+          $field_type_options[$category_info[$category]['label']->render()] = ['unique_identifier' => $name] + $field_type;
+        }
+        else {
+          $field_type_options[$field_type['label']->render()] = ['unique_identifier' => $name] + $field_type;
+        }
+      }
+    }
+    $form['add-label'] = [
+      '#type' => 'label',
+      '#title' => t('Choose a type of field'),
+      '#required' => TRUE,
+    ];
+
+    $form['add'] = [
+      '#type' => 'container',
+      '#attributes' => [
+        'class' => 'add-field-container',
+      ],
+    ];
+    $field_type_options_radios = [];
+    foreach ($field_type_options as $field_option => $val) {
+      // Boolean flag for whether a field option is to be displayed as a group.
+      // When an option should be displayed as a group, its category value
+      // is a string id instead of TranslatableMarkup. The string id maps
+      // to the values returned by the field_type_category_info hook.
+      $display_as_group = is_string($val['category']) && $val['category'] !== FieldTypePluginManager::DEFAULT_CATEGORY;
+      $option_info = $display_as_group ? $category_info[$val['category']] : $val;
+      $cleaned_class_name = Html::getClass($val['unique_identifier']);
+      $field_type_options_radios[$field_option] = [
+        '#type' => 'container',
+        '#attributes' => [
+          'class' => ['field-option', 'js-click-to-select'],
+          'checked' => $this->getRequest()->request->get('new_storage_type') !== NULL && $this->getRequest()->request->get('new_storage_type') == ($display_as_group ? $val['category'] : $val['unique_identifier']),
+        ],
+        '#weight' => $option_info['weight'] ?? 1,
+        'thumb' => [
+          '#type' => 'container',
+          '#attributes' => [
+            'class' => ['field-option__thumb'],
+          ],
+          'icon' => [
+            '#type' => 'container',
+            '#attributes' => [
+              'class' => ['field-option__icon', $display_as_group ?
+                "field-icon-$val[category]" : "field-icon-$cleaned_class_name",
+              ],
+            ],
+          ],
+        ],
+        // Store some data we later need.
+        '#data' => [
+          '#group_display' => $display_as_group,
+        ],
+        'radio' => [
+          '#type' => 'radio',
+          '#title' => $field_option,
+          '#parents' => ['new_storage_type'],
+          '#title_display' => 'before',
+          '#description_display' => 'before',
+          // If it is a category, set return value as the category label,
+          // otherwise, set it as the field type id.
+          '#return_value' => $display_as_group ? $val['category'] : $val['unique_identifier'],
+          '#attributes' => [
+            'class' => ['field-option-radio'],
+          ],
+          '#ajax' => [
+            'callback' => [$this, 'showFieldsCallback'],
+            'event' => 'updateOptions',
+            'wrapper' => 'group-field-options-wrapper',
+            'progress' => 'none',
+            'disable-refocus' => TRUE,
+          ],
+          '#description' => [
+            '#type' => 'container',
+            '#attributes' => [
+              'class' => ['field-option__description'],
+            ],
+            '#markup' => $option_info['description'] ?? NULL,
+          ],
+          '#variant' => 'field-option',
+        ],
+      ];
+    }
+    uasort($field_type_options_radios, [SortArray::class, 'sortByWeightProperty']);
+    $form['add']['new_storage_type'] = $field_type_options_radios;
+    $form['group_submit'] = [
+      '#type' => 'submit',
+      '#value' => $this->t('Change field group'),
+      '#limit_validation_errors' => [],
+      '#attributes' => [
+        'class' => ['js-hide'],
+      ],
+      '#submit' => [[static::class, 'showFieldsHandler']],
+    ];
+    $form['group_field_options_wrapper'] = [
+      '#prefix' => '<div id="group-field-options-wrapper" class="group-field-options-wrapper">',
+      '#suffix' => '</div>',
     ];
 
+    // Set the selected field to the form state by checking
+    // the checked attribute.
+    $selected_field_type = NULL;
+    foreach ($field_type_options_radios as $field_type_options_radio) {
+      if ($field_type_options_radio['#attributes']['checked']) {
+        $selected_field_label = $field_type_options_radio['radio']['#title'];
+        $selected_field_type = $field_type_options_radio['radio']['#return_value'];
+        $form_state->setValue('selected_field_type', $selected_field_type);
+        break;
+      }
+    }
+    if (isset($selected_field_label)) {
+      $group_display = $field_type_options_radios[$selected_field_label]['#data']['#group_display'];
+      if ($group_display) {
+        $form['group_field_options_wrapper']['label'] = [
+          '#type' => 'label',
+          '#title' => t('Choose an option below'),
+          '#required' => TRUE,
+        ];
+        $form['group_field_options_wrapper']['fields'] = [
+          '#type' => 'container',
+          '#attributes' => [
+            'class' => ['group-field-options'],
+          ],
+        ];
+
+        foreach ($unique_definitions[$selected_field_type] as $option_key => $option) {
+          $radio_element = [
+            '#type' => 'radio',
+            '#title' => $option['label']->render(),
+            '#description' => [
+              '#theme' => 'item_list',
+              '#items' => $unique_definitions[$selected_field_type][$option_key]['description'],
+            ],
+            '#id' => $option['unique_identifier'],
+            '#parents' => ['group_field_options_wrapper'],
+            '#attributes' => [
+              'class' => ['field-option-radio'],
+              'data-once' => 'field-click-to-select',
+            ],
+            '#wrapper_attributes' => [
+              'class' => ['js-click-to-select', 'subfield-option'],
+            ],
+            '#variant' => 'field-suboption',
+          ];
+          $radio_element['#return_value'] = $option['unique_identifier'];
+          if ((string) $option['unique_identifier'] === 'entity_reference') {
+            $radio_element['#title'] = 'Other';
+            $radio_element['#weight'] = 10;
+          }
+          $form['group_field_options_wrapper']['fields'][$option['unique_identifier']] = $radio_element;
+        }
+      }
+    }
     $field_prefix = $this->config('field_ui.settings')->get('field_prefix');
     $form['new_storage_wrapper']['field_name'] = [
       '#type' => 'machine_name',
@@ -177,7 +324,6 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
       ],
       '#required' => FALSE,
     ];
-
     // Place the 'translatable' property as an explicit value so that contrib
     // modules can form_alter() the value for newly created fields. By default
     // we create field storage as translatable so it will be possible to enable
@@ -194,8 +340,11 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
       '#button_type' => 'primary',
     ];
 
-    $form['#attached']['library'][] = 'field_ui/drupal.field_ui';
-
+    $form['#attached']['library'] = [
+      'field_ui/drupal.field_ui',
+      'field_ui/drupal.field_ui.manage_fields',
+      'core/drupal.ajax',
+    ];
     return $form;
   }
 
@@ -203,15 +352,11 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
    * {@inheritdoc}
    */
   public function validateForm(array &$form, FormStateInterface $form_state) {
-    // Missing field type.
     if (!$form_state->getValue('new_storage_type')) {
       $form_state->setErrorByName('new_storage_type', $this->t('You need to select a field type.'));
     }
-    // Both field type and existing field option selected. This is prevented in
-    // the UI with JavaScript but we also need a proper server-side validation.
-    elseif ($form_state->getValue('new_storage_type') && $form_state->getValue('existing_storage_name')) {
-      $form_state->setErrorByName('new_storage_type', $this->t('Adding a new field and re-using an existing field at the same time is not allowed.'));
-      return;
+    elseif (isset($form['group_field_options_wrapper']['fields']) && !$form_state->getValue('group_field_options_wrapper')) {
+      $form_state->setErrorByName('group_field_options_wrapper', $this->t('You need to select a field type.'));
     }
 
     $this->validateAddNew($form, $form_state);
@@ -256,17 +401,16 @@ protected function validateAddNew(array $form, FormStateInterface $form_state) {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $values = $form_state->getValues();
     $entity_type = $this->entityTypeManager->getDefinition($this->entityTypeId);
+
+    $field_storage_type = $values['group_field_options_wrapper'] ?? $values['new_storage_type'];
+    $field_name = $values['field_name'];
     $field_values = [
       'entity_type' => $this->entityTypeId,
       'bundle' => $this->bundle,
     ];
-    $field_name = $values['field_name'];
-    $field_storage_type = $values['new_storage_type'];
-    $default_options = [];
-
     // Check if we're dealing with a preconfigured field.
     if (strpos($field_storage_type, 'field_ui:') === 0) {
-      list(, $field_type, $preset_key) = explode(':', $field_storage_type, 3);
+      [, $field_type, $preset_key] = explode(':', $field_storage_type, 3);
       $default_options = $this->getNewFieldDefaults($field_type, $preset_key);
     }
     else {
@@ -303,7 +447,9 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
       $this->configureEntityViewDisplay($field_name, $default_options['entity_view_display'] ?? []);
     }
     catch (\Exception $e) {
-      $this->messenger()->addError($this->t('There was a problem creating field %label: @message', ['%label' => $values['label'], '@message' => $e->getMessage()]));
+      $this->messenger()->addError($this->t(
+        'There was a problem creating field %label: @message',
+        ['%label' => $values['label'], '@message' => $e->getMessage()]));
       return;
     }
 
@@ -382,7 +528,9 @@ protected function getNewFieldDefaults(string $field_name, string $preset_key):
     // Preconfigured options only apply to the default display modes.
     foreach (['entity_form_display', 'entity_view_display'] as $key) {
       if (isset($field_options[$key])) {
-        $default_options[$key] = ['default' => array_intersect_key($field_options[$key], ['type' => '', 'settings' => []])];
+        $default_options[$key] = [
+          'default' => array_intersect_key($field_options[$key], ['type' => '', 'settings' => []]),
+        ];
       }
       else {
         $default_options[$key] = ['default' => []];
@@ -413,4 +561,18 @@ public function fieldNameExists($value, $element, FormStateInterface $form_state
     return isset($field_storage_definitions[$field_name]);
   }
 
+  /**
+   * Callback for displaying fields after a group has been selected.
+   */
+  public function showFieldsCallback($form, FormStateInterface &$form_state) {
+    return $form['group_field_options_wrapper'];
+  }
+
+  /**
+   * Submit handler for displaying fields after a group is selected.
+   */
+  public static function showFieldsHandler($form, FormStateInterface &$form_state) {
+    $form_state->setRebuild();
+  }
+
 }
diff --git a/core/modules/field_ui/templates/form-element--new-storage-type.html.twig b/core/modules/field_ui/templates/form-element--new-storage-type.html.twig
new file mode 100644
index 000000000000..be1821fa2682
--- /dev/null
+++ b/core/modules/field_ui/templates/form-element--new-storage-type.html.twig
@@ -0,0 +1,46 @@
+{#
+/**
+ * @file
+ * Default theme implementation for a storage type option form element.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing element.
+ * - errors: (optional) Any errors for this form element, may not be set.
+ * - label: A rendered label element.
+ * - description: (optional) A list of description properties containing:
+ *    - content: A description of the form element, may not be set.
+ *    - attributes: (optional) A list of HTML attributes to apply to the
+ *      description content wrapper. Will only be set when description is set.
+ * - variant: specifies option type. Typically 'field-option' or 'field-suboption'.
+ *
+ * @see template_preprocess_form_element()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+    errors ? 'form-item--error',
+    variant ? variant ~ '__item'
+  ]
+%}
+<div{{ attributes.addClass(classes) }}>
+  {% if variant == 'field-option' %}
+    {{ label }}
+    <div{{ description.attributes }}>
+      {{ description.content }}
+    </div>
+  {% endif %}
+  {{ children }}
+  {% if variant == 'field-suboption' %}
+    {{ label }}
+    <div{{ description.attributes.addClass(description_classes) }}>
+      {{ description.content }}
+    </div>
+  {% endif %}
+  {% if errors %}
+    <div class="form-item--error-message">
+      {{ errors }}
+    </div>
+  {% endif %}
+</div>
diff --git a/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php b/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php
index 5235a3e457ca..c1fc660c4003 100644
--- a/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php
+++ b/core/modules/field_ui/tests/src/Functional/ManageFieldsFunctionalTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\field_ui\Functional;
 
+use Behat\Mink\Exception\ElementNotFoundException;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;
 use Drupal\Core\Entity\Entity\EntityFormMode;
 use Drupal\Core\Entity\Entity\EntityViewDisplay;
@@ -692,8 +693,8 @@ public function testLockedField() {
   public function testHiddenFields() {
     // Check that the field type is not available in the 'add new field' row.
     $this->drupalGet('admin/structure/types/manage/' . $this->contentType . '/fields/add-field');
-    $this->assertSession()->optionNotExists('edit-new-storage-type', 'hidden_test_field');
-    $this->assertSession()->optionExists('edit-new-storage-type', 'shape');
+    $this->assertSession()->elementNotExists('css', "[name='new_storage_type'][value='hidden_test_field']");
+    $this->assertSession()->elementExists('css', "[name='new_storage_type'][value='shape']");
 
     // Create a field storage and a field programmatically.
     $field_name = 'hidden_test_field';
@@ -731,10 +732,19 @@ public function testHiddenFields() {
     $this->drupalGet('admin/structure/types/manage/page/fields/add-field');
     foreach ($field_types as $field_type => $definition) {
       if (empty($definition['no_ui'])) {
-        $this->assertSession()->optionExists('edit-new-storage-type', $field_type);
+        try {
+          $this->assertSession()
+            ->elementExists('css', "[name='new_storage_type'][value='$field_type']");
+        }
+        catch (ElementNotFoundException) {
+          if ($this->getFieldFromGroup($field_type)) {
+            $this->assertSession()
+              ->elementExists('css', "[name='group_field_options_wrapper'][value='$field_type']");
+          }
+        }
       }
       else {
-        $this->assertSession()->optionNotExists('edit-new-storage-type', $field_type);
+        $this->assertSession()->elementNotExists('css', "[name='new_storage_type'][value='$field_type']");
       }
     }
   }
@@ -745,17 +755,11 @@ public function testHiddenFields() {
   public function testDuplicateFieldName() {
     // field_tags already exists, so we're expecting an error when trying to
     // create a new field with the same name.
-    $edit = [
-      'field_name' => 'tags',
-      'label' => $this->randomMachineName(),
-      'new_storage_type' => 'entity_reference',
-    ];
-    $url = 'admin/structure/types/manage/' . $this->contentType . '/fields/add-field';
-    $this->drupalGet($url);
-    $this->submitForm($edit, 'Save and continue');
+    $url = 'admin/structure/types/manage/' . $this->contentType;
+    $this->fieldUIAddNewField($url, 'tags', $this->randomMachineName(), 'entity_reference', [], [], FALSE);
 
     $this->assertSession()->pageTextContains('The machine-readable name is already in use. It must be unique.');
-    $this->assertSession()->addressEquals($url);
+    $this->assertSession()->addressEquals($url . '/fields/add-field');
   }
 
   /**
@@ -855,8 +859,8 @@ public function testPreconfiguredFields() {
 
     // Check that the preconfigured field option exist alongside the regular
     // field type option.
-    $this->assertSession()->optionExists('edit-new-storage-type', 'field_ui:test_field_with_preconfigured_options:custom_options');
-    $this->assertSession()->optionExists('edit-new-storage-type', 'test_field_with_preconfigured_options');
+    $this->assertSession()->elementExists('css', "[name='new_storage_type'][value='field_ui:test_field_with_preconfigured_options:custom_options']");
+    $this->assertSession()->elementExists('css', "[name='new_storage_type'][value='test_field_with_preconfigured_options']");
 
     // Add a field with every possible preconfigured value.
     $this->fieldUIAddNewField(NULL, 'test_custom_options', 'Test label', 'field_ui:test_field_with_preconfigured_options:custom_options');
diff --git a/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php b/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php
index e32c1b418857..e61606d55245 100644
--- a/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php
+++ b/core/modules/field_ui/tests/src/FunctionalJavascript/ManageFieldsTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\field_ui\FunctionalJavascript;
 
+use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
 use Drupal\Tests\field_ui\Traits\FieldUiJSTestTrait;
 
@@ -166,4 +167,79 @@ public function testFieldDelete() {
     $assert_session->waitForText('The field Body has been deleted from the Article content type.');
   }
 
+  /**
+   * Tests field add.
+   */
+  public function testAddField() {
+    $page = $this->getSession()->getPage();
+    $assert_session = $this->assertSession();
+
+    $this->drupalGet('admin/structure/types/manage/article/fields/add-field');
+    $field_name = 'test_field_1';
+    $page->fillField('label', $field_name);
+
+    // Test validation.
+    $page->pressButton('Save and continue');
+    $assert_session->pageTextContains('You need to select a field type.');
+    $assert_session->elementExists('css', '[name="new_storage_type"].error');
+    $assert_session->pageTextNotContains('Choose an option below');
+
+    $this->assertNotEmpty($number_field = $page->find('xpath', '//*[text() = "Number"]'));
+    $number_field->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="number"]')->isSelected());
+    $assert_session->pageTextContains('Choose an option below');
+    $page->pressButton('Save and continue');
+    $assert_session->pageTextContains('You need to select a field type.');
+    $assert_session->elementNotExists('css', '[name="new_storage_type"].error');
+    $assert_session->elementExists('css', '[name="group_field_options_wrapper"].error');
+
+    // Try adding a field using a grouped field type.
+    $this->assertNotEmpty($email_field = $page->find('xpath', '//*[text() = "Email"]'));
+    $email_field->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="email"]')->isSelected());
+    $assert_session->pageTextNotContains('Choose an option below');
+
+    $this->assertNotEmpty($text = $page->find('xpath', '//*[text() = "Plain text"]'));
+    $text->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="plain_text"]')->isSelected());
+    $assert_session->pageTextContains('Choose an option below');
+
+    $this->assertNotEmpty($text_plain = $page->find('xpath', '//*[text() = "Text (plain)"]'));
+    $text_plain->click();
+    $this->assertTrue($assert_session->elementExists('css', '[name="group_field_options_wrapper"][value="string"]')->isSelected());
+
+    $page->pressButton('Save and continue');
+    $assert_session->pageTextContains('Your settings have been saved.');
+    $this->assertNotNull($field_storage = FieldStorageConfig::loadByName('node', "field_$field_name"));
+    $this->assertEquals('string', $field_storage->getType());
+
+    // Try adding a field using a non-grouped field type.
+    $this->drupalGet('admin/structure/types/manage/article/fields/add-field');
+    $field_name = 'test_field_2';
+    $page->fillField('label', $field_name);
+
+    $this->assertNotEmpty($number_field = $page->find('xpath', '//*[text() = "Number"]'));
+    $number_field->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="number"]')->isSelected());
+    $assert_session->pageTextContains('Choose an option below');
+    $this->assertNotEmpty($number_integer = $page->find('xpath', '//*[text() = "Number (integer)"]'));
+    $number_integer->click();
+    $this->assertTrue($assert_session->elementExists('css', '[name="group_field_options_wrapper"][value="integer"]')->isSelected());
+
+    $this->assertNotEmpty($test_field = $page->find('xpath', '//*[text() = "Test field"]'));
+    $test_field->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $this->assertTrue($assert_session->elementExists('css', '[name="new_storage_type"][value="test_field"]')->isSelected());
+    $assert_session->pageTextNotContains('Choose an option below');
+
+    $page->pressButton('Save and continue');
+    $assert_session->pageTextContains('Your settings have been saved.');
+    $this->assertNotNull($field_storage = FieldStorageConfig::loadByName('node', "field_$field_name"));
+    $this->assertEquals('test_field', $field_storage->getType());
+  }
+
 }
diff --git a/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php b/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php
index c544421f29d8..f1c57fc03b37 100644
--- a/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php
+++ b/core/modules/field_ui/tests/src/Traits/FieldUiJSTestTrait.php
@@ -19,8 +19,13 @@ trait FieldUiJSTestTrait {
    * @param string $field_type
    *   (optional) The field type of the new field storage. Defaults to
    *   'test_field'.
+   * @param bool $save_settings
+   *   (optional) Parameter for conditional execution of second and third step
+   *   (Saving the storage settings and field settings). Defaults to 'TRUE'.
+   *
+   * @throws \Behat\Mink\Exception\ElementNotFoundException
    */
-  public function fieldUIAddNewFieldJS(?string $bundle_path, string $field_name, ?string $label = NULL, string $field_type = 'test_field'): void {
+  public function fieldUIAddNewFieldJS(?string $bundle_path, string $field_name, ?string $label = NULL, string $field_type = 'test_field', bool $save_settings = TRUE): void {
     $label = $label ?: $field_name;
 
     // Allow the caller to set a NULL path in case they navigated to the right
@@ -36,13 +41,19 @@ public function fieldUIAddNewFieldJS(?string $bundle_path, string $field_name, ?
     $page = $session->getPage();
     $assert_session = $this->assertSession();
 
-    $field_new_storage_type = $page->findField('new_storage_type');
-    $field_new_storage_type->setValue($field_type);
-
-    $field_label = $page->findField('label');
+    if ($assert_session->waitForElementVisible('css', "[name='new_storage_type'][value='$field_type']")) {
+      $page = $this->getSession()->getPage();
+      $field_card = $page->find('css', "[name='new_storage_type'][value='$field_type']");
+    }
+    else {
+      $field_card = $this->getFieldFromGroupJS($field_type);
+    }
+    $field_card?->click();
+    $field_label = $page->findField('edit-label');
     $this->assertTrue($field_label->isVisible());
+    $field_label = $page->find('css', 'input[data-drupal-selector="edit-label"]');
     $field_label->setValue($label);
-    $machine_name = $assert_session->waitForElementVisible('css', '[name="label"] + * .machine-name-value');
+    $machine_name = $assert_session->waitForElementVisible('css', '[data-drupal-selector="edit-label"] + * .machine-name-value');
     $this->assertNotEmpty($machine_name);
     $page->findButton('Edit')->press();
 
@@ -51,24 +62,25 @@ public function fieldUIAddNewFieldJS(?string $bundle_path, string $field_name, ?
     $field_field_name->setValue($field_name);
 
     $page->findButton('Save and continue')->click();
+    $assert_session->waitForText("These settings apply to the $label field everywhere it is used.");
+    if ($save_settings) {
+      $breadcrumb_link = $page->findLink($label);
 
-    $assert_session->pageTextContains("These settings apply to the $label field everywhere it is used.");
-    $breadcrumb_link = $page->findLink($label);
+      // Test breadcrumb.
+      $this->assertTrue($breadcrumb_link->isVisible());
 
-    // Test breadcrumb.
-    $this->assertTrue($breadcrumb_link->isVisible());
+      // Second step: 'Storage settings' form.
+      $page->findButton('Save field settings')->click();
+      $assert_session->pageTextContains("Updated field $label field settings.");
 
-    // Second step: 'Storage settings' form.
-    $page->findButton('Save field settings')->click();
-    $assert_session->pageTextContains("Updated field $label field settings.");
+      // Third step: 'Field settings' form.
+      $page->findButton('Save settings')->click();
+      $assert_session->pageTextContains("Saved $label configuration.");
 
-    // Third step: 'Field settings' form.
-    $page->findButton('Save settings')->click();
-    $assert_session->pageTextContains("Saved $label configuration.");
-
-    // Check that the field appears in the overview form.
-    $row = $page->find('css', '#field-' . $field_name);
-    $this->assertNotEmpty($row, 'Field was created and appears in the overview page.');
+      // Check that the field appears in the overview form.
+      $row = $page->find('css', '#field-' . $field_name);
+      $this->assertNotEmpty($row, 'Field was created and appears in the overview page.');
+    }
   }
 
   /**
@@ -116,4 +128,32 @@ public function fieldUIAddExistingFieldJS(string $bundle_path, string $existing_
     $this->assertSession()->elementExists('xpath', $xpath);
   }
 
+  /**
+   * Helper function that returns the field card element if it is in a group.
+   *
+   * @param string $field_type
+   *   The name of the field type.
+   *
+   * @return \Behat\Mink\Element\NodeElement|false|mixed|null
+   *   Field card element within a group.
+   */
+  public function getFieldFromGroupJS($field_type) {
+    $group_elements = $this->getSession()->getPage()->findAll('css', '.field-option-radio');
+    $groups = [];
+    foreach ($group_elements as $group_element) {
+      $groups[] = $group_element->getAttribute('value');
+    }
+    $field_card = NULL;
+    foreach ($groups as $group) {
+      $group_field_card = $this->getSession()->getPage()->find('css', "[name='new_storage_type'][value='$group']");
+      $group_field_card->click();
+      $this->assertSession()->assertWaitOnAjaxRequest();
+      $field_card = $this->getSession()->getPage()->find('css', "[name='group_field_options_wrapper'][value='$field_type']");
+      if ($field_card) {
+        break;
+      }
+    }
+    return $field_card;
+  }
+
 }
diff --git a/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php b/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php
index f77481fe4b7f..3fca460a0ced 100644
--- a/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php
+++ b/core/modules/field_ui/tests/src/Traits/FieldUiTestTrait.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\field_ui\Traits;
 
+use Behat\Mink\Exception\ElementNotFoundException;
+
 /**
  * Provides common functionality for the Field UI test classes.
  */
@@ -25,17 +27,16 @@ trait FieldUiTestTrait {
    * @param array $field_edit
    *   (optional) $edit parameter for submitForm() on the third step ('Field
    *   settings' form).
+   * @param bool $save_settings
+   *   (optional) Parameter for conditional execution of second and third step
+   *   (Saving the storage settings and field settings). Defaults to 'TRUE'.
    */
-  public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $field_type = 'test_field', array $storage_edit = [], array $field_edit = []) {
+  public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $field_type = 'test_field', array $storage_edit = [], array $field_edit = [], bool $save_settings = TRUE) {
     // Generate a label containing only letters and numbers to prevent random
     // test failure.
     // See https://www.drupal.org/project/drupal/issues/3030902
     $label = $label ?: $this->randomMachineName();
-    $initial_edit = [
-      'new_storage_type' => $field_type,
-      'label' => $label,
-      'field_name' => $field_name,
-    ];
+    $initial_edit = [];
 
     // Allow the caller to set a NULL path in case they navigated to the right
     // page before calling this method.
@@ -47,24 +48,56 @@ public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $fi
     if ($bundle_path !== NULL) {
       $this->drupalGet($bundle_path);
     }
-    $this->submitForm($initial_edit, 'Save and continue');
-    $this->assertSession()->pageTextContains("These settings apply to the $label field everywhere it is used.");
-    // Test Breadcrumbs.
-    $this->assertSession()->linkExists($label, 0, 'Field label is correct in the breadcrumb of the storage settings page.');
-
-    // Second step: 'Storage settings' form.
-    $this->submitForm($storage_edit, 'Save field settings');
-    $this->assertSession()->pageTextContains("Updated field $label field settings.");
-
-    // Third step: 'Field settings' form.
-    $this->submitForm($field_edit, 'Save settings');
-    $this->assertSession()->pageTextContains("Saved $label configuration.");
 
-    // Check that the field appears in the overview form.
-    $xpath = $this->assertSession()->buildXPathQuery("//table[@id=\"field-overview\"]//tr/td[1 and text() = :label]", [
-      ':label' => $label,
-    ]);
-    $this->assertSession()->elementExists('xpath', $xpath);
+    try {
+      // First check if the passed in field type is not part of a group.
+      $this->assertSession()->elementExists('css', "[name='new_storage_type'][value='$field_type']");
+      // If the element exists then we can add it to our object.
+      $initial_edit = [
+        'new_storage_type' => $field_type,
+        'label' => $label,
+        'field_name' => $field_name,
+      ];
+    }
+    // If the element could not be found then it is probably in a group.
+    catch (ElementNotFoundException) {
+      // Call the helper function to confirm it is in a group.
+      $field_group = $this->getFieldFromGroup($field_type);
+      if ($field_group) {
+        // Pass in the group name as the new storage type.
+        $selected_group = [
+          'new_storage_type' => $field_group,
+        ];
+        $this->submitForm($selected_group, 'Change field group');
+        $initial_edit = [
+          'group_field_options_wrapper' => $field_type,
+          'label' => $label,
+          'field_name' => $field_name,
+        ];
+      }
+    }
+    $this->submitForm($initial_edit, 'Save and continue');
+    if ($save_settings) {
+      $this->assertSession()->pageTextContains("These settings apply to the $label field everywhere it is used.");
+      // Test Breadcrumbs.
+      $this->getSession()->getPage()->findLink($label);
+
+      // Second step: 'Storage settings' form.
+      $this->submitForm($storage_edit, 'Save field settings');
+      $this->assertSession()
+        ->pageTextContains("Updated field $label field settings.");
+
+      // Third step: 'Field settings' form.
+      $this->submitForm($field_edit, 'Save settings');
+      $this->assertSession()->pageTextContains("Saved $label configuration.");
+
+      // Check that the field appears in the overview form.
+      $xpath = $this->assertSession()
+        ->buildXPathQuery("//table[@id=\"field-overview\"]//tr/td[1 and text() = :label]", [
+          ':label' => $label,
+        ]);
+      $this->assertSession()->elementExists('xpath', $xpath);
+    }
   }
 
   /**
@@ -141,4 +174,35 @@ public function fieldUIDeleteField($bundle_path, $field_name, $label, $bundle_la
     $this->assertSession()->elementNotExists('xpath', $xpath);
   }
 
+  /**
+   * Helper function that returns the name of the group that a field is in.
+   *
+   * @param string $field_type
+   *   The name of the field type.
+   *
+   * @returns string
+   *  Group name
+   */
+  public function getFieldFromGroup($field_type) {
+    $group_elements = $this->getSession()->getPage()->findAll('css', '.field-option-radio');
+    $groups = [];
+    foreach ($group_elements as $group_element) {
+      $groups[] = $group_element->getAttribute('value');
+    }
+    foreach ($groups as $group) {
+      $test = [
+        'new_storage_type' => $group,
+      ];
+      $this->submitForm($test, 'Change field group');
+      try {
+        $this->assertSession()->elementExists('css', "[name='group_field_options_wrapper'][value='$field_type']");
+        return $group;
+      }
+      catch (ElementNotFoundException) {
+        continue;
+      }
+    }
+    return NULL;
+  }
+
 }
diff --git a/core/modules/file/css/file.icon.theme.css b/core/modules/file/css/file.icon.theme.css
new file mode 100644
index 000000000000..0853ae51d116
--- /dev/null
+++ b/core/modules/file/css/file.icon.theme.css
@@ -0,0 +1,9 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+.field-icon-file_upload {
+  background-image: url("data:image/svg+xml,%3csvg width='18' height='20' fill='none' xmlns='http://www.w3.org/2000/svg'%3e  %3cpath d='M12 2H2v16h14V6h-4V2ZM0 .992C0 .444.447 0 .999 0H13l5 5v13.992A1 1 0 0 1 17.007 20H.993A1 1 0 0 1 0 19.008V.992Z' fill='%2355565B'/%3e  %3cpath d='M10.25 13v3h-2.5v-3H5l4-5 4 5h-2.75Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
diff --git a/core/modules/file/css/file.icon.theme.pcss.css b/core/modules/file/css/file.icon.theme.pcss.css
new file mode 100644
index 000000000000..1133d5843ed6
--- /dev/null
+++ b/core/modules/file/css/file.icon.theme.pcss.css
@@ -0,0 +1,3 @@
+.field-icon-file_upload {
+  background-image: url(../../../misc/icons/55565b/file_upload.svg);
+}
diff --git a/core/modules/file/file.libraries.yml b/core/modules/file/file.libraries.yml
index 81c9324ea536..50904d18f5b0 100644
--- a/core/modules/file/file.libraries.yml
+++ b/core/modules/file/file.libraries.yml
@@ -7,3 +7,11 @@ drupal.file:
     - core/once
     - core/drupal
     - core/drupalSettings
+
+drupal.file-icon:
+  version: VERSION
+  css:
+    theme:
+      css/file.icon.theme.css: {}
+  dependencies:
+    - field_ui/drupal.field_ui.manage_fields
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index a6332db90c77..1fa00345e651 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -1552,3 +1552,23 @@ function file_field_find_file_reference_column(FieldDefinitionInterface $field)
   }
   return FALSE;
 }
+
+/**
+ * Implements hook_field_type_category_info().
+ */
+function file_field_type_category_info() {
+  return [
+    'file_upload' => [
+      'label' => t('File upload'),
+      'description' => t('Field to upload any type of files.'),
+      'weight' => -15,
+    ],
+  ];
+}
+
+/**
+ * Implements hook_preprocess_form_element__new_storage_type().
+ */
+function file_preprocess_form_element__new_storage_type(&$variables) {
+  $variables['#attached']['library'][] = 'file/drupal.file-icon';
+}
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
index 3f59cfe80e5c..252bdf8bae42 100644
--- a/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileItem.php
@@ -21,8 +21,11 @@
  * @FieldType(
  *   id = "file",
  *   label = @Translation("File"),
- *   description = @Translation("This field stores the ID of a file as an integer value."),
- *   category = @Translation("Reference"),
+ *   description = {
+ *     @Translation("For uploading files"),
+ *     @Translation("Can be configured with options such as allowed file extensions and maximum upload size"),
+ *   },
+ *   category = "file_upload",
  *   default_widget = "file_generic",
  *   default_formatter = "file_default",
  *   list_class = "\Drupal\file\Plugin\Field\FieldType\FileFieldItemList",
diff --git a/core/modules/file/tests/src/Functional/FileFieldDisplayTest.php b/core/modules/file/tests/src/Functional/FileFieldDisplayTest.php
index 3e342dae4ce9..7b12a90dc8e0 100644
--- a/core/modules/file/tests/src/Functional/FileFieldDisplayTest.php
+++ b/core/modules/file/tests/src/Functional/FileFieldDisplayTest.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\file\Entity\File;
 use Drupal\node\Entity\Node;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 
 /**
  * Tests the display of file fields in node and views.
@@ -13,6 +14,8 @@
  */
 class FileFieldDisplayTest extends FileFieldTestBase {
 
+  use FieldUiTestTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -165,20 +168,10 @@ public function testDescToggle() {
     ];
     $this->drupalGet('admin/structure/types/add');
     $this->submitForm($edit, 'Save and manage fields');
-    $edit = [
-      'new_storage_type' => $field_type,
-      'field_name' => $field_name,
-      'label' => $this->randomString(),
-    ];
-    $this->drupalGet('/admin/structure/types/manage/' . $type_name . '/fields/add-field');
-    $this->submitForm($edit, 'Save and continue');
-    $this->submitForm([], 'Save field settings');
-    // Ensure the description field is selected on the field instance settings
-    // form. That's what this test is all about.
-    $edit = [
+    $field_edit = [
       'settings[description_field]' => TRUE,
     ];
-    $this->submitForm($edit, 'Save settings');
+    $this->fieldUIAddNewField('/admin/structure/types/manage/' . $type_name, $field_name, $this->randomString(), $field_type, [], $field_edit);
     // Add a node of our new type and upload a file to it.
     $file = current($this->drupalGetTestFiles('text'));
     $title = $this->randomString();
diff --git a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
index de73760de9d0..2d29c34ba95b 100644
--- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
+++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
@@ -21,8 +21,12 @@
  * @FieldType(
  *   id = "image",
  *   label = @Translation("Image"),
- *   description = @Translation("This field stores the ID of an image file as an integer value."),
- *   category = @Translation("Reference"),
+ *   description = {
+ *     @Translation("For uploading images"),
+ *     @Translation("Allows a user to upload an image with configurable extensions, image resolutions, upload size"),
+ *     @Translation("Can be configured with options such as allowed file extensions, maximum upload size and image resolution minimums/maximums"),
+ *   },
+ *   category = "file_upload",
  *   default_widget = "image_image",
  *   default_formatter = "image",
  *   column_groups = {
diff --git a/core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldType/DummyAjaxItem.php b/core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldType/DummyAjaxItem.php
index 50b77bd1c924..587661234564 100644
--- a/core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldType/DummyAjaxItem.php
+++ b/core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldType/DummyAjaxItem.php
@@ -14,7 +14,6 @@
  *   id = "image_module_test_dummy_ajax",
  *   label = @Translation("Dummy AJAX"),
  *   description = @Translation("A field containing an AJAX handler."),
- *   category = @Translation("Field"),
  *   default_widget = "image_module_test_dummy_ajax_widget",
  *   default_formatter = "image_module_test_dummy_ajax_formatter"
  * )
diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php
index ee41c012ec4e..296ab0c4a7c9 100644
--- a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php
+++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderTest.php
@@ -6,6 +6,7 @@
 use Drupal\layout_builder\Section;
 use Drupal\node\Entity\Node;
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 use Drupal\views\Entity\View;
 
 /**
@@ -15,6 +16,8 @@
  */
 class LayoutBuilderTest extends BrowserTestBase {
 
+  use FieldUiTestTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -444,15 +447,7 @@ public function testLayoutBuilderUi() {
     $assert_session->linkNotExists('Layout');
 
     // Add a new field.
-    $edit = [
-      'new_storage_type' => 'string',
-      'label' => 'My text field',
-      'field_name' => 'my_text',
-    ];
-    $this->drupalGet("{$field_ui_prefix}/fields/add-field");
-    $this->submitForm($edit, 'Save and continue');
-    $page->pressButton('Save field settings');
-    $page->pressButton('Save settings');
+    $this->fieldUIAddNewField($field_ui_prefix, 'my_text', 'My text field', 'string');
     $this->drupalGet("$field_ui_prefix/display/default/layout");
     $assert_session->pageTextContains('My text field');
     $assert_session->elementExists('css', '.field--name-field-my-text');
diff --git a/core/modules/link/css/link.icon.theme.css b/core/modules/link/css/link.icon.theme.css
new file mode 100644
index 000000000000..df6253915827
--- /dev/null
+++ b/core/modules/link/css/link.icon.theme.css
@@ -0,0 +1,9 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+.field-icon-link {
+  background-image: url("data:image/svg+xml,%3csvg width='21' height='20' fill='none' xmlns='http://www.w3.org/2000/svg'%3e  %3cpath d='M8.197 10.906a4.529 4.529 0 0 0 6.832.49l2.718-2.719a4.53 4.53 0 0 0-6.406-6.405L9.783 3.82' stroke='%2355565B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3e  %3cpath d='M11.821 9.094a4.53 4.53 0 0 0-6.831-.49l-2.718 2.719a4.53 4.53 0 0 0 6.405 6.405l1.55-1.549' stroke='%2355565B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3e%3c/svg%3e");
+}
diff --git a/core/modules/link/css/link.icon.theme.pcss.css b/core/modules/link/css/link.icon.theme.pcss.css
new file mode 100644
index 000000000000..733eedbcf527
--- /dev/null
+++ b/core/modules/link/css/link.icon.theme.pcss.css
@@ -0,0 +1,3 @@
+.field-icon-link {
+  background-image: url(../../../misc/icons/55565b/link.svg);
+}
diff --git a/core/modules/link/link.libraries.yml b/core/modules/link/link.libraries.yml
new file mode 100644
index 000000000000..d192cb32112c
--- /dev/null
+++ b/core/modules/link/link.libraries.yml
@@ -0,0 +1,7 @@
+drupal.link-icon:
+  version: VERSION
+  css:
+    theme:
+      css/link.icon.theme.css: {}
+  dependencies:
+    - field_ui/drupal.field_ui.manage_fields
diff --git a/core/modules/link/link.module b/core/modules/link/link.module
index fafa734f7d12..590c01cc91c4 100644
--- a/core/modules/link/link.module
+++ b/core/modules/link/link.module
@@ -65,3 +65,10 @@ function link_theme() {
 function template_preprocess_link_formatter_link_separate(&$variables) {
   $variables['link'] = Link::fromTextAndUrl($variables['url_title'], $variables['url'])->toString();
 }
+
+/**
+ * Implements hook_preprocess_form_element__new_storage_type().
+ */
+function link_preprocess_form_element__new_storage_type(&$variables) {
+  $variables['#attached']['library'][] = 'link/drupal.link-icon';
+}
diff --git a/core/modules/media/css/media.icon.theme.css b/core/modules/media/css/media.icon.theme.css
new file mode 100644
index 000000000000..6bc614d5c8a0
--- /dev/null
+++ b/core/modules/media/css/media.icon.theme.css
@@ -0,0 +1,10 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+/* cspell:ignore uientity referencemedia */
+.field-icon-field-uientity-referencemedia {
+  background-image: url("data:image/svg+xml,%3csvg width='20' height='20' fill='none' xmlns='http://www.w3.org/2000/svg'%3e  %3cg clip-path='url(%23a)' fill-rule='evenodd' clip-rule='evenodd' fill='%2355565B'%3e    %3cpath d='m19.187 4.864-8.936 2v.16h-.001v8.485a2.401 2.401 0 0 0-1.91-.195c-1.22.376-1.93 1.583-1.587 2.696.343 1.113 1.61 1.71 2.83 1.334 1.084-.334 1.765-1.324 1.664-2.32h.003V9.801l6.93-1.551v6.136a2.401 2.401 0 0 0-1.909-.196c-1.22.376-1.93 1.583-1.587 2.696.343 1.113 1.61 1.71 2.83 1.335 1.083-.334 1.765-1.324 1.663-2.32h.003V8.026l.007-.002v-3.16Z'/%3e    %3cpath d='M13.504.744H.387V12.16h13.117V.744Zm-1.166 1.014H1.553v9.387h.104l2.35-2.281 2.056 1.997L9.25 5.913l2.131 3.309.395-.554.562.788V1.758ZM6.22 4.508a1.943 1.943 0 1 1-3.886 0 1.943 1.943 0 0 1 3.886 0Z'/%3e  %3c/g%3e  %3cdefs%3e    %3cclipPath id='a'%3e      %3cpath fill='%23fff' d='M0 0h20v20H0z'/%3e    %3c/clipPath%3e  %3c/defs%3e%3c/svg%3e");
+}
diff --git a/core/modules/media/css/media.icon.theme.pcss.css b/core/modules/media/css/media.icon.theme.pcss.css
new file mode 100644
index 000000000000..b987efbdfa8f
--- /dev/null
+++ b/core/modules/media/css/media.icon.theme.pcss.css
@@ -0,0 +1,4 @@
+/* cspell:ignore uientity referencemedia */
+.field-icon-field-uientity-referencemedia {
+  background-image: url(../../../misc/icons/55565b/media.svg);
+}
diff --git a/core/modules/media/media.libraries.yml b/core/modules/media/media.libraries.yml
index f8febc4dfcb4..41fc310fbb9a 100644
--- a/core/modules/media/media.libraries.yml
+++ b/core/modules/media/media.libraries.yml
@@ -40,3 +40,11 @@ media_embed_ckeditor_theme:
     js/media_embed_ckeditor.theme.js: {}
   dependencies:
     - core/drupal
+
+drupal.media-icon:
+  version: VERSION
+  css:
+    theme:
+      css/media.icon.theme.css: {}
+  dependencies:
+    - field_ui/drupal.field_ui.manage_fields
diff --git a/core/modules/media/media.module b/core/modules/media/media.module
index 83884fe45fe1..e93a7f528039 100644
--- a/core/modules/media/media.module
+++ b/core/modules/media/media.module
@@ -200,27 +200,21 @@ function media_form_field_ui_field_storage_add_form_alter(&$form, FormStateInter
       '@help_url' => Url::fromRoute('help.page', ['name' => 'media'])->toString(),
     ]);
   }
-  $form['add']['description_wrapper'] = [
-    '#type' => 'container',
-  ];
   $field_types = [
-    'file',
-    'image',
+    'file_upload',
     'field_ui:entity_reference:media',
   ];
-  foreach ($field_types as $field_name) {
-    $form['add']['description_wrapper']["description_{$field_name}"] = [
+  if (in_array($form_state->getValue('selected_field_type'), $field_types)) {
+    $form['group_field_options_wrapper']['description_wrapper'] = [
       '#type' => 'item',
       '#markup' => $description_text,
-      '#states' => [
-        'visible' => [
-          ':input[name="new_storage_type"]' => ['value' => $field_name],
-        ],
-      ],
     ];
   }
-  $form['add']['new_storage_type']['#weight'] = 0;
-  $form['add']['description_wrapper']['#weight'] = 1;
+
+  // Remove Media from the Reference group since it already exists as a field.
+  if ($form_state->getValue('selected_field_type') === 'reference') {
+    unset($form['group_field_options_wrapper']['fields']['field_ui:entity_reference:media']);
+  }
 }
 
 /**
@@ -539,3 +533,10 @@ function media_views_query_substitutions(ViewExecutable $view) {
     '***ADMINISTER_MEDIA***' => (int) $account->hasPermission('administer media'),
   ];
 }
+
+/**
+ * Implements hook_preprocess_form_element__new_storage_type().
+ */
+function media_preprocess_form_element__new_storage_type(&$variables) {
+  $variables['#attached']['library'][] = 'media/drupal.media-icon';
+}
diff --git a/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php b/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php
index 362467b3807c..2fedf2d1f399 100644
--- a/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php
+++ b/core/modules/media/tests/src/Functional/MediaUiFunctionalTest.php
@@ -7,6 +7,7 @@
 use Drupal\Core\Url;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 
 /**
  * Ensures that media UI works correctly.
@@ -15,6 +16,8 @@
  */
 class MediaUiFunctionalTest extends MediaFunctionalTestBase {
 
+  use FieldUiTestTrait;
+
   /**
    * Modules to enable.
    *
@@ -23,6 +26,8 @@ class MediaUiFunctionalTest extends MediaFunctionalTestBase {
   protected static $modules = [
     'block',
     'media_test_source',
+    'media',
+    'media_library',
   ];
 
   /**
@@ -189,15 +194,10 @@ public function testMediaWithMultipleMediaTypes() {
    * Tests that media in ER fields use the Rendered Entity formatter by default.
    */
   public function testRenderedEntityReferencedMedia() {
-    $page = $this->getSession()->getPage();
     $assert_session = $this->assertSession();
 
     $this->drupalCreateContentType(['type' => 'page', 'name' => 'Page']);
-    $this->drupalGet('/admin/structure/types/manage/page/fields/add-field');
-    $page->selectFieldOption('new_storage_type', 'field_ui:entity_reference:media');
-    $page->fillField('label', 'Foo field');
-    $page->fillField('field_name', 'foo_field');
-    $page->pressButton('Save and continue');
+    $this->fieldUIAddNewField('/admin/structure/types/manage/page', 'foo_field', 'Foo field', 'field_ui:entity_reference:media', [], [], FALSE);
     $this->drupalGet('/admin/structure/types/manage/page/display');
     $assert_session->fieldValueEquals('fields[field_foo_field][type]', 'entity_reference_entity_view');
   }
@@ -341,13 +341,7 @@ public function testMediaReferenceWidget($cardinality, array $media_type_create_
     // settings form.
     // Using submitForm() to avoid dealing with JavaScript on the previous
     // page in the field creation.
-    $edit = [
-      'new_storage_type' => 'field_ui:entity_reference:media',
-      'label' => "Media (cardinality $cardinality)",
-      'field_name' => 'media_reference',
-    ];
-    $this->drupalGet("admin/structure/types/manage/{$content_type->id()}/fields/add-field");
-    $this->submitForm($edit, 'Save and continue');
+    $this->fieldUIAddNewField("admin/structure/types/manage/{$content_type->id()}", 'media_reference', "Media (cardinality $cardinality)", 'field_ui:entity_reference:media', [], [], FALSE);
     $edit = [];
     foreach ($media_types as $type) {
       $edit["settings[handler_settings][target_bundles][$type]"] = TRUE;
diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaReferenceFieldHelpTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaReferenceFieldHelpTest.php
index 74d4237bfdc6..99e5466a70fc 100644
--- a/core/modules/media/tests/src/FunctionalJavascript/MediaReferenceFieldHelpTest.php
+++ b/core/modules/media/tests/src/FunctionalJavascript/MediaReferenceFieldHelpTest.php
@@ -2,8 +2,6 @@
 
 namespace Drupal\Tests\media\FunctionalJavascript;
 
-use Drupal\Component\Utility\Html;
-
 /**
  * Tests related to media reference fields.
  *
@@ -16,6 +14,14 @@ class MediaReferenceFieldHelpTest extends MediaJavascriptTestBase {
    */
   protected $defaultTheme = 'stark';
 
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'media',
+    'media_library',
+  ];
+
   /**
    * Tests our custom help texts when creating a field.
    *
@@ -30,31 +36,27 @@ public function testFieldCreationHelpText() {
     ]);
     $this->drupalGet("/admin/structure/types/manage/{$type->id()}/fields/add-field");
 
-    $field_types = [
-      'file',
-      'image',
+    $field_groups = [
+      'file_upload',
       'field_ui:entity_reference:media',
     ];
-    $description_ids = array_map(function ($item) {
-      return '#edit-description-' . Html::cleanCssIdentifier($item);
-    }, $field_types);
+
+    $help_text = 'Use Media reference fields for most files, images, audio, videos, and remote media. Use File or Image reference fields when creating your own media types, or for legacy files and images created before enabling the Media module.';
 
     // Choose a boolean field, none of the description containers should be
     // visible.
-    $assert_session->optionExists('edit-new-storage-type', 'boolean');
-    $page->selectFieldOption('edit-new-storage-type', 'boolean');
-    foreach ($description_ids as $description_id) {
-      $this->assertFalse($assert_session->elementExists('css', $description_id)->isVisible());
-    }
-    // Select each of the file, image, and media fields and verify their
+    $assert_session->elementExists('css', "[name='new_storage_type'][value='boolean']");
+    $page->find('css', "[name='new_storage_type'][value='boolean']")->click();
+    $assert_session->assertWaitOnAjaxRequest();
+    $assert_session->pageTextNotContains($help_text);
+
+    // Select each of the Reference, File upload field groups and verify their
     // descriptions are now visible and match the expected text.
-    $help_text = 'Use Media reference fields for most files, images, audio, videos, and remote media. Use File or Image reference fields when creating your own media types, or for legacy files and images created before enabling the Media module.';
-    foreach ($field_types as $field_name) {
-      $assert_session->optionExists('edit-new-storage-type', $field_name);
-      $page->selectFieldOption('edit-new-storage-type', $field_name);
-      $field_description_element = $assert_session->elementExists('css', '#edit-description-' . Html::cleanCssIdentifier($field_name));
-      $this->assertTrue($field_description_element->isVisible());
-      $this->assertSame($help_text, $field_description_element->getText());
+    foreach ($field_groups as $field_group) {
+      $assert_session->elementExists('css', "[name='new_storage_type'][value='$field_group']");
+      $page->find('css', "[name='new_storage_type'][value='$field_group']")->click();
+      $assert_session->assertWaitOnAjaxRequest();
+      $assert_session->pageTextContains($help_text);
     }
   }
 
diff --git a/core/modules/media_library/media_library.module b/core/modules/media_library/media_library.module
index b858450094ac..1bb81ece7f39 100644
--- a/core/modules/media_library/media_library.module
+++ b/core/modules/media_library/media_library.module
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;
 use Drupal\Core\Entity\Entity\EntityViewDisplay;
+use Drupal\Core\Field\FieldTypePluginManager;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
@@ -346,6 +347,9 @@ function media_library_field_ui_preconfigured_options_alter(array &$options, $fi
   // Set the default field widget for media to be the Media library.
   if (!empty($options['media'])) {
     $options['media']['entity_form_display']['type'] = 'media_library_widget';
+    $options['media']['description'] = t('Field to reference media. Allows uploading and selecting from uploaded media.');
+    $options['media']['weight'] = -25;
+    $options['media']['category'] = FieldTypePluginManager::DEFAULT_CATEGORY;
   }
 }
 
diff --git a/core/modules/media_library/tests/modules/media_library_test/src/Plugin/Field/FieldType/EntityReferenceItemSubclass.php b/core/modules/media_library/tests/modules/media_library_test/src/Plugin/Field/FieldType/EntityReferenceItemSubclass.php
index e065d3aed4fe..9fd286d8af18 100644
--- a/core/modules/media_library/tests/modules/media_library_test/src/Plugin/Field/FieldType/EntityReferenceItemSubclass.php
+++ b/core/modules/media_library/tests/modules/media_library_test/src/Plugin/Field/FieldType/EntityReferenceItemSubclass.php
@@ -11,10 +11,10 @@
  *   id = "entity_reference_subclass",
  *   label = @Translation("Entity reference subclass"),
  *   description = @Translation("An entity field containing an entity reference."),
- *   category = @Translation("Reference"),
+ *   category = "reference",
  *   default_widget = "entity_reference_autocomplete",
  *   default_formatter = "entity_reference_label",
- *   list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList"
+ *   list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
  * )
  */
 class EntityReferenceItemSubclass extends EntityReferenceItem {
diff --git a/core/modules/options/css/options.icon.theme.css b/core/modules/options/css/options.icon.theme.css
new file mode 100644
index 000000000000..5c66700be357
--- /dev/null
+++ b/core/modules/options/css/options.icon.theme.css
@@ -0,0 +1,9 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+.field-icon-selection_list {
+  background-image: url("data:image/svg+xml,%3csvg width='18' height='15' viewBox='0 0 18 15' fill='none' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5 6.5H18V8.5H5V6.5Z' fill='%2355565B'/%3e%3cpath d='M3 6H0V9H3V6Z' fill='%2355565B'/%3e%3cpath d='M18 12.5H5V14.5H18V12.5Z' fill='%2355565B'/%3e%3cpath d='M3 12H0V15H3V12Z' fill='%2355565B'/%3e%3cpath d='M18 0.5H5V2.5H18V0.5Z' fill='%2355565B'/%3e%3cpath d='M3 0H0V3H3V0Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
diff --git a/core/modules/options/css/options.icon.theme.pcss.css b/core/modules/options/css/options.icon.theme.pcss.css
new file mode 100644
index 000000000000..cc748ad631ad
--- /dev/null
+++ b/core/modules/options/css/options.icon.theme.pcss.css
@@ -0,0 +1,3 @@
+.field-icon-selection_list {
+  background-image: url(../../../misc/icons/55565b/selection_list.svg);
+}
diff --git a/core/modules/options/options.libraries.yml b/core/modules/options/options.libraries.yml
new file mode 100644
index 000000000000..a95f9154b348
--- /dev/null
+++ b/core/modules/options/options.libraries.yml
@@ -0,0 +1,7 @@
+drupal.options-icon:
+  version: VERSION
+  css:
+    theme:
+      css/options.icon.theme.css: {}
+  dependencies:
+    - field_ui/drupal.field_ui.manage_fields
diff --git a/core/modules/options/options.module b/core/modules/options/options.module
index 608f4bcc6586..244a766acd61 100644
--- a/core/modules/options/options.module
+++ b/core/modules/options/options.module
@@ -139,6 +139,19 @@ function _options_values_in_use($entity_type, $field_name, $values) {
   return FALSE;
 }
 
+/**
+ * Implements hook_field_type_category_info().
+ */
+function options_field_type_category_info() {
+  return [
+    'selection_list' => [
+      'label' => t('Selection list'),
+      'description' => t('Field to select from predefined options.'),
+      'weight' => -15,
+    ],
+  ];
+}
+
 /**
  * Implements hook_form_FORM_ID_alter().
  *
@@ -155,3 +168,10 @@ function options_form_field_storage_config_edit_form_alter(&$form, FormStateInte
   $form['#attached']['library'][] = 'field_ui/drupal.field_ui';
   $table['#attributes']['class'][] = 'allowed-values-table';
 }
+
+/**
+ * Implements hook_preprocess_form_element__new_storage_type().
+ */
+function options_preprocess_form_element__new_storage_type(&$variables) {
+  $variables['#attached']['library'][] = 'options/drupal.options-icon';
+}
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php
index 10006d6f0aec..26388d3aba6f 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php
@@ -15,8 +15,11 @@
  * @FieldType(
  *   id = "list_float",
  *   label = @Translation("List (float)"),
- *   description = @Translation("This field stores float values from a list of allowed 'value => label' pairs, i.e. 'Fraction': 0 => 0, .25 => 1/4, .75 => 3/4, 1 => 1."),
- *   category = @Translation("Number"),
+ *   description = {
+ *     @Translation("Values stored are floating-point numbers"),
+ *     @Translation("For example, 'Fraction': 0 => 0, .25 => 1/4, .75 => 3/4, 1 => 1"),
+ *   },
+ *   category = "selection_list",
  *   default_widget = "options_select",
  *   default_formatter = "list_default",
  * )
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php
index ce9bef192770..cf4674880e7e 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListIntegerItem.php
@@ -15,8 +15,11 @@
  * @FieldType(
  *   id = "list_integer",
  *   label = @Translation("List (integer)"),
- *   description = @Translation("This field stores integer values from a list of allowed 'value => label' pairs, i.e. 'Lifetime in days': 1 => 1 day, 7 => 1 week, 31 => 1 month."),
- *   category = @Translation("Number"),
+ *   description = {
+ *     @Translation("Values stored are numbers without decimals"),
+ *     @Translation("For example, 'Lifetime in days': 1 => 1 day, 7 => 1 week, 31 => 1 month"),
+ *   },
+ *   category = "selection_list",
  *   default_widget = "options_select",
  *   default_formatter = "list_default",
  * )
diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php
index 0fdba2b0c324..ca4f5d22229d 100644
--- a/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php
+++ b/core/modules/options/src/Plugin/Field/FieldType/ListStringItem.php
@@ -15,8 +15,11 @@
  * @FieldType(
  *   id = "list_string",
  *   label = @Translation("List (text)"),
- *   description = @Translation("This field stores text values from a list of allowed 'value => label' pairs, i.e. 'US States': IL => Illinois, IA => Iowa, IN => Indiana."),
- *   category = @Translation("Text"),
+ *   description = {
+ *     @Translation("Values stored are text values"),
+ *     @Translation("For example, 'US States': IL => Illinois, IA => Iowa, IN => Indiana"),
+ *   },
+ *   category = "selection_list",
  *   default_widget = "options_select",
  *   default_formatter = "list_default",
  * )
diff --git a/core/modules/responsive_image/tests/src/FunctionalJavascript/ResponsiveImageFieldUiTest.php b/core/modules/responsive_image/tests/src/FunctionalJavascript/ResponsiveImageFieldUiTest.php
index 5a2d5cf72396..4409ec274712 100644
--- a/core/modules/responsive_image/tests/src/FunctionalJavascript/ResponsiveImageFieldUiTest.php
+++ b/core/modules/responsive_image/tests/src/FunctionalJavascript/ResponsiveImageFieldUiTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
 use Drupal\responsive_image\Entity\ResponsiveImageStyle;
+use Drupal\Tests\field_ui\Traits\FieldUiJSTestTrait;
 
 /**
  * Tests the responsive image field UI.
@@ -12,6 +13,8 @@
  */
 class ResponsiveImageFieldUiTest extends WebDriverTestBase {
 
+  use FieldUiJSTestTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -69,28 +72,11 @@ protected function setUp(): void {
    */
   public function testResponsiveImageFormatterUi() {
     $manage = 'admin/structure/types/manage/' . $this->type;
-    $add_field = $manage . '/fields/add-field';
     $manage_display = $manage . '/display';
+    /** @var \Drupal\FunctionalJavascriptTests\JSWebAssert $assert_session */
     $assert_session = $this->assertSession();
 
-    // Create a field, and a node with some data for the field.
-    // Create the field.
-    $this->drupalGet($add_field);
-
-    $page = $this->getSession()->getPage();
-    $storage_type = $page->findField('edit-new-storage-type');
-    $storage_type->setValue('image');
-
-    // Set the label.
-    $label = $page->findField('edit-label');
-    $label->setValue('Image');
-
-    // Wait for the machine name.
-    $assert_session->waitForElementVisible('css', '[name="label"] + * .machine-name-value');
-
-    // Save the current page.
-    $save_button = $page->findButton('Save and continue');
-    $save_button->click();
+    $this->fieldUIAddNewFieldJS('admin/structure/types/manage/' . $this->type, 'image', 'Image', 'image');
 
     // Display the "Manage display" page.
     $this->drupalGet($manage_display);
diff --git a/core/modules/search/tests/src/Functional/SearchPageCacheTagsTest.php b/core/modules/search/tests/src/Functional/SearchPageCacheTagsTest.php
index 940807e30ca3..23a7de2d787c 100644
--- a/core/modules/search/tests/src/Functional/SearchPageCacheTagsTest.php
+++ b/core/modules/search/tests/src/Functional/SearchPageCacheTagsTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Cache\Cache;
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 use Drupal\Tests\system\Functional\Cache\AssertPageCacheContextsAndTagsTrait;
 
 /**
@@ -14,6 +15,7 @@
 class SearchPageCacheTagsTest extends BrowserTestBase {
 
   use AssertPageCacheContextsAndTagsTrait;
+  use FieldUiTestTrait;
 
   /**
    * {@inheritdoc}
@@ -155,17 +157,7 @@ public function testSearchTagsBubbling() {
     ]);
     $this->drupalLogin($admin_user);
 
-    // First step: 'Add new field' on the 'Manage fields' page.
-    $this->drupalGet($bundle_path . '/fields/add-field');
-    $this->submitForm([
-      'label' => 'Test label',
-      'field_name' => 'test__ref',
-      'new_storage_type' => 'entity_reference',
-    ], 'Save and continue');
-
-    // Second step: 'Field settings' form.
-    $this->submitForm([], 'Save field settings');
-
+    $this->fieldUIAddNewField($bundle_path, 'test__ref', 'Test label', 'entity_reference', [], [], FALSE);
     // Create a new node of our newly created node type and fill in the entity
     // reference field.
     $edit = [
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/AutoIncrementingTestItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/AutoIncrementingTestItem.php
index d4b53defe939..f0d6beacefa6 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/AutoIncrementingTestItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/AutoIncrementingTestItem.php
@@ -11,7 +11,6 @@
  *   id = "auto_incrementing_test",
  *   label = @Translation("Auto incrementing test field item"),
  *   description = @Translation("An entity field designed to test the field method invocation order."),
- *   category = @Translation("Number"),
  *   no_ui = TRUE,
  * )
  */
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ComputedTestCacheableStringItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ComputedTestCacheableStringItem.php
index 06da5ea3ab56..2ee17c143a8d 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ComputedTestCacheableStringItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/ComputedTestCacheableStringItem.php
@@ -14,7 +14,6 @@
  *   id = "computed_test_cacheable_string_item",
  *   label = @Translation("Test Text (plain string with cacheability)"),
  *   description = @Translation("A test field containing a plain string value and cacheability metadata."),
- *   category = @Translation("Text"),
  *   no_ui = TRUE,
  *   default_widget = "string_textfield",
  *   default_formatter = "string"
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php
index 1549978b4239..43e43c1a8220 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/FieldTestItem.php
@@ -16,7 +16,6 @@
  *   id = "field_test",
  *   label = @Translation("Test field item"),
  *   description = @Translation("A field containing a plain string value."),
- *   category = @Translation("Field"),
  * )
  */
 class FieldTestItem extends FieldItemBase {
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php
index 2bc8154cf9e9..647598d98810 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/InternalPropertyTestFieldItem.php
@@ -15,7 +15,6 @@
  *   id = "internal_property_test",
  *   label = @Translation("Internal Property (test)"),
  *   description = @Translation("A field containing one string, from which two strings are computed (one internal, one not)."),
- *   category = @Translation("Test"),
  *   default_widget = "string_textfield",
  *   default_formatter = "string"
  * )
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedItem.php
index d460cb2026aa..f9606918f5c8 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedItem.php
@@ -14,7 +14,6 @@
  *   id = "serialized_item_test",
  *   label = @Translation("Test serialized field item"),
  *   description = @Translation("A field containing a serialized string value."),
- *   category = @Translation("Field"),
  * )
  */
 class SerializedItem extends FieldItemBase {
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedPropertyItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedPropertyItem.php
index c9b067fe421f..9f5a674c5a09 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedPropertyItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SerializedPropertyItem.php
@@ -14,7 +14,6 @@
  *   id = "serialized_property_item_test",
  *   label = @Translation("Test serialized property field item"),
  *   description = @Translation("A field containing a string representing serialized data."),
- *   category = @Translation("Field"),
  *   serialized_property_names = {
  *     "value"
  *   }
diff --git a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SingleInternalPropertyTestFieldItem.php b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SingleInternalPropertyTestFieldItem.php
index 30add62df1d0..8d3d25e1f4bd 100644
--- a/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SingleInternalPropertyTestFieldItem.php
+++ b/core/modules/system/tests/modules/entity_test/src/Plugin/Field/FieldType/SingleInternalPropertyTestFieldItem.php
@@ -21,7 +21,6 @@
  *   id = "single_internal_property_test",
  *   label = @Translation("Single Internal Property (test)"),
  *   description = @Translation("A field containing one string, from which one internal string is computed."),
- *   category = @Translation("Test"),
  *   default_widget = "string_textfield",
  *   default_formatter = "string"
  * )
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php b/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php
index 9062c5cf81e8..7d001f8df336 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityReferenceFieldCreationTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\Tests\BrowserTestBase;
 use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 
 /**
  * Tests creating entity reference fields in the UI.
@@ -13,6 +14,7 @@
 class EntityReferenceFieldCreationTest extends BrowserTestBase {
 
   use EntityReferenceTestTrait;
+  use FieldUiTestTrait;
 
   /**
    * {@inheritdoc}
@@ -33,13 +35,7 @@ public function testAddReferenceFieldTargetingEntityTypeWithoutId() {
 
     // Entity types without an ID key should not be presented as options when
     // creating an entity reference field in the UI.
-    $this->drupalGet("/admin/structure/types/manage/$node_type/fields/add-field");
-    $edit = [
-      'new_storage_type' => 'entity_reference',
-      'label' => 'Test Field',
-      'field_name' => 'test_reference_field',
-    ];
-    $this->submitForm($edit, 'Save and continue');
+    $this->fieldUIAddNewField("/admin/structure/types/manage/$node_type", 'test_reference_field', 'Test Field', 'entity_reference', [], [], FALSE);
     $this->assertSession()->optionNotExists('settings[target_type]', 'entity_test_no_id');
 
     // Trying to do it programmatically should raise an exception.
diff --git a/core/modules/system/tests/src/Functional/System/DateTimeTest.php b/core/modules/system/tests/src/Functional/System/DateTimeTest.php
index 5ba373205334..c96df16d4ffc 100644
--- a/core/modules/system/tests/src/Functional/System/DateTimeTest.php
+++ b/core/modules/system/tests/src/Functional/System/DateTimeTest.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Datetime\Entity\DateFormat;
 use Drupal\Core\Url;
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 
 /**
  * Test date formatting and time zone handling, including daylight saving time.
@@ -13,6 +14,8 @@
  */
 class DateTimeTest extends BrowserTestBase {
 
+  use FieldUiTestTrait;
+
   /**
    * Modules to enable.
    *
@@ -211,26 +214,12 @@ public function testEnteringDateTimeViaSelectors() {
     $this->drupalGet('admin/structure/types/manage/page_with_date');
     $this->assertSession()->statusCodeEquals(200);
 
-    $this->drupalGet('admin/structure/types/manage/page_with_date/fields/add-field');
-    $edit = [
-      'new_storage_type' => 'datetime',
-      'label' => 'dt',
-      'field_name' => 'dt',
-    ];
-    $this->drupalGet('admin/structure/types/manage/page_with_date/fields/add-field');
-    $this->submitForm($edit, 'Save and continue');
-    // Check that the new datetime field was created, and process is now set
-    // to continue for configuration.
-    $this->assertSession()->pageTextContains('These settings apply to the');
-
-    $this->drupalGet('admin/structure/types/manage/page_with_date/fields/node.page_with_date.field_dt/storage');
-    $edit = [
+    $storage_edit = [
       'settings[datetime_type]' => 'datetime',
       'cardinality' => 'number',
       'cardinality_number' => '1',
     ];
-    $this->drupalGet('admin/structure/types/manage/page_with_date/fields/node.page_with_date.field_dt/storage');
-    $this->submitForm($edit, 'Save field settings');
+    $this->fieldUIAddNewField('admin/structure/types/manage/page_with_date', 'dt', 'dt', 'datetime', $storage_edit);
 
     $this->drupalGet('admin/structure/types/manage/page_with_date/fields');
     $this->assertSession()->pageTextContains('field_dt');
diff --git a/core/modules/telephone/css/telephone.icon.theme.css b/core/modules/telephone/css/telephone.icon.theme.css
new file mode 100644
index 000000000000..23a41035c68e
--- /dev/null
+++ b/core/modules/telephone/css/telephone.icon.theme.css
@@ -0,0 +1,9 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+.field-icon-telephone {
+  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' fill='none'%3e  %3cpath d='M4.548 7.943a14.422 14.422 0 0 0 7.55 7.397c.552-.4.863-1.095 1.235-1.66.278-.275.834-.81 1.39-.55 1.381.65 2.04.877 3.61 1.1.155.021.579.164.834.19.383.037.833.36.833 1.136v3.07c0 .55-.451 1.282-1.033 1.323-.486.034-.882.051-1.19.051C7.918 20 0 11.862 0 2.198c0-.303.017-.696.052-1.176C.092.446.833 0 1.389 0h3.104c.785 0 1.11.445 1.149.824.025.252.17.672.191.825.225 1.552.455 2.205 1.111 3.572.265.55-.277 1.099-.555 1.374-.6.387-1.476.733-1.84 1.348Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
diff --git a/core/modules/telephone/css/telephone.icon.theme.pcss.css b/core/modules/telephone/css/telephone.icon.theme.pcss.css
new file mode 100644
index 000000000000..d92cd6c1a476
--- /dev/null
+++ b/core/modules/telephone/css/telephone.icon.theme.pcss.css
@@ -0,0 +1,3 @@
+.field-icon-telephone {
+  background-image: url(../../../misc/icons/55565b/telephone.svg);
+}
diff --git a/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php b/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
index 3cec358056e5..d0f0c17f05bf 100644
--- a/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
+++ b/core/modules/telephone/src/Plugin/Field/FieldType/TelephoneItem.php
@@ -14,8 +14,7 @@
  * @FieldType(
  *   id = "telephone",
  *   label = @Translation("Telephone number"),
- *   description = @Translation("This field stores a telephone number in the database."),
- *   category = @Translation("Number"),
+ *   description = @Translation("This field stores a telephone number."),
  *   default_widget = "telephone_default",
  *   default_formatter = "basic_string"
  * )
diff --git a/core/modules/telephone/telephone.libraries.yml b/core/modules/telephone/telephone.libraries.yml
new file mode 100644
index 000000000000..21ebb918f1b5
--- /dev/null
+++ b/core/modules/telephone/telephone.libraries.yml
@@ -0,0 +1,7 @@
+drupal.telephone-icon:
+  version: VERSION
+  css:
+    theme:
+      css/telephone.icon.theme.css: {}
+  dependencies:
+    - field_ui/drupal.field_ui.manage_fields
diff --git a/core/modules/telephone/telephone.module b/core/modules/telephone/telephone.module
index 65a9b1c713c3..c89d8bd83fd9 100644
--- a/core/modules/telephone/telephone.module
+++ b/core/modules/telephone/telephone.module
@@ -34,3 +34,10 @@ function telephone_help($route_name, RouteMatchInterface $route_match) {
 function telephone_field_formatter_info_alter(&$info) {
   $info['string']['field_types'][] = 'telephone';
 }
+
+/**
+ * Implements hook_preprocess_form_element__new_storage_type().
+ */
+function telephone_preprocess_form_element__new_storage_type(&$variables) {
+  $variables['#attached']['library'][] = 'telephone/drupal.telephone-icon';
+}
diff --git a/core/modules/text/css/text.icon.theme.css b/core/modules/text/css/text.icon.theme.css
new file mode 100644
index 000000000000..2b064e4acc18
--- /dev/null
+++ b/core/modules/text/css/text.icon.theme.css
@@ -0,0 +1,9 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/3084859
+ * @preserve
+ */
+.field-icon-formatted_text {
+  background-image: url("data:image/svg+xml,%3csvg width='24' height='14' fill='none' xmlns='http://www.w3.org/2000/svg'%3e  %3cpath fill-rule='evenodd' clip-rule='evenodd' d='M24 2H14V0h10v2ZM24 8H11V6h13v2ZM24 14H9v-2h15v2Z' fill='%2355565B'/%3e  %3cpath d='M0 13c.612 0 1.272.123 1.5 0 .365-.198.399-.352.581-1.034L5.003 1C4.007.993 3.21.989 2.5 1.5c-.71.504-1.054 1.487-1.523 2.475H0L.977 0H12v4l-1.25-.025c-.033-1.362-.398-2.292-1.095-2.79C9.35.968 8.5 1 8 1L5.17 11.435l-.167.726a1.738 1.738 0 0 0-.049.419c0 .36 0 .325.215.42.215.089 1.147-.048 1.831 0v1H0v-1Z' fill='%2355565B'/%3e%3c/svg%3e");
+}
diff --git a/core/modules/text/css/text.icon.theme.pcss.css b/core/modules/text/css/text.icon.theme.pcss.css
new file mode 100644
index 000000000000..bc609928828c
--- /dev/null
+++ b/core/modules/text/css/text.icon.theme.pcss.css
@@ -0,0 +1,3 @@
+.field-icon-formatted_text {
+  background-image: url(../../../misc/icons/55565b/formatted_text.svg);
+}
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
index 8926a4acb639..5492544db25b 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextItem.php
@@ -11,8 +11,13 @@
  * @FieldType(
  *   id = "text",
  *   label = @Translation("Text (formatted)"),
- *   description = @Translation("This field stores a text with a text format."),
- *   category = @Translation("Text"),
+ *   description = {
+ *     @Translation("Ideal for titles and names that need to support markup such as bold, italics or links"),
+ *     @Translation("Efficient storage for short text"),
+ *     @Translation("Requires specifying a maximum length"),
+ *     @Translation("Good for fields with known or predictable lengths"),
+ *   },
+ *   category = "formatted_text",
  *   default_widget = "text_textfield",
  *   default_formatter = "text_default",
  *   list_class = "\Drupal\text\Plugin\Field\FieldType\TextFieldItemList"
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php
index ce35873326c5..3656d05e4fb5 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextLongItem.php
@@ -10,8 +10,12 @@
  * @FieldType(
  *   id = "text_long",
  *   label = @Translation("Text (formatted, long)"),
- *   description = @Translation("This field stores a long text with a text format."),
- *   category = @Translation("Text"),
+ *   description = {
+ *     @Translation("Ideal for longer texts, like body or description without a summary"),
+ *     @Translation("Supports long text without specifying a maximum length"),
+ *     @Translation("May use more storage and be slower for searching and sorting"),
+ *   },
+ *   category = "formatted_text",
  *   default_widget = "text_textarea",
  *   default_formatter = "text_default",
  *   list_class = "\Drupal\text\Plugin\Field\FieldType\TextFieldItemList"
diff --git a/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
index 088986d5760d..9d43920520cf 100644
--- a/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
+++ b/core/modules/text/src/Plugin/Field/FieldType/TextWithSummaryItem.php
@@ -13,8 +13,13 @@
  * @FieldType(
  *   id = "text_with_summary",
  *   label = @Translation("Text (formatted, long, with summary)"),
- *   description = @Translation("This field stores long text with a format and an optional summary."),
- *   category = @Translation("Text"),
+ *   description = {
+ *     @Translation("Ideal for longer texts, like body or description with a summary"),
+ *     @Translation("Allows specifying a summary for the text"),
+ *     @Translation("Supports long text without specifying a maximum length"),
+ *     @Translation("May use more storage and be slower for searching and sorting"),
+ *   },
+ *   category = "formatted_text",
  *   default_widget = "text_textarea_with_summary",
  *   default_formatter = "text_default",
  *   list_class = "\Drupal\text\Plugin\Field\FieldType\TextFieldItemList"
diff --git a/core/modules/text/text.libraries.yml b/core/modules/text/text.libraries.yml
index ebcfd3ab88b6..91df5cac843e 100644
--- a/core/modules/text/text.libraries.yml
+++ b/core/modules/text/text.libraries.yml
@@ -6,3 +6,11 @@ drupal.text:
     - core/jquery
     - core/once
     - core/drupal
+
+drupal.text-icon:
+  version: VERSION
+  css:
+    theme:
+      css/text.icon.theme.css: {}
+  dependencies:
+    - field_ui/drupal.field_ui.manage_fields
diff --git a/core/modules/text/text.module b/core/modules/text/text.module
index c0a03c62f978..eb74636478ac 100644
--- a/core/modules/text/text.module
+++ b/core/modules/text/text.module
@@ -164,3 +164,23 @@ function text_summary($text, $format = NULL, $size = NULL) {
 
   return $summary;
 }
+
+/**
+ * Implements hook_field_type_category_info().
+ */
+function text_field_type_category_info() {
+  return [
+    'formatted_text' => [
+      'label' => t('Formatted text'),
+      'description' => t('Text field with markup support and optional editor.'),
+      'weight' => -45,
+    ],
+  ];
+}
+
+/**
+ * Implements hook_preprocess_form_element__new_storage_type().
+ */
+function text_preprocess_form_element__new_storage_type(&$variables) {
+  $variables['#attached']['library'][] = 'text/drupal.text-icon';
+}
diff --git a/core/modules/workspaces/tests/src/Functional/WorkspaceTest.php b/core/modules/workspaces/tests/src/Functional/WorkspaceTest.php
index 5c04a00ba2bd..a4f54f59b4a9 100644
--- a/core/modules/workspaces/tests/src/Functional/WorkspaceTest.php
+++ b/core/modules/workspaces/tests/src/Functional/WorkspaceTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\workspaces\Functional;
 
 use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\field_ui\Traits\FieldUiTestTrait;
 use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
 use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
 
@@ -16,6 +17,7 @@ class WorkspaceTest extends BrowserTestBase {
   use WorkspaceTestUtilities;
   use ContentTypeCreationTrait;
   use TaxonomyTestTrait;
+  use FieldUiTestTrait;
 
   /**
    * {@inheritdoc}
@@ -225,16 +227,7 @@ public function testWorkspaceFieldUi() {
     // Create a new filed.
     $field_name = mb_strtolower($this->randomMachineName());
     $field_label = $this->randomMachineName();
-    $edit = [
-      'new_storage_type' => 'string',
-      'label' => $field_label,
-      'field_name' => $field_name,
-    ];
-    $this->drupalGet("admin/config/workflow/workspaces/fields/add-field");
-    $this->submitForm($edit, 'Save and continue');
-    $page = $this->getSession()->getPage();
-    $page->pressButton('Save field settings');
-    $page->pressButton('Save settings');
+    $this->fieldUIAddNewField('admin/config/workflow/workspaces', $field_name, $field_label, 'string');
 
     // Check that the field is displayed on the manage form display page.
     $this->drupalGet('admin/config/workflow/workspaces/form-display');
diff --git a/core/phpstan-baseline.neon b/core/phpstan-baseline.neon
index 82e143308d71..5b40932acc96 100644
--- a/core/phpstan-baseline.neon
+++ b/core/phpstan-baseline.neon
@@ -470,16 +470,6 @@ parameters:
 			count: 1
 			path: lib/Drupal/Core/Field/FieldItemList.php
 
-		-
-			message: "#^Call to method getDefinitions\\(\\) on an unknown class Drupal\\\\Core\\\\Plugin\\\\CategorizingPluginManagerTrait\\.$#"
-			count: 1
-			path: lib/Drupal/Core/Field/FieldTypePluginManager.php
-
-		-
-			message: "#^Call to method getSortedDefinitions\\(\\) on an unknown class Drupal\\\\Core\\\\Plugin\\\\CategorizingPluginManagerTrait\\.$#"
-			count: 1
-			path: lib/Drupal/Core/Field/FieldTypePluginManager.php
-
 		-
 			message: "#^Call to deprecated constant REQUEST_TIME\\: Deprecated in drupal\\:8\\.3\\.0 and is removed from drupal\\:11\\.0\\.0\\. Use \\\\Drupal\\:\\:time\\(\\)\\-\\>getRequestTime\\(\\); $#"
 			count: 1
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/Stable9LibraryOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Theme/Stable9LibraryOverrideTest.php
index e140b481c4ee..d4361352f7f5 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/Stable9LibraryOverrideTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/Stable9LibraryOverrideTest.php
@@ -18,6 +18,15 @@ class Stable9LibraryOverrideTest extends StableLibraryOverrideTestBase {
     'core/drupal.dialog.off_canvas',
     'layout_builder/drupal.layout_builder',
     'views/views.responsive-grid',
+    'field_ui/drupal.field_ui.manage_fields',
+    'comment/drupal.comment-icon',
+    'file/drupal.file-icon',
+    'text/drupal.text-icon',
+    'link/drupal.link-icon',
+    'media/drupal.media-icon',
+    'options/drupal.options-icon',
+    'telephone/drupal.telephone-icon',
+    'datetime_range/drupal.datetime_range-icon',
   ];
 
   /**
diff --git a/core/themes/stable9/templates/admin/form-element--new-storage-type.html.twig b/core/themes/stable9/templates/admin/form-element--new-storage-type.html.twig
new file mode 100644
index 000000000000..e2f7e3a714d8
--- /dev/null
+++ b/core/themes/stable9/templates/admin/form-element--new-storage-type.html.twig
@@ -0,0 +1,46 @@
+{#
+/**
+ * @file
+ * Theme override for a storage type option form element.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the containing element.
+ * - errors: (optional) Any errors for this form element, may not be set.
+ * - label: A rendered label element.
+ * - description: (optional) A list of description properties containing:
+ *    - content: A description of the form element, may not be set.
+ *    - attributes: (optional) A list of HTML attributes to apply to the
+ *      description content wrapper. Will only be set when description is set.
+ * - variant: specifies option type. Typically 'field-option' or 'field-suboption'.
+ *
+ * @see template_preprocess_form_element()
+ *
+ * @ingroup themeable
+ */
+#}
+{%
+  set classes = [
+  errors ? 'form-item--error',
+  variant ? variant ~ '__item'
+]
+%}
+<div{{ attributes.addClass(classes) }}>
+  {% if variant == 'field-option' %}
+    {{ label }}
+    <div{{ description.attributes }}>
+      {{ description.content }}
+    </div>
+  {% endif %}
+  {{ children }}
+  {% if variant == 'field-suboption' %}
+    {{ label }}
+    <div{{ description.attributes.addClass(description_classes) }}>
+      {{ description.content }}
+    </div>
+  {% endif %}
+  {% if errors %}
+    <div class="form-item--error-message">
+      {{ errors }}
+    </div>
+  {% endif %}
+</div>
-- 
GitLab