diff --git a/core/includes/form.inc b/core/includes/form.inc
index e41efe8e52d14a0fef7ac7dffc10f187d92e15b1..d759c8862516b9e8dc221ee13fd89a51d3010d63 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -216,6 +216,10 @@ function template_preprocess_fieldset(&$variables) {
   if (!empty($element['#description'])) {
     $description_id = $element['#attributes']['id'] . '--description';
     $description_attributes['id'] = $description_id;
+    $variables['description_display'] = $element['#description_display'];
+    if ($element['#description_display'] === 'invisible') {
+      $description_attributes['class'][] = 'visually-hidden';
+    }
     $description_attributes['data-drupal-field-elements'] = 'description';
     $variables['description']['attributes'] = new Attribute($description_attributes);
     $variables['description']['content'] = $element['#description'];
diff --git a/core/modules/system/templates/fieldset.html.twig b/core/modules/system/templates/fieldset.html.twig
index edb737111c527709e5f544ff194e3cb95ca43718..6a240e0bdc05c85f1a88cb49a8b96ec74e669837 100644
--- a/core/modules/system/templates/fieldset.html.twig
+++ b/core/modules/system/templates/fieldset.html.twig
@@ -14,6 +14,11 @@
  * - description: The description element containing the following properties:
  *   - content: The description content of the <fieldset>.
  *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
  * - children: The rendered child elements of the <fieldset>.
  * - prefix: The content to add before the <fieldset> children.
  * - suffix: The content to add after the <fieldset> children.
@@ -44,6 +49,9 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
+    {% endif %}
     {% if errors %}
       <div>
         {{ errors }}
@@ -56,7 +64,7 @@
     {% if suffix %}
       <span class="field-suffix">{{ suffix }}</span>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
     {% endif %}
   </div>
diff --git a/core/modules/system/tests/src/Kernel/Form/ElementsFieldsetTest.php b/core/modules/system/tests/src/Kernel/Form/ElementsFieldsetTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c2aafbfbeb9622dd40f46ac91bc26a316870dc02
--- /dev/null
+++ b/core/modules/system/tests/src/Kernel/Form/ElementsFieldsetTest.php
@@ -0,0 +1,149 @@
+<?php
+
+namespace Drupal\Tests\system\Kernel\Form;
+
+use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests fieldset element rendering and description placement.
+ *
+ * @group Form
+ */
+class ElementsFieldsetTest extends KernelTestBase implements FormInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['system'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'form_test_fieldset_element';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['fieldset_default'] = [
+      '#type' => 'fieldset',
+      '#title' => 'Fieldset title for default description display',
+      '#description' => 'Fieldset description for default description display.',
+    ];
+    $form['meta_default'] = [
+      '#type' => 'container',
+      '#title' => 'Group element',
+      '#group' => 'fieldset_default',
+    ];
+    $form['meta_default']['element'] = [
+      '#type' => 'textfield',
+      '#title' => 'Nested text field inside meta_default element',
+    ];
+
+    $form['fieldset_before'] = [
+      '#type' => 'fieldset',
+      '#title' => 'Fieldset title for description displayed before element',
+      '#description' => 'Fieldset description for description displayed before element.',
+      '#description_display' => 'before',
+    ];
+    $form['meta_before'] = [
+      '#type' => 'container',
+      '#title' => 'Group element',
+      '#group' => 'fieldset_before',
+    ];
+    $form['meta_before']['element'] = [
+      '#type' => 'textfield',
+      '#title' => 'Nested text field inside meta_before element',
+    ];
+
+    $form['fieldset_after'] = [
+      '#type' => 'fieldset',
+      '#title' => 'Fieldset title for description displayed after element',
+      '#description' => 'Fieldset description for description displayed after element.',
+      '#description_display' => 'after',
+    ];
+    $form['meta_after'] = [
+      '#type' => 'container',
+      '#title' => 'Group element',
+      '#group' => 'fieldset_after',
+    ];
+    $form['meta_after']['element'] = [
+      '#type' => 'textfield',
+      '#title' => 'Nested text field inside meta_after element',
+    ];
+
+    $form['fieldset_invisible'] = [
+      '#type' => 'fieldset',
+      '#title' => 'Fieldset title for description displayed as visually hidden element',
+      '#description' => 'Fieldset description for description displayed as visually hidden element.',
+      '#description_display' => 'invisible',
+    ];
+    $form['meta_invisible'] = [
+      '#type' => 'container',
+      '#title' => 'Group element',
+      '#group' => 'fieldset_invisible',
+    ];
+    $form['meta_invisible']['element'] = [
+      '#type' => 'textfield',
+      '#title' => 'Nested text field inside meta_invisible element',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {}
+
+  /**
+   * Tests different display options for fieldset element descriptions.
+   */
+  public function testFieldsetDescriptions() {
+    $form_state = new FormState();
+    $form = \Drupal::formBuilder()->getForm($this);
+    $this->render($form);
+
+    // Check #description placement with #description_display not set. By
+    // default, the #description should appear after any fieldset elements.
+    $field_id = 'edit-fieldset-default';
+    $description_id = $field_id . '--description';
+    $elements = $this->xpath('//fieldset[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]//div[@id="edit-meta-default"]/following-sibling::div[@id="' . $description_id . '"]');
+    $this->assertCount(1, $elements);
+
+    // Check #description placement with #description_display set to 'before'.
+    $field_id = 'edit-fieldset-before';
+    $description_id = $field_id . '--description';
+    $elements = $this->xpath('//fieldset[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]//div[@id="edit-meta-before"]/preceding-sibling::div[@id="' . $description_id . '"]');
+    $this->assertCount(1, $elements);
+
+    // Check #description placement with #description_display set to 'after'.
+    $field_id = 'edit-fieldset-after';
+    $description_id = $field_id . '--description';
+    $elements = $this->xpath('//fieldset[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]//div[@id="edit-meta-after"]/following-sibling::div[@id="' . $description_id . '"]');
+    $this->assertCount(1, $elements);
+
+    // Check if the 'visually-hidden' class is set on the fieldset description
+    // with #description_display set to 'invisible'. Also check that the
+    // description is placed after the form element.
+    $field_id = 'edit-fieldset-invisible';
+    $description_id = $field_id . '--description';
+    $elements = $this->xpath('//fieldset[@id="' . $field_id . '" and @aria-describedby="' . $description_id . '"]//div[@id="edit-meta-invisible"]/following-sibling::div[contains(@class, "visually-hidden")]');
+    $this->assertCount(1, $elements);
+
+    \Drupal::formBuilder()->submitForm($this, $form_state);
+    $errors = $form_state->getErrors();
+    $this->assertEmpty($errors);
+  }
+
+}
diff --git a/core/profiles/demo_umami/themes/umami/templates/classy/form/fieldset.html.twig b/core/profiles/demo_umami/themes/umami/templates/classy/form/fieldset.html.twig
index db63082e8a8979b3a54b84ec5b071fa424791470..93b5f54d8964c748ddce16a004bb78f2b21abc5f 100644
--- a/core/profiles/demo_umami/themes/umami/templates/classy/form/fieldset.html.twig
+++ b/core/profiles/demo_umami/themes/umami/templates/classy/form/fieldset.html.twig
@@ -13,6 +13,11 @@
  * - description: The description element containing the following properties:
  *   - content: The description content of the fieldset.
  *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
  * - children: The rendered child elements of the fieldset.
  * - prefix: The content to add before the fieldset children.
  * - suffix: The content to add after the fieldset children.
@@ -41,6 +46,9 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
+    {% endif %}
     {% if errors %}
       <div class="form-item--error-message">
         <strong>{{ errors }}</strong>
@@ -53,7 +61,7 @@
     {% if suffix %}
       <span class="field-suffix">{{ suffix }}</span>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
     {% endif %}
   </div>
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/ConfirmClassyCopiesTest.php b/core/tests/Drupal/KernelTests/Core/Theme/ConfirmClassyCopiesTest.php
index e95aad3e1e9533f5e504056b4685a98501dfc3f8..3e54a43ce4d6a0c8840644cd597b34bc9b8015c3 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/ConfirmClassyCopiesTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/ConfirmClassyCopiesTest.php
@@ -752,7 +752,7 @@ protected function getClassyHash($type, $file) {
         'input.html.twig' => 'b844ef5f74df3058bd6ff9ec024d907b',
         'form-element-label.html.twig' => '21026361fa9ba15ccdc823d6bb00a6d9',
         'datetime-wrapper.html.twig' => '765aa519f7e4ae36ee4726ae2633de6d',
-        'fieldset.html.twig' => 'a9067cc8e8896e91059a50798942aca8',
+        'fieldset.html.twig' => '5900e42f5ee2574a3d002d19771bd764',
         'form.html.twig' => '0767ff441543553d51443b970c4b736b',
         'datetime-form.html.twig' => '649c36a2900c556b8a1385c1fa755281',
         'checkboxes.html.twig' => 'a5faa5fdd7de4aa42045753db65ffb0b',
diff --git a/core/themes/bartik/templates/classy/form/fieldset.html.twig b/core/themes/bartik/templates/classy/form/fieldset.html.twig
index db63082e8a8979b3a54b84ec5b071fa424791470..93b5f54d8964c748ddce16a004bb78f2b21abc5f 100644
--- a/core/themes/bartik/templates/classy/form/fieldset.html.twig
+++ b/core/themes/bartik/templates/classy/form/fieldset.html.twig
@@ -13,6 +13,11 @@
  * - description: The description element containing the following properties:
  *   - content: The description content of the fieldset.
  *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
  * - children: The rendered child elements of the fieldset.
  * - prefix: The content to add before the fieldset children.
  * - suffix: The content to add after the fieldset children.
@@ -41,6 +46,9 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
+    {% endif %}
     {% if errors %}
       <div class="form-item--error-message">
         <strong>{{ errors }}</strong>
@@ -53,7 +61,7 @@
     {% if suffix %}
       <span class="field-suffix">{{ suffix }}</span>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
     {% endif %}
   </div>
diff --git a/core/themes/claro/templates/fieldset.html.twig b/core/themes/claro/templates/fieldset.html.twig
index 3609aba98fa6ace6b5cd59bca34c85f4e9f3cd45..d6c87070e9976ca6a3d25eb81ef80220e8da0bdf 100644
--- a/core/themes/claro/templates/fieldset.html.twig
+++ b/core/themes/claro/templates/fieldset.html.twig
@@ -3,6 +3,25 @@
  * @file
  * Theme override for a fieldset element and its children.
  *
+ * Available variables:
+ * - attributes: HTML attributes for the fieldset element.
+ * - errors: (optional) Any errors for this fieldset element, may not be set.
+ * - required: Boolean indicating whether the fieldset element is required.
+ * - legend: The legend element containing the following properties:
+ *   - title: Title of the fieldset, intended for use as the text of the legend.
+ *   - attributes: HTML attributes to apply to the legend.
+ * - description: The description element containing the following properties:
+ *   - content: The description content of the fieldset.
+ *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
+ * - children: The rendered child elements of the fieldset.
+ * - prefix: The content to add before the fieldset children.
+ * - suffix: The content to add after the fieldset children.
+ *
  * @see template_preprocess_fieldset()
  * @see claro_preprocess_fieldset()
  */
@@ -54,6 +73,9 @@
   {% endif %}
 
   <div{{ content_attributes.addClass(wrapper_classes) }}>
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass(description_classes) }}>{{ description.content }}</div>
+    {% endif %}
     {% if inline_items %}
       <div class="container-inline">
     {% endif %}
@@ -70,7 +92,7 @@
         {{ errors }}
       </div>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass(description_classes) }}>{{ description.content }}</div>
     {% endif %}
 
diff --git a/core/themes/classy/templates/form/fieldset.html.twig b/core/themes/classy/templates/form/fieldset.html.twig
index db63082e8a8979b3a54b84ec5b071fa424791470..93b5f54d8964c748ddce16a004bb78f2b21abc5f 100644
--- a/core/themes/classy/templates/form/fieldset.html.twig
+++ b/core/themes/classy/templates/form/fieldset.html.twig
@@ -13,6 +13,11 @@
  * - description: The description element containing the following properties:
  *   - content: The description content of the fieldset.
  *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
  * - children: The rendered child elements of the fieldset.
  * - prefix: The content to add before the fieldset children.
  * - suffix: The content to add after the fieldset children.
@@ -41,6 +46,9 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
+    {% endif %}
     {% if errors %}
       <div class="form-item--error-message">
         <strong>{{ errors }}</strong>
@@ -53,7 +61,7 @@
     {% if suffix %}
       <span class="field-suffix">{{ suffix }}</span>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
     {% endif %}
   </div>
diff --git a/core/themes/olivero/templates/form/fieldset.html.twig b/core/themes/olivero/templates/form/fieldset.html.twig
index 7994ea57f0a940c0aeb41baddd316842db675df8..9efc4e5a70fe0c17b5e1cce4928f44df502bddb5 100644
--- a/core/themes/olivero/templates/form/fieldset.html.twig
+++ b/core/themes/olivero/templates/form/fieldset.html.twig
@@ -14,6 +14,11 @@
  * - description: The description element containing the following properties:
  *   - content: The description content of the <fieldset>.
  *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
  * - children: The rendered child elements of the <fieldset>.
  * - prefix: The content to add before the <fieldset> children.
  * - suffix: The content to add after the <fieldset> children.
@@ -75,6 +80,9 @@
       <div class="container-inline">
     {% endif %}
 
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass(description_classes) }}>{{ description.content }}</div>
+    {% endif %}
     {% if prefix %}
       <span class="fieldset__prefix">{{ prefix }}</span>
     {% endif %}
@@ -87,7 +95,7 @@
         {{ errors }}
       </div>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass(description_classes) }}>{{ description.content }}</div>
     {% endif %}
 
diff --git a/core/themes/seven/templates/classy/form/fieldset.html.twig b/core/themes/seven/templates/classy/form/fieldset.html.twig
index db63082e8a8979b3a54b84ec5b071fa424791470..93b5f54d8964c748ddce16a004bb78f2b21abc5f 100644
--- a/core/themes/seven/templates/classy/form/fieldset.html.twig
+++ b/core/themes/seven/templates/classy/form/fieldset.html.twig
@@ -13,6 +13,11 @@
  * - description: The description element containing the following properties:
  *   - content: The description content of the fieldset.
  *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
  * - children: The rendered child elements of the fieldset.
  * - prefix: The content to add before the fieldset children.
  * - suffix: The content to add after the fieldset children.
@@ -41,6 +46,9 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
+    {% endif %}
     {% if errors %}
       <div class="form-item--error-message">
         <strong>{{ errors }}</strong>
@@ -53,7 +61,7 @@
     {% if suffix %}
       <span class="field-suffix">{{ suffix }}</span>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
     {% endif %}
   </div>
diff --git a/core/themes/stable/templates/form/fieldset.html.twig b/core/themes/stable/templates/form/fieldset.html.twig
index c13ddda7c427e1c4af60cb907894b10fb8e20c72..efd05e3425bcb373a0b6b9ce0ac63568748d926d 100644
--- a/core/themes/stable/templates/form/fieldset.html.twig
+++ b/core/themes/stable/templates/form/fieldset.html.twig
@@ -14,6 +14,11 @@
  * - description: The description element containing the following properties:
  *   - content: The description content of the <fieldset>.
  *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
  * - children: The rendered child elements of the <fieldset>.
  * - prefix: The content to add before the <fieldset> children.
  * - suffix: The content to add after the <fieldset> children.
@@ -42,6 +47,9 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
+    {% endif %}
     {% if errors %}
       <div>
         {{ errors }}
@@ -54,7 +62,7 @@
     {% if suffix %}
       <span class="field-suffix">{{ suffix }}</span>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
     {% endif %}
   </div>
diff --git a/core/themes/stable9/templates/form/fieldset.html.twig b/core/themes/stable9/templates/form/fieldset.html.twig
index c13ddda7c427e1c4af60cb907894b10fb8e20c72..efd05e3425bcb373a0b6b9ce0ac63568748d926d 100644
--- a/core/themes/stable9/templates/form/fieldset.html.twig
+++ b/core/themes/stable9/templates/form/fieldset.html.twig
@@ -14,6 +14,11 @@
  * - description: The description element containing the following properties:
  *   - content: The description content of the <fieldset>.
  *   - attributes: HTML attributes to apply to the description container.
+ * - description_display: Description display setting. It can have these values:
+ *   - before: The description is output before the element.
+ *   - after: The description is output after the element (default).
+ *   - invisible: The description is output after the element, hidden visually
+ *     but available to screen readers.
  * - children: The rendered child elements of the <fieldset>.
  * - prefix: The content to add before the <fieldset> children.
  * - suffix: The content to add after the <fieldset> children.
@@ -42,6 +47,9 @@
     <span{{ legend_span.attributes.addClass(legend_span_classes) }}>{{ legend.title }}</span>
   </legend>
   <div class="fieldset-wrapper">
+    {% if description_display == 'before' and description.content %}
+      <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
+    {% endif %}
     {% if errors %}
       <div>
         {{ errors }}
@@ -54,7 +62,7 @@
     {% if suffix %}
       <span class="field-suffix">{{ suffix }}</span>
     {% endif %}
-    {% if description.content %}
+    {% if description_display in ['after', 'invisible'] and description.content %}
       <div{{ description.attributes.addClass('description') }}>{{ description.content }}</div>
     {% endif %}
   </div>