diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index 67ced51148..0ed0907949 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -245,6 +245,20 @@ drupal.progress:
     - core/jquery
     - core/drupalSettings
 
+drupal.splitbutton:
+  version: VERSION
+  js:
+    misc/splitbutton/splitbutton.js: {}
+  css:
+    component:
+      misc/splitbutton/splitbutton.css: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - core/popperjs
+
 drupal.states:
   version: VERSION
   js:
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 5998a235b4..b3e688da0a 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -630,6 +630,18 @@ function template_preprocess_datetime_wrapper(&$variables) {
   $variables['content'] = $element['#children'];
 }
 
+function template_preprocess_operation_list(&$variables) {
+  $operations = $variables['operations'];
+  if (!empty($operations)) {
+    $elements = ['submit', 'input', 'button', 'link'];
+    foreach ($operations as $key => $operation) {
+      if (isset($operation['#type']) && in_array($operation['#type'], $elements)) {
+        $variables['items'][] = $operation;
+      }
+    }
+  }
+}
+
 /**
  * Prepares variables for links templates.
  *
@@ -2008,6 +2020,9 @@ function drupal_common_theme() {
     'links' => [
       'variables' => ['links' => [], 'attributes' => ['class' => ['links']], 'heading' => [], 'set_active_class' => FALSE],
     ],
+    'operation_list' => [
+      'variables' => ['attributes' => [], 'operations' => null, '#theme' => 'operation_list'],
+    ],
     'dropbutton_wrapper' => [
       'variables' => ['children' => NULL],
     ],
diff --git a/core/lib/Drupal/Core/Render/Element/OperationList.php b/core/lib/Drupal/Core/Render/Element/OperationList.php
new file mode 100644
index 0000000000..addb943aef
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/OperationList.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\Core\Render\Element;
+
+/**
+ * A list of links, buttons and submit inputs.
+ *
+ * @RenderElement("operation_list")
+ */
+class OperationList extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    return [
+      '#theme' => 'operation_list',
+      '#operations' => [],
+    ];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Element/Splitbutton.php b/core/lib/Drupal/Core/Render/Element/Splitbutton.php
new file mode 100644
index 0000000000..47b128b7e4
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/Splitbutton.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace Drupal\Core\Render\Element;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Splitbutton.
+ *
+ * @todo Write the description + properties.
+ *
+ * @RenderElement("splitbutton")
+ */
+class Splitbutton extends RenderElement {
+
+  use StringTranslationTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $class = get_class($this);
+    return [
+      '#pre_render' => [
+        [$class, 'preRenderSplitbutton'],
+      ],
+      '#theme_wrappers' => [
+        'container' => [
+          '#attributes' => [
+            'class' => ['splitbutton'],
+          ],
+        ],
+      ],
+      '#theme' => 'splitbutton',
+    ];
+  }
+
+  /**
+   * Description pending.
+   *
+   * @param array $element
+   *   The render element.
+   *
+   * @return array
+   *   Render array.
+   */
+  public static function preRenderSplitbutton(array $element) {
+    $element['#attached']['library'][] = 'core/drupal.splitbutton';
+    if (!empty($element['#dropbutton'])) {
+      $element['#theme_wrappers']['container']['#attributes']['class'][] = 'splitbutton--dropbutton';
+    }
+
+    $splitbutton_types = [];
+
+    if (!empty($element['#splitbutton_type'])) {
+      // If #splitbutton_type exists and it is a string, place it in an array.
+      $splitbutton_types = is_array($element['#splitbutton_type']) ? $element['#splitbutton_type'] : [$element['#splitbutton_type']];
+    };
+
+    if (!empty($element['#dropbutton_type'])) {
+      @trigger_error("The #dropbutton_type property in splitbutton is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0.", E_USER_DEPRECATED);
+      $splitbutton_types[] = $element['#dropbutton_type'];
+    }
+
+    // Add modifier class to wrapper.
+    foreach ($splitbutton_types as $container_splitbutton_type) {
+      $element['#theme_wrappers']['container']['#attributes']['class'][] = 'splitbutton--' . $container_splitbutton_type;
+    }
+
+    $items = $element['#splitbutton_items'] ?? [];
+
+    if (isset($element['#links'])) {
+      @trigger_error("The #links property in splitbutton is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0.", E_USER_DEPRECATED);
+
+      foreach ($element['#links'] as &$op) {
+        if (isset($op['url']) && isset($op['title'])) {
+          $op['#url'] = $op['url'];
+          unset($op['url']);
+          $op['#title'] = $op['title'];
+          unset($op['title']);
+          $op['#type'] = 'link';
+        }
+      }
+      $items += $element['#links'];
+    }
+
+    $element['trigger'] = [
+      '#type' => 'container',
+      '#attributes' => [
+        'class' => [
+          'splitbutton__main',
+        ],
+      ],
+    ];
+
+    $trigger_id = Html::getUniqueId('splitbutton');
+
+    // If the #title property is present, Splitbutton has a single main button
+    // that toggles the operations list instead of a link/button accompanied by
+    // a separate toggle.
+    if (isset($element['#title'])) {
+      $element['trigger']['#attributes']['class'][] = 'splitbutton__main--no-toggle';
+      $element['trigger']['title'] = [
+        '#type' => 'html_tag',
+        '#tag' => 'button',
+        '#attributes' => [
+          'class' => ['splitbutton__title--toggle', 'button'],
+          'data-splitbutton-trigger' => $trigger_id,
+        ],
+        'value' => [
+          '#markup' => $element['#title'],
+        ],
+      ];
+    }
+    else {
+      $element['trigger']['#attributes']['class'][] = 'splitbutton__main--with-toggle';
+      $first_item = array_shift($items);
+      $element['trigger']['title'] = $first_item;
+      $element['trigger']['title']['#attributes']['class'][] = 'button';
+      $element['trigger']['title']['#attributes']['class'][] = 'splitbutton__main-button';
+      $element['trigger']['title']['#attributes']['class'][] = 'splitbutton__main-button--' . $first_item['#type'];
+
+      // If there are additional items, add a toggle for their visibility.
+      if (count($items)) {
+        $element['trigger']['toggle'] = [
+          '#type' => 'html_tag',
+          '#tag' => 'button',
+          '#value' => '',
+          '#attributes' => [
+            'class' => [
+              'js-show',
+              'splitbutton__toggle',
+              'button',
+            ],
+            'data-splitbutton-trigger' => $trigger_id,
+          ],
+          'splitbutton_arrow' => [
+            '#type' => 'html_tag',
+            '#tag' => 'span',
+            '#attributes' => [
+              'class' => ['splitbutton__toggle-arrow'],
+            ],
+            'list_additional_actions' => [
+              '#type' => 'html_tag',
+              '#tag' => 'span',
+              '#attributes' => [
+                'class' => ['visually-hidden'],
+              ],
+              'message' => [
+                '#markup' => t('List additional actions'),
+              ],
+            ],
+          ],
+        ];
+        foreach ($splitbutton_types as $toggle_splitbutton_type) {
+          $element['trigger']['toggle']['#attributes']['class'][] = 'button--' . $toggle_splitbutton_type;
+        }
+      }
+    }
+
+    // Add modifier classes based on #splitbutton_type to the main button.
+    foreach ($splitbutton_types as $main_splitbutton_type) {
+      $element['trigger']['title']['#attributes']['class'][] = 'button--' . $main_splitbutton_type;
+    }
+
+    // If additional items are present, place them in an operation list.
+    if (count($items)) {
+      foreach ($items as &$item) {
+        $item['#attributes']['class'][] = 'splitbutton__operation-list-item';
+      }
+      $element['operation_list'] = [
+        '#operations' => $items,
+        '#theme' => 'operation_list',
+        '#attributes' => [
+          'class' => 'splitbutton__operation-list',
+          'data-splitbutton-target' => $trigger_id,
+        ],
+      ];
+    }
+
+    return $element;
+  }
+
+}
diff --git a/core/misc/splitbutton/splitbutton.css b/core/misc/splitbutton/splitbutton.css
new file mode 100644
index 0000000000..154b3be98c
--- /dev/null
+++ b/core/misc/splitbutton/splitbutton.css
@@ -0,0 +1,37 @@
+.splitbutton {
+  box-sizing: border-box;
+}
+
+.splitbutton__operation-list {
+  margin: 0;
+  padding: 0;
+  list-style: none;
+}
+
+.splitbutton__main {
+  position: relative;
+  display: inline-flex;
+  font-size: 0.889rem;
+}
+
+.splitbutton__main .button {
+  margin: 0;
+}
+
+.js .splitbutton__operation-list {
+  display: none;
+}
+.js .splitbutton--enabled.open .splitbutton__operation-list {
+  z-index: 4;
+  display: block;
+}
+
+.splitbutton__operation-list-item {
+  display: block;
+  padding: 0;
+  border: none;
+  background: none;
+}
+.splitbutton__operation-list-item:hover {
+  text-decoration: none;
+}
diff --git a/core/misc/splitbutton/splitbutton.es6.js b/core/misc/splitbutton/splitbutton.es6.js
new file mode 100644
index 0000000000..92dc8f8ab2
--- /dev/null
+++ b/core/misc/splitbutton/splitbutton.es6.js
@@ -0,0 +1,239 @@
+/**
+ * @file
+ * Splitbutton feature.
+ */
+
+(($, Drupal) => {
+  /**
+   * A SplitButton presents an HTML list. The first item in the list is always
+   * visible, and the remaining items are accessed via a toggle button.
+   *
+   * @constructor Drupal.SplitButton
+   *
+   * @param {HTMLElement} splitbutton
+   *   A DOM element.
+   */
+  function SplitButton(splitbutton) {
+    // Add ARIA attributes to adhere to the Menu Button pattern.
+    // @see https://www.w3.org/TR/wai-aria-practices/examples/menu-button/menu-button-links.html
+    Array.prototype.slice
+      .call(splitbutton.querySelectorAll('.splitbutton__operation-list li'))
+      .forEach(item => {
+        item.setAttribute('role', 'none');
+        item
+          .querySelector('.splitbutton__operation-list-item')
+          .setAttribute('role', 'menuitem');
+      });
+
+    const trigger = splitbutton.querySelector('[data-splitbutton-trigger]');
+    const triggerIdentifier = trigger.getAttribute('data-splitbutton-trigger');
+    const target = splitbutton.querySelector('[data-splitbutton-target]');
+    const triggerContainer = splitbutton.querySelector('.splitbutton__main');
+
+    trigger.setAttribute('aria-haspopup', 'true');
+    trigger.setAttribute('aria-controls', `${triggerIdentifier}-trigger`);
+    trigger.setAttribute('id', `${triggerIdentifier}-menu`);
+
+    target.setAttribute('role', 'menu');
+    target.setAttribute('aria-labelledby', `${triggerIdentifier}-menu`);
+    target.setAttribute('id', `${triggerIdentifier}-trigger`);
+    target.style['min-width'] = `${triggerContainer.offsetWidth}px`;
+
+    this.triggerIdentifier = triggerIdentifier;
+
+    this.popper = Popper.createPopper(triggerContainer, target, {
+      placement: 'bottom-start',
+      modifiers: [
+        {
+          name: 'flip',
+          options: {
+            fallbackPlacements: [],
+          },
+        },
+      ],
+    });
+
+    splitbutton.classList.add('splitbutton--enabled');
+
+    /**
+     * @type {jQuery}
+     */
+    this.$splitbutton = $(splitbutton);
+
+    /**
+     * Find actions and mark them.
+     *
+     * @type {jQuery}
+     */
+    this.$splitbutton.on({
+      /**
+       * Adds a timeout to close the dropdown on mouseleave.
+       *
+       * @ignore
+       */
+      'mouseleave.splitbutton': $.proxy(this.hoverOut, this),
+
+      /**
+       * Clears timeout when mouseout of the dropdown.
+       *
+       * @ignore
+       */
+      'mouseenter.splitbutton': $.proxy(this.hoverIn, this),
+
+      /**
+       * Similar to mouseleave/mouseenter, but for keyboard navigation.
+       *
+       * @ignore
+       */
+      'focusout.splitbutton': $.proxy(this.focusOut, this),
+
+      /**
+       * @ignore
+       */
+      'focusin.splitbutton': $.proxy(this.focusIn, this),
+    });
+  }
+
+  /**
+   * Delegated callback for opening and closing splitbutton secondary actions.
+   *
+   * @function Drupal.SplitButton~splitbuttonClickHandler
+   *
+   * @param {jQuery.Event} e
+   *   The event triggered.
+   */
+  function splitbuttonClickHandler(e) {
+    e.preventDefault();
+    const triggerIdentifier = e.currentTarget.getAttribute(
+      'data-splitbutton-trigger',
+    );
+
+    $(e.target)
+      .closest('.splitbutton')
+      .toggleClass('open');
+    $.each(Drupal.SplitButton.splitbuttons, (index, splitbutton) => {
+      if (triggerIdentifier === splitbutton.triggerIdentifier) {
+        splitbutton.popper.forceUpdate();
+      }
+    });
+  }
+
+  /**
+   * Process elements with the .splitbutton class on page load.
+   *
+   * @type {Drupal~behavior}
+   *
+   * @prop {Drupal~behaviorAttach} attach
+   *   Attaches splitButton behaviors.
+   */
+  Drupal.behaviors.splitButton = {
+    attach(context) {
+      const $splitbuttons = $(context)
+        .find('.splitbutton')
+        .once('splitbutton');
+      if ($splitbuttons.length) {
+        // Adds the delegated handler that will toggle dropdowns on click.
+        const $body = $('body').once('splitbutton-click');
+        if ($body.length) {
+          $body.on(
+            'click',
+            '[data-splitbutton-trigger]',
+            splitbuttonClickHandler,
+          );
+        }
+        // Initialize all buttons.
+        const il = $splitbuttons.length;
+        for (let i = 0; i < il; i++) {
+          SplitButton.splitbuttons.push(new SplitButton($splitbuttons[i]));
+        }
+      }
+    },
+  };
+
+  /**
+   * Extend the SplitButton constructor.
+   */
+  $.extend(
+    SplitButton,
+    /** @lends Drupal.SplitButton */ {
+      /**
+       * Store all processed SplitButtons.
+       *
+       * @type {Array.<Drupal.SplitButton>}
+       */
+      splitbuttons: [],
+    },
+  );
+
+  /**
+   * Extend the SplitButton prototype.
+   */
+  $.extend(
+    SplitButton.prototype,
+    /** @lends Drupal.SplitButton# */ {
+      /**
+       * Toggle the splitbutton open and closed.
+       *
+       * @param {bool} [show]
+       *   Force the splitbutton to open by passing true or to close by
+       *   passing false.
+       */
+      toggle(show) {
+        const isBool = typeof show === 'boolean';
+        show = isBool ? show : !this.$splitbutton.hasClass('open');
+        this.$splitbutton.toggleClass('open', show);
+      },
+
+      /**
+       * @method
+       */
+      hoverIn() {
+        // Clear any previous timer we were using.
+        if (this.timerID) {
+          window.clearTimeout(this.timerID);
+        }
+      },
+
+      /**
+       * @method
+       */
+      hoverOut() {
+        // Wait half a second before closing.
+        this.timerID = window.setTimeout($.proxy(this, 'close'), 500);
+      },
+
+      /**
+       * @method
+       */
+      open() {
+        this.toggle(true);
+      },
+
+      /**
+       * @method
+       */
+      close() {
+        this.toggle(false);
+      },
+
+      /**
+       * @param {jQuery.Event} e
+       *   The event triggered.
+       */
+      focusOut(e) {
+        this.hoverOut.call(this, e);
+      },
+
+      /**
+       * @param {jQuery.Event} e
+       *   The event triggered.
+       */
+      focusIn(e) {
+        this.hoverIn.call(this, e);
+      },
+    },
+  );
+
+  // Expose constructor in the public space.
+  Drupal.SplitButton = SplitButton;
+})(jQuery, Drupal, Popper);
diff --git a/core/misc/splitbutton/splitbutton.js b/core/misc/splitbutton/splitbutton.js
new file mode 100644
index 0000000000..7e5d0d3160
--- /dev/null
+++ b/core/misc/splitbutton/splitbutton.js
@@ -0,0 +1,106 @@
+/**
+* DO NOT EDIT THIS FILE.
+* See the following change record for more information,
+* https://www.drupal.org/node/2815083
+* @preserve
+**/
+
+(function ($, Drupal) {
+  function SplitButton(splitbutton) {
+    Array.prototype.slice.call(splitbutton.querySelectorAll('.splitbutton__operation-list li')).forEach(function (item) {
+      item.setAttribute('role', 'none');
+      item.querySelector('.splitbutton__operation-list-item').setAttribute('role', 'menuitem');
+    });
+    var trigger = splitbutton.querySelector('[data-splitbutton-trigger]');
+    var triggerIdentifier = trigger.getAttribute('data-splitbutton-trigger');
+    var target = splitbutton.querySelector('[data-splitbutton-target]');
+    var triggerContainer = splitbutton.querySelector('.splitbutton__main');
+    trigger.setAttribute('aria-haspopup', 'true');
+    trigger.setAttribute('aria-controls', "".concat(triggerIdentifier, "-trigger"));
+    trigger.setAttribute('id', "".concat(triggerIdentifier, "-menu"));
+    target.setAttribute('role', 'menu');
+    target.setAttribute('aria-labelledby', "".concat(triggerIdentifier, "-menu"));
+    target.setAttribute('id', "".concat(triggerIdentifier, "-trigger"));
+    target.style['min-width'] = "".concat(triggerContainer.offsetWidth, "px");
+    this.triggerIdentifier = triggerIdentifier;
+    this.popper = Popper.createPopper(triggerContainer, target, {
+      placement: 'bottom-start',
+      modifiers: [{
+        name: 'flip',
+        options: {
+          fallbackPlacements: []
+        }
+      }]
+    });
+    splitbutton.classList.add('splitbutton--enabled');
+    this.$splitbutton = $(splitbutton);
+    this.$splitbutton.on({
+      'mouseleave.splitbutton': $.proxy(this.hoverOut, this),
+      'mouseenter.splitbutton': $.proxy(this.hoverIn, this),
+      'focusout.splitbutton': $.proxy(this.focusOut, this),
+      'focusin.splitbutton': $.proxy(this.focusIn, this)
+    });
+  }
+
+  function splitbuttonClickHandler(e) {
+    e.preventDefault();
+    var triggerIdentifier = e.currentTarget.getAttribute('data-splitbutton-trigger');
+    $(e.target).closest('.splitbutton').toggleClass('open');
+    $.each(Drupal.SplitButton.splitbuttons, function (index, splitbutton) {
+      if (triggerIdentifier === splitbutton.triggerIdentifier) {
+        splitbutton.popper.forceUpdate();
+      }
+    });
+  }
+
+  Drupal.behaviors.splitButton = {
+    attach: function attach(context) {
+      var $splitbuttons = $(context).find('.splitbutton').once('splitbutton');
+
+      if ($splitbuttons.length) {
+        var $body = $('body').once('splitbutton-click');
+
+        if ($body.length) {
+          $body.on('click', '[data-splitbutton-trigger]', splitbuttonClickHandler);
+        }
+
+        var il = $splitbuttons.length;
+
+        for (var i = 0; i < il; i++) {
+          SplitButton.splitbuttons.push(new SplitButton($splitbuttons[i]));
+        }
+      }
+    }
+  };
+  $.extend(SplitButton, {
+    splitbuttons: []
+  });
+  $.extend(SplitButton.prototype, {
+    toggle: function toggle(show) {
+      var isBool = typeof show === 'boolean';
+      show = isBool ? show : !this.$splitbutton.hasClass('open');
+      this.$splitbutton.toggleClass('open', show);
+    },
+    hoverIn: function hoverIn() {
+      if (this.timerID) {
+        window.clearTimeout(this.timerID);
+      }
+    },
+    hoverOut: function hoverOut() {
+      this.timerID = window.setTimeout($.proxy(this, 'close'), 500);
+    },
+    open: function open() {
+      this.toggle(true);
+    },
+    close: function close() {
+      this.toggle(false);
+    },
+    focusOut: function focusOut(e) {
+      this.hoverOut.call(this, e);
+    },
+    focusIn: function focusIn(e) {
+      this.hoverIn.call(this, e);
+    }
+  });
+  Drupal.SplitButton = SplitButton;
+})(jQuery, Drupal, Popper);
\ No newline at end of file
diff --git a/core/modules/system/templates/operation-list.html.twig b/core/modules/system/templates/operation-list.html.twig
new file mode 100644
index 0000000000..8166804cd1
--- /dev/null
+++ b/core/modules/system/templates/operation-list.html.twig
@@ -0,0 +1,7 @@
+{%- if items -%}
+    <ul {{ attributes }}>
+        {%- for item in items -%}
+            <li>{{ item }}</li>
+        {%- endfor -%}
+    </ul>
+{%- endif -%}
\ No newline at end of file
diff --git a/core/modules/system/tests/modules/splitbutton_test/splitbutton_test.info.yml b/core/modules/system/tests/modules/splitbutton_test/splitbutton_test.info.yml
new file mode 100644
index 0000000000..ec9dbcc87d
--- /dev/null
+++ b/core/modules/system/tests/modules/splitbutton_test/splitbutton_test.info.yml
@@ -0,0 +1,5 @@
+name: 'Splitbutton Test'
+type: module
+description: 'For testing splitbuttons'
+core_version_requirement: ^9
+package: Testing
diff --git a/core/modules/system/tests/modules/splitbutton_test/splitbutton_test.routing.yml b/core/modules/system/tests/modules/splitbutton_test/splitbutton_test.routing.yml
new file mode 100644
index 0000000000..5c10607f5d
--- /dev/null
+++ b/core/modules/system/tests/modules/splitbutton_test/splitbutton_test.routing.yml
@@ -0,0 +1,8 @@
+splitbutton.test:
+  path: '/splitbuttons'
+  defaults:
+    _form: '\Drupal\splitbutton_test\Form\SplitbuttonTestForm'
+    _title: 'Splitbutton Test'
+    disabled: false
+  requirements:
+    _permission: 'access content'
diff --git a/core/modules/system/tests/modules/splitbutton_test/src/Form/SplitbuttonTestForm.php b/core/modules/system/tests/modules/splitbutton_test/src/Form/SplitbuttonTestForm.php
new file mode 100644
index 0000000000..c586bd6e50
--- /dev/null
+++ b/core/modules/system/tests/modules/splitbutton_test/src/Form/SplitbuttonTestForm.php
@@ -0,0 +1,218 @@
+<?php
+
+namespace Drupal\splitbutton_test\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+
+/**
+ * Form for testing splitbuttons.
+ */
+class SplitbuttonTestForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'splitbutton_test_form';
+  }
+
+  /**
+   * Returns a renderable array for a test page.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $disabled = FALSE) {
+    $button_types = [
+      'default' => 'Default',
+      'primary' => 'Primary',
+      'danger' => 'Danger',
+      'small' => 'Small',
+      'extrasmall' => 'Extra Small',
+    ];
+
+    $links_dropbutton = [
+      'link_one' => [
+        'title' => $this->t('Link One'),
+        'url' => Url::fromRoute('splitbutton.test'),
+      ],
+      'link_two' => [
+        'title' => $this->t('Link Two'),
+        'url' => Url::fromRoute('splitbutton.test'),
+      ],
+      'link_three' => [
+        'title' => $this->t('Link Three'),
+        'url' => Url::fromRoute('splitbutton.test'),
+      ],
+      'link_four' => [
+        'title' => $this->t('Link Four'),
+        'url' => Url::fromRoute('splitbutton.test'),
+      ],
+    ];
+
+    $links_plus_button = [
+      'link_one' => [
+        '#type' => 'link',
+        '#title' => $this->t('Link One'),
+        '#url' => Url::fromRoute('splitbutton.test'),
+      ],
+      'link_two' => [
+        '#type' => 'link',
+        '#title' => $this->t('Link Two'),
+        '#url' => Url::fromRoute('splitbutton.test'),
+      ],
+      'link_three' => [
+        '#type' => 'link',
+        '#title' => $this->t('Link Three'),
+        '#url' => Url::fromRoute('splitbutton.test'),
+      ],
+      'link_four' => [
+        '#type' => 'link',
+        '#title' => $this->t('Link Four'),
+        '#url' => Url::fromRoute('splitbutton.test'),
+      ],
+      'added_button' => [
+        '#type' => 'button',
+        '#value' => $this->t('Added Button'),
+      ],
+      'another_added_button' => [
+        '#type' => 'submit',
+        '#value' => $this->t('Another Added Button'),
+      ],
+    ];
+
+    $links_starts_with_button = [
+      'start_button' => [
+        '#type' => 'submit',
+        '#value' => $this->t('Beginning Button'),
+      ],
+    ] + $links_plus_button;
+
+    $scenarios = [
+      'splitbutton_link_first' => [
+        'title' => 'Splitbuttons with link as first item',
+        'element_type' => 'splitbutton',
+        'list' => $links_plus_button,
+      ],
+      'splitbutton_submit_first' => [
+        'title' => 'Splitbuttons with submit as first item',
+        'element_type' => 'splitbutton',
+        'list' => $links_starts_with_button,
+      ],
+      'splitbutton_with_title' => [
+        'title' => 'Splitbuttons where the primary button is just a toggle',
+        'element_type' => 'splitbutton',
+        'list' => $links_plus_button,
+        'splitbutton_title' => 'Toggle Only',
+      ],
+      'dropbutton_converted' => [
+        'title' => 'Dropbuttons converted to Splitbuttons by changing #type',
+        'element_type' => 'splitbutton',
+        'list' => $links_dropbutton,
+        'use_links' => TRUE,
+      ],
+      'dropbuttons' => [
+        'title' => 'Regular Dropbuttons for reference',
+        'element_type' => 'dropbutton',
+        'list' => $links_dropbutton,
+        'use_links' => TRUE,
+      ],
+    ];
+
+    foreach ($scenarios as $scenario_key => $scenario) {
+      $form[$scenario_key] = [
+        '#type' => 'container',
+      ];
+      $form[$scenario_key]['title'] = [
+        '#type' => 'item',
+        '#name' => $scenario_key,
+        '#title' => $scenario['title'],
+      ];
+      foreach ($button_types as $button_type => $button_type_label) {
+        $form[$scenario_key][$button_type] = [
+          '#type' => $scenario['element_type'],
+        ];
+        if (empty($scenario['splitbutton_title'])) {
+          $first_item_key = key($scenario['list']);
+          if (isset($scenario['list'][$first_item_key]['#type'])) {
+            $first_item_type = $scenario['list'][$first_item_key]['#type'] ?? 'link';
+            $label_key = $first_item_type === 'link' ? '#title' : '#value';
+            $scenario['list'][$first_item_key][$label_key] = "$button_type_label - $first_item_type ";
+            if ($first_item_type === 'link') {
+              $scenario['list'][$first_item_key]['#url'] = Url::fromRoute('splitbutton.test', ['prevent_generated_link' => microtime()]);
+            }
+          }
+          else {
+            $scenario['list'][$first_item_key]['title'] = "$button_type_label - link";
+          }
+        }
+        else {
+          $form[$scenario_key][$button_type]['#title'] = "{$scenario['splitbutton_title']} - $button_type_label";
+        }
+
+        if ($scenario['element_type'] === 'splitbutton') {
+          if ($button_type !== 'default') {
+            $form[$scenario_key][$button_type]['#splitbutton_type'] = $button_type;
+          }
+        }
+        else {
+          $form[$scenario_key][$button_type]['#dropbutton_type'] = $button_type;
+        }
+
+        if (!empty($scenario['use_links'])) {
+          $scenario['list'][key($scenario['list'])]['url'] = Url::fromRoute('splitbutton.test', ['prevent_generated_link' => microtime()]);
+          $form[$scenario_key][$button_type]['#links'] = $scenario['list'];
+        }
+        else {
+          $form[$scenario_key][$button_type]['#splitbutton_items'] = $scenario['list'];
+        }
+
+      }
+    }
+
+    $form['combined'] = [
+      '#type' => 'container',
+    ];
+    $form['combined']['title'] = [
+      '#type' => 'item',
+      '#name' => 'combined_types',
+      '#title' => "Combined types",
+    ];
+    $form['combined']['primary_small'] = [
+      '#type' => 'splitbutton',
+      '#splitbutton_type' => [
+        'small',
+        'primary',
+      ],
+      '#splitbutton_items' => [
+        'item1' => [
+          '#type' => 'link',
+          '#title' => $this->t('Small + Primary'),
+          '#url' => Url::fromRoute('splitbutton.test', ['random' => microtime()]),
+        ],
+      ] + $links_plus_button,
+    ];
+    $form['combined']['danger_extrasmall'] = [
+      '#type' => 'splitbutton',
+      '#splitbutton_type' => [
+        'extrasmall',
+        'danger',
+      ],
+      '#splitbutton_items' => [
+        'item1' => [
+          '#type' => 'link',
+          '#title' => $this->t('Extrasmall + danger'),
+          '#url' => Url::fromRoute('splitbutton.test', ['random' => microtime()]),
+        ],
+      ] + $links_plus_button,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+  }
+
+}
diff --git a/core/themes/bartik/bartik.info.yml b/core/themes/bartik/bartik.info.yml
index 7a184637bb..418accaaf1 100644
--- a/core/themes/bartik/bartik.info.yml
+++ b/core/themes/bartik/bartik.info.yml
@@ -30,6 +30,8 @@ libraries-extend:
     - bartik/classy.dropbutton
   core/drupal.progress:
     - bartik/classy.progress
+  core/drupal.splitbutton:
+    - bartik/splitbutton
   file/drupal.file:
     - bartik/classy.file
   filter/drupal.filter.admin:
diff --git a/core/themes/bartik/bartik.libraries.yml b/core/themes/bartik/bartik.libraries.yml
index 820687e001..d2dfec360f 100644
--- a/core/themes/bartik/bartik.libraries.yml
+++ b/core/themes/bartik/bartik.libraries.yml
@@ -87,6 +87,12 @@ maintenance_page:
     - system/maintenance
     - bartik/global-styling
 
+splitbutton:
+  version: VERSION
+  css:
+    component:
+      css/components/splitbutton.css: {}
+
 classy.base:
   version: VERSION
   css:
diff --git a/core/themes/bartik/css/components/splitbutton.css b/core/themes/bartik/css/components/splitbutton.css
new file mode 100644
index 0000000000..dfba4c7fc8
--- /dev/null
+++ b/core/themes/bartik/css/components/splitbutton.css
@@ -0,0 +1,111 @@
+.splitbutton * {
+  box-sizing: border-box;
+}
+
+.splitbutton *:focus {
+  outline: none;
+  box-shadow: 0 0 0 2px #0071b3;
+}
+
+.js .splitbutton__toggle {
+  position: relative;
+  border-radius: 0 20em 20em 0;
+  background-color: #e8e8e8;
+  background-image: -webkit-linear-gradient(top, #e8e8e8, #d2d2d2);
+  background-image: linear-gradient(to bottom, #e8e8e8, #d2d2d2);
+}
+
+.splitbutton__toggle:focus {
+  z-index: 5;
+}
+
+.js .splitbutton__main-button {
+  border-radius: 20em 0 0 20em;
+}
+
+.splitbutton__toggle-arrow {
+  position: absolute;
+  top: 50%;
+  right: 38%;
+  display: block;
+  overflow: hidden;
+  width: 0;
+  height: 0;
+  margin-top: -0.1666em;
+  border-width: 0.3333em 0.3333em 0;
+  border-style: solid;
+  border-right-color: transparent;
+  border-bottom-color: transparent;
+  border-left-color: transparent;
+  line-height: 0;
+}
+.splitbutton.open .splitbutton__toggle-arrow {
+  top: 58%;
+  right: 53%;
+  transform: translate(50%, -50%) rotate(180deg);
+}
+
+.splitbutton__title--toggle {
+  position: relative;
+  padding-right: 2.5em; /* LTR */
+}
+
+.splitbutton__title--toggle::after {
+  position: absolute;
+  top: 52%;
+  right: 14px;
+  width: 0;
+  height: 0;
+  content: "";
+  transform: translate(50%, -50%) rotate(0);
+  border-width: 0.3333em 0.3333em 0;
+  border-style: solid;
+  border-right-color: transparent;
+  border-bottom-color: transparent;
+  border-left-color: transparent;
+}
+.splitbutton.open .splitbutton__title--toggle::after {
+  top: 48%;
+  transform: translate(50%, -50%) rotate(180deg);
+}
+
+.js .splitbutton__operation-list {
+  padding-top: 2px;
+}
+
+.js .splitbutton__operation-list li:first-child {
+  border-radius: 0.5em 0.5em 0 0;
+}
+.js .splitbutton__operation-list li:last-child {
+  border-radius: 0 0 0.5em 0.5em;
+}
+
+.js .splitbutton__operation-list-item {
+  width: 100%;
+  margin: 0;
+  padding: 0.25em 1.063em;
+  text-align: left;
+  color: #3a3a3a;
+  border: 1px solid #a6a6a6;
+  border-top-width: 0;
+  border-bottom-width: 0;
+  border-radius: 0;
+  background-color: #fff;
+  background-image: -webkit-linear-gradient(top, #f3f3f3, #e8e8e8);
+  background-image: linear-gradient(to bottom, #f3f3f3, #e8e8e8);
+  font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, sans-serif;
+  font-size: 0.875rem;
+  font-weight: normal;
+}
+.js .splitbutton__operation-list-item:hover {
+  background: #dedede;
+}
+
+.js .splitbutton__operation-list li:first-child .splitbutton__operation-list-item {
+  border-top-width: 1px;
+  border-radius: 0.5em 0.5em 0 0;
+}
+.js .splitbutton__operation-list li:last-child .splitbutton__operation-list-item {
+  border-bottom-width: 1px;
+  border-radius: 0 0 0.5em 0.5em;
+}
diff --git a/core/themes/claro/claro.info.yml b/core/themes/claro/claro.info.yml
index d04bf1df4d..e94896aa5c 100644
--- a/core/themes/claro/claro.info.yml
+++ b/core/themes/claro/claro.info.yml
@@ -93,6 +93,8 @@ libraries-extend:
     - claro/messages
   core/drupal.progress:
     - claro/progress
+  core/drupal.splitbutton:
+    - claro/splitbutton
   core/drupal.vertical-tabs:
     - claro/vertical-tabs
   core/jquery.ui:
diff --git a/core/themes/claro/claro.libraries.yml b/core/themes/claro/claro.libraries.yml
index dc1972693e..53d4315727 100644
--- a/core/themes/claro/claro.libraries.yml
+++ b/core/themes/claro/claro.libraries.yml
@@ -265,6 +265,12 @@ progress:
     component:
       css/components/progress.css: {}
 
+splitbutton:
+  version: VERSION
+  css:
+    component:
+      css/components/splitbutton.css: {}
+
 filter:
   version: VERSION
   css:
diff --git a/core/themes/claro/claro.theme b/core/themes/claro/claro.theme
index bfd8348d9c..610f9689c5 100644
--- a/core/themes/claro/claro.theme
+++ b/core/themes/claro/claro.theme
@@ -248,6 +248,8 @@ function claro_element_info_alter(&$type) {
   // Add a pre-render function that handles dropbutton variants.
   if (isset($type['dropbutton'])) {
     $type['dropbutton']['#pre_render'][] = [ClaroPreRender::class, 'dropButton'];
+    $type['dropbutton']['#type'] = 'splitbutton';
+    unset($type['dropbutton']['theme']);
   }
 
   if (isset($type['vertical_tabs'])) {
diff --git a/core/themes/claro/css/components/splitbutton.css b/core/themes/claro/css/components/splitbutton.css
new file mode 100644
index 0000000000..097a584858
--- /dev/null
+++ b/core/themes/claro/css/components/splitbutton.css
@@ -0,0 +1,191 @@
+/*
+ * DO NOT EDIT THIS FILE.
+ * See the following change record for more information,
+ * https://www.drupal.org/node/2815083
+ * @preserve
+ */
+
+:root {
+  /*
+   * Color Palette.
+   */
+  /* Secondary. */
+  /* Variations. */ /* 5% darker than base. */ /* 10% darker than base. */ /* 10% darker than base. */ /* 20% darker than base. */ /* 5% darker than base. */ /* 10% darker than base. */ /* 5% darker than base. */ /* 10% darker than base. */ /* 5% darker than base. */ /* 10% darker than base. */
+  /*
+   * Base.
+   */
+  /*
+   * Typography.
+   */ /* 1rem = 16px if font root is 100% ands browser defaults are used. */ /* ~32px */ /* ~29px */ /* ~26px */ /* ~23px */ /* ~20px */ /* 18px */ /* ~14px */ /* ~13px */ /* ~11px */
+  /**
+   * Spaces.
+   */ /* 3 * 16px = 48px */ /* 1.5 * 16px = 24px */ /* 1 * 16px = 16px */ /* 0.75 * 16px = 12px */ /* 0.5 * 16px = 8px */
+  /*
+   * Common.
+   */
+  /*
+   * Inputs.
+   */ /* Absolute zero with opacity. */ /* Davy's grey with 0.6 opacity. */ /* Light gray with 0.3 opacity on white bg. */ /* Old silver with 0.5 opacity on white bg. */ /* (1/8)em ~ 2px */ /* (1/16)em ~ 1px */ /* Font size is too big to use 1rem for extrasmall line-height */ /* 7px inside the form element label. */ /* 8px with the checkbox width of 19px */
+  /*
+   * Details.
+   */
+  /**
+   * Buttons.
+   */
+  /**
+   * jQuery.UI dropdown.
+   */ /* Light gray with 0.8 opacity. */ /* Text color with 0.1 opacity. */
+  /**
+   * Progress bar.
+   */
+  /**
+   * Tabledrag icon size.
+   */ /* 17px */
+  /**
+   * Ajax progress.
+   */
+  /**
+   * Breadcrumb.
+   */
+}
+
+.splitbutton {
+  display: inline-flex;
+}
+
+.splitbutton__main .splitbutton__toggle {
+  margin-left: 1px;
+}
+
+.splitbutton__main .splitbutton__toggle:focus {
+  z-index: 5;
+}
+
+.splitbutton__main .button {
+  margin-top: 0;
+  margin-right: 0;
+  margin-bottom: 0;
+}
+
+.splitbutton .button:not(:focus) {
+  box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.25);
+}
+
+.splitbutton__toggle {
+  position: relative;
+  box-shadow: none;
+}
+
+.splitbutton__toggle::before {
+  position: absolute;
+  top: 50%;
+  right: 50%;
+  width: 0.875rem;
+  height: 0.5625rem;
+  content: "";
+  transform: translate(50%, -50%) rotate(0);
+  background: url("data:image/svg+xml,%3Csvg width='14' height='9' viewBox='0 0 14 9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.2384999,1.9384769 1.646703,0.5166019 7.0002189,5.8193359 12.353735,0.5166019 13.761938,1.9384769 7.0002189,8.635742Z' fill='%23222330'/%3E%3C/svg%3E") no-repeat center;
+  background-size: contain;
+}
+
+.splitbutton.open .splitbutton__toggle::before {
+  transform: translate(50%, -50%) rotate(180deg);
+}
+
+[dir="rtl"] .splitbutton.open .splitbutton__toggle::before {
+  transform: translate(50%, -50%) rotate(-180deg);
+}
+
+.no-touchevents .splitbutton--small .splitbutton__toggle::before,
+.no-touchevents .splitbutton--extrasmall .splitbutton__toggle::before {
+  width: 0.75rem;
+}
+
+.js .splitbutton__operation-list li {
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
+}
+
+.js .splitbutton__operation-list-item {
+  position: relative;
+  display: block;
+  box-sizing: border-box;
+  width: 100%;
+  margin: 0;
+  padding: calc(1rem - 1px);
+  text-align: left;
+  text-decoration: none;
+  color: #545560;
+  border-right: 1px solid #d4d4d8;
+  border-left: 1px solid #d4d4d8;
+  border-radius: 2px;
+  background: #fff;
+  box-shadow: none;
+  font-size: 1rem;
+  font-weight: normal;
+  line-height: 1rem;
+  -webkit-font-smoothing: antialiased;
+}
+
+.js .splitbutton__operation-list-item:hover {
+  color: #222330;
+  background: #f3f4f9;
+}
+
+.js li:first-child .splitbutton__operation-list-item {
+  border-top: 1px solid #d4d4d8;
+}
+
+.js li:last-child .splitbutton__operation-list-item {
+  border-bottom: 1px solid #d4d4d8;
+}
+
+.splitbutton--primary .splitbutton__toggle::before,
+.splitbutton--danger .splitbutton__toggle::before {
+  background: url("data:image/svg+xml,%3Csvg width='14' height='9' fill='%23FF00FF' viewBox='0 0 14 9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.2384999,1.9384769 1.646703,0.5166019 7.0002189,5.8193359 12.353735,0.5166019 13.761938,1.9384769 7.0002189,8.635742Z' fill='white'/%3E%3C/svg%3E") no-repeat center;
+}
+
+.splitbutton--small .splitbutton__operation-list-item {
+  padding-top: calc(0.625rem - 1px);
+  padding-bottom: calc(0.625rem - 1px);
+  font-size: 0.79rem;
+  line-height: 0.75rem;
+}
+
+.splitbutton--extrasmall .splitbutton__operation-list-item {
+  padding-top: calc(0.375rem - 1px);
+  padding-bottom: calc(0.375rem - 1px);
+  font-size: 0.79rem;
+  line-height: 0.75rem;
+}
+
+.splitbutton__title--toggle {
+  position: relative;
+  padding-right: 3rem; /* LTR */
+}
+
+.splitbutton__title--toggle::after {
+  position: absolute;
+  top: 50%;
+  right: 1.25rem;
+  width: 0.875rem;
+  height: 0.5625rem;
+  content: "";
+  transform: translate(50%, -50%) rotate(0);
+  background: url("data:image/svg+xml,%3Csvg width='14' height='9' viewBox='0 0 14 9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.2384999,1.9384769 1.646703,0.5166019 7.0002189,5.8193359 12.353735,0.5166019 13.761938,1.9384769 7.0002189,8.635742Z' fill='%23222330'/%3E%3C/svg%3E") no-repeat center;
+  background-size: contain;
+}
+
+.splitbutton.open .splitbutton__title--toggle::after {
+  top: 48%;
+  transform: translate(50%, -50%) rotate(180deg);
+}
+
+.splitbutton__title--toggle.button--primary::after,
+.splitbutton__title--toggle.button--danger::after {
+  background: url("data:image/svg+xml,%3Csvg width='14' height='9' fill='%23FF00FF' viewBox='0 0 14 9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.2384999,1.9384769 1.646703,0.5166019 7.0002189,5.8193359 12.353735,0.5166019 13.761938,1.9384769 7.0002189,8.635742Z' fill='white'/%3E%3C/svg%3E") no-repeat center;
+}
+
+.splitbutton__title--toggle.button--small,
+.splitbutton__title--toggle.button--extrasmall {
+  padding-right: 2.5rem;
+}
diff --git a/core/themes/claro/css/components/splitbutton.pcss.css b/core/themes/claro/css/components/splitbutton.pcss.css
new file mode 100644
index 0000000000..62c79f38e2
--- /dev/null
+++ b/core/themes/claro/css/components/splitbutton.pcss.css
@@ -0,0 +1,135 @@
+@import "../base/variables.pcss.css";
+
+.splitbutton {
+  display: inline-flex;
+}
+
+.splitbutton__main .splitbutton__toggle {
+  margin-left: 1px;
+}
+
+.splitbutton__main .splitbutton__toggle:focus {
+  z-index: 5;
+}
+
+.splitbutton__main .button {
+  margin-top: 0;
+  margin-right: 0;
+  margin-bottom: 0;
+}
+
+.splitbutton .button:not(:focus) {
+  box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.25);
+}
+
+.splitbutton__toggle {
+  position: relative;
+  box-shadow: none;
+}
+.splitbutton__toggle::before {
+  position: absolute;
+  top: 50%;
+  right: 50%;
+  width: 0.875rem;
+  height: 0.5625rem;
+  content: "";
+  transform: translate(50%, -50%) rotate(0);
+  background: url("data:image/svg+xml,%3Csvg width='14' height='9' viewBox='0 0 14 9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.2384999,1.9384769 1.646703,0.5166019 7.0002189,5.8193359 12.353735,0.5166019 13.761938,1.9384769 7.0002189,8.635742Z' fill='%23222330'/%3E%3C/svg%3E") no-repeat center;
+  background-size: contain;
+}
+.splitbutton.open .splitbutton__toggle::before {
+  transform: translate(50%, -50%) rotate(180deg);
+}
+[dir="rtl"] .splitbutton.open .splitbutton__toggle::before {
+  transform: translate(50%, -50%) rotate(-180deg);
+}
+
+.no-touchevents .splitbutton--small .splitbutton__toggle::before,
+.no-touchevents .splitbutton--extrasmall .splitbutton__toggle::before {
+  width: 0.75rem;
+}
+
+.js .splitbutton__operation-list li {
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
+}
+
+.js .splitbutton__operation-list-item {
+  position: relative;
+  display: block;
+  box-sizing: border-box;
+  width: 100%;
+  margin: 0;
+  padding: calc(1rem - 1px);
+  text-align: left;
+  text-decoration: none;
+  color: #545560;
+  border-right: 1px solid var(--color-lightgray);
+  border-left: 1px solid var(--color-lightgray);
+  border-radius: 2px;
+  background: #fff;
+  box-shadow: none;
+  font-size: 1rem;
+  font-weight: normal;
+  line-height: 1rem;
+  -webkit-font-smoothing: antialiased;
+}
+.js .splitbutton__operation-list-item:hover {
+  color: #222330;
+  background: #f3f4f9;
+}
+.js li:first-child .splitbutton__operation-list-item {
+  border-top: 1px solid var(--color-lightgray);
+}
+.js li:last-child .splitbutton__operation-list-item {
+  border-bottom: 1px solid var(--color-lightgray);
+}
+
+.splitbutton--primary .splitbutton__toggle::before,
+.splitbutton--danger .splitbutton__toggle::before {
+  background: url("data:image/svg+xml,%3Csvg width='14' height='9' fill='%23FF00FF' viewBox='0 0 14 9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.2384999,1.9384769 1.646703,0.5166019 7.0002189,5.8193359 12.353735,0.5166019 13.761938,1.9384769 7.0002189,8.635742Z' fill='white'/%3E%3C/svg%3E") no-repeat center;
+}
+
+.splitbutton--small .splitbutton__operation-list-item {
+  padding-top: calc(0.625rem - 1px);
+  padding-bottom: calc(0.625rem - 1px);
+  font-size: var(--font-size-xs);
+  line-height: 0.75rem;
+}
+.splitbutton--extrasmall .splitbutton__operation-list-item {
+  padding-top: calc(0.375rem - 1px);
+  padding-bottom: calc(0.375rem - 1px);
+  font-size: var(--font-size-xs);
+  line-height: 0.75rem;
+}
+
+.splitbutton__title--toggle {
+  position: relative;
+  padding-right: 3rem; /* LTR */
+}
+
+.splitbutton__title--toggle::after {
+  position: absolute;
+  top: 50%;
+  right: 1.25rem;
+  width: 0.875rem;
+  height: 0.5625rem;
+  content: "";
+  transform: translate(50%, -50%) rotate(0);
+  background: url("data:image/svg+xml,%3Csvg width='14' height='9' viewBox='0 0 14 9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.2384999,1.9384769 1.646703,0.5166019 7.0002189,5.8193359 12.353735,0.5166019 13.761938,1.9384769 7.0002189,8.635742Z' fill='%23222330'/%3E%3C/svg%3E") no-repeat center;
+  background-size: contain;
+}
+
+.splitbutton.open .splitbutton__title--toggle::after {
+  top: 48%;
+  transform: translate(50%, -50%) rotate(180deg);
+}
+
+.splitbutton__title--toggle.button--primary::after,
+.splitbutton__title--toggle.button--danger::after {
+  background: url("data:image/svg+xml,%3Csvg width='14' height='9' fill='%23FF00FF' viewBox='0 0 14 9' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.2384999,1.9384769 1.646703,0.5166019 7.0002189,5.8193359 12.353735,0.5166019 13.761938,1.9384769 7.0002189,8.635742Z' fill='white'/%3E%3C/svg%3E") no-repeat center;
+}
+
+.splitbutton__title--toggle.button--small,
+.splitbutton__title--toggle.button--extrasmall {
+  padding-right: 2.5rem;
+}
diff --git a/core/themes/seven/css/components/splitbutton.css b/core/themes/seven/css/components/splitbutton.css
new file mode 100644
index 0000000000..b6554f8c5e
--- /dev/null
+++ b/core/themes/seven/css/components/splitbutton.css
@@ -0,0 +1,135 @@
+.splitbutton * {
+  box-sizing: border-box;
+}
+
+.splitbutton__toggle {
+  position: relative;
+  padding: 0 14px;
+  border: 1px solid #a6a6a6;
+  border-left-width: 0; /* LTR */
+  border-radius: 0 20em 20em 0;
+}
+
+.splitbutton__toggle:focus {
+  z-index: 5;
+}
+
+.splitbutton__toggle.button {
+  border-radius: 0 20em 20em 0;
+}
+
+.splitbutton__toggle-arrow {
+  position: absolute;
+  top: 50%;
+  right: 38%;
+  display: block;
+  overflow: hidden;
+  width: 0;
+  height: 0;
+  margin-top: -0.1666em;
+  border-width: 0.3333em 0.3333em 0;
+  border-style: solid;
+  border-right-color: transparent;
+  border-bottom-color: transparent;
+  border-left-color: transparent;
+  line-height: 0;
+}
+.splitbutton__title--toggle {
+  position: relative;
+  padding-right: 2.5em; /* LTR */
+}
+
+.splitbutton__title--toggle.button--small {
+  padding-right: 2em;
+}
+
+.splitbutton__title--toggle::after {
+  position: absolute;
+  top: 52%;
+  right: 14px;
+  width: 0;
+  height: 0;
+  content: "";
+  transform: translate(50%, -50%) rotate(0);
+  border-width: 0.3333em 0.3333em 0;
+  border-style: solid;
+  border-right-color: transparent;
+  border-bottom-color: transparent;
+  border-left-color: transparent;
+}
+
+.splitbutton.open .splitbutton__title--toggle::after {
+  top: 48%;
+  transform: translate(50%, -50%) rotate(180deg);
+}
+
+.splitbutton__main-button.button {
+  border-radius: 20em 0 0 20em;
+}
+
+.js .splitbutton__operation-list {
+  overflow: hidden;
+  padding-top: 2px;
+}
+.js .splitbutton__operation-list li:first-child {
+  border-radius: 0.5em 0.5em 0 0;
+}
+.js .splitbutton__operation-list li:last-child {
+  border-radius: 0 0 0.5em 0.5em;
+}
+
+.js .splitbutton__operation-list-item {
+  width: 100%;
+  margin: 0;
+  padding: 4px 10px;
+  text-align: left;
+  color: #333;
+  border: 1px solid #a6a6a6;
+  border-top-width: 0;
+  border-bottom-width: 0;
+  border-radius: 0;
+  background: #fff;
+  font-size: 0.875rem;
+  font-weight: normal;
+}
+
+.js .splitbutton__operation-list-item:hover {
+  background: #aae0fe;
+  text-shadow: 0.25px 0 0.1px, -0.25px 0 0.1px;
+}
+
+.js .splitbutton__operation-list li:first-child .splitbutton__operation-list-item {
+  border-top-width: 1px;
+  border-radius: 0.5em 0.5em 0 0;
+}
+
+.js .splitbutton__operation-list li:last-child .splitbutton__operation-list-item {
+  border-bottom-width: 1px;
+  border-radius: 0 0 0.5em 0.5em;
+}
+
+.js .splitbutton .splitbutton__main-button.button--danger {
+  border-radius: 20em 0 0 20em;
+}
+
+.js .splitbutton .button--danger {
+  padding: 4px 1.5em;
+  text-decoration: none;
+  border: 1px solid #a6a6a6;
+  border-radius: 20em;
+}
+
+.js .splitbutton .splitbutton__toggle.button--danger {
+  padding: 0 14px;
+  border-left-width: 0;
+  border-radius: 0 20em 20em 0;
+}
+
+.js .splitbutton__title--toggle.button--danger {
+  padding-right: 2.5em;
+}
+
+.splitbutton--primary .splitbutton__toggle {
+  border-color: #1e5c90;
+  background-color: #0071b8;
+}
diff --git a/core/themes/seven/seven.info.yml b/core/themes/seven/seven.info.yml
index 44f08011a4..66e546e3cf 100644
--- a/core/themes/seven/seven.info.yml
+++ b/core/themes/seven/seven.info.yml
@@ -62,8 +62,8 @@ libraries-extend:
     - seven/filter
   filter/drupal.filter:
     - seven/filter
-  media/media_embed_ckeditor_theme:
-    - seven/classy.media_embed_ckeditor_theme
+  core/drupal.splitbutton:
+    - seven/splitbutton
   media_library/view:
     - seven/media_library
   media_library/widget:
diff --git a/core/themes/seven/seven.libraries.yml b/core/themes/seven/seven.libraries.yml
index 060c000296..ddfed2079e 100644
--- a/core/themes/seven/seven.libraries.yml
+++ b/core/themes/seven/seven.libraries.yml
@@ -181,6 +181,12 @@ filter:
     component:
       css/theme/filter.admin.css: {}
 
+splitbutton:
+  version: VERSION
+  css:
+    component:
+      css/components/splitbutton.css: {}
+
 classy.book-navigation:
   version: VERSION
   css:
