Open demo

CSS code

HTML
<div class="split">
  <button class="split-main" type="button">Publish</button>
  <details class="split-more">
    <summary aria-label="More publish options"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></summary>
    <div class="split-menu">
      <button type="button">Publish and notify subscribers</button>
      <button type="button">Schedule for later…</button>
      <hr>
      <button type="button">Save as draft</button>
    </div>
  </details>
</div>

<div class="split split-ghost">
  <button class="split-main" type="button">Export CSV</button>
  <details class="split-more">
    <summary aria-label="More export options"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></summary>
    <div class="split-menu">
      <button type="button">Export as JSON</button>
      <button type="button">Export as XLSX</button>
      <hr>
      <button type="button">Copy to clipboard</button>
    </div>
  </details>
</div>
CSS
/* Split button: a primary action <button> plus a native <details> disclosure holding the secondary actions. */
.split{display:inline-flex;align-items:stretch}
.split-main{border:0;border-radius:10px 0 0 10px;padding:12px 20px;font:inherit;font-size:.9rem;font-weight:700;
  line-height:normal;background:#4f46e5;color:#fff;cursor:pointer;transition:background-color .18s}
.split-main:hover{background:#4338ca}
.split-more{position:relative}
.split-more summary{list-style:none;height:100%;width:40px;display:grid;place-items:center;cursor:pointer;
  border-radius:0 10px 10px 0;border-left:1px solid rgba(255,255,255,.22);background:#4338ca;color:#fff;
  transition:background-color .18s}
.split-more summary::-webkit-details-marker{display:none}
.split-more summary:hover{background:#3730a3}
.split-more summary svg{display:block;width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2.4;
  transition:transform .2s}
.split-more[open] summary svg{transform:rotate(180deg)}
.split-main:focus-visible,.split-more summary:focus-visible{outline:2px solid #4f46e5;outline-offset:3px}
.split-menu{position:absolute;top:calc(100% + 7px);right:0;z-index:5;min-width:210px;padding:6px;
  background:#fff;border:1px solid #e4e7ec;border-radius:11px;box-shadow:0 16px 38px -14px rgba(16,24,40,.28)}
.split-menu button{display:block;width:100%;border:0;border-radius:8px;padding:9px 12px;background:none;
  font:inherit;font-size:.87rem;line-height:normal;text-align:left;white-space:nowrap;color:#344054;cursor:pointer}
.split-menu button:hover{background:#f4f5f9;color:#101828}
.split-menu button:focus-visible{outline:2px solid #4f46e5;outline-offset:-2px}
.split-menu hr{border:0;border-top:1px solid #f0f2f6;margin:5px 0}
/* outline variant: the border is darkened to 3:1 against the page */
.split-ghost .split-main{background:#fff;color:#344054;border:1.5px solid #838b9b;border-right:0}
.split-ghost .split-main:hover{background:#fafbfd}
.split-ghost summary{background:#fff;color:#475467;border:1.5px solid #838b9b;border-left:1px solid #d0d5dd}
.split-ghost summary:hover{background:#f4f5f9}
@media (prefers-reduced-motion:reduce){.split-main,.split-more summary,.split-more summary svg{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div class="btn-group split">
  <button class="btn split-main" type="button">Publish</button>
  <details class="split-more">
    <summary class="btn" aria-label="More publish options"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></summary>
    <div class="dropdown-menu">
      <button class="dropdown-item" type="button">Publish and notify subscribers</button>
      <button class="dropdown-item" type="button">Schedule for later…</button>
      <hr class="dropdown-divider">
      <button class="dropdown-item" type="button">Save as draft</button>
    </div>
  </details>
</div>

<div class="btn-group split split-ghost">
  <button class="btn split-main" type="button">Export CSV</button>
  <details class="split-more">
    <summary class="btn" aria-label="More export options"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></summary>
    <div class="dropdown-menu">
      <button class="dropdown-item" type="button">Export as JSON</button>
      <button class="dropdown-item" type="button">Export as XLSX</button>
      <hr class="dropdown-divider">
      <button class="dropdown-item" type="button">Copy to clipboard</button>
    </div>
  </details>
</div>
CSS
/* Split button: a .btn-group of a .btn and a native <details> whose <summary> is the toggle and whose
   .dropdown-menu is shown while the details element is open, so no Bootstrap JavaScript is needed. */
.split .btn{--bs-btn-font-size:.9rem;--bs-btn-font-weight:700;--bs-btn-line-height:normal;--bs-btn-border-width:0;
  --bs-btn-color:#fff;--bs-btn-bg:#4f46e5;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#4338ca;
  --bs-btn-active-color:#fff;--bs-btn-active-bg:#4338ca;
  --bs-btn-focus-box-shadow:0 0 0 3px #f4f5f9,0 0 0 5px #4f46e5;transition:background-color .18s}
.split .split-main{--bs-btn-padding-x:20px;--bs-btn-padding-y:12px;border-radius:10px 0 0 10px}
.split-more{position:relative}
.split-more summary.btn{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-bg:#4338ca;--bs-btn-hover-bg:#3730a3;
  --bs-btn-active-bg:#3730a3;list-style:none;height:100%;width:40px;display:grid;place-items:center;
  border-radius:0 10px 10px 0;border-left:1px solid rgba(255,255,255,.22)}
.split-more summary::-webkit-details-marker{display:none}
.split-more summary svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2.4;transition:transform .2s}
.split-more[open] summary svg{transform:rotate(180deg)}
.split .dropdown-menu{--bs-dropdown-min-width:210px;--bs-dropdown-padding-x:6px;--bs-dropdown-padding-y:6px;
  --bs-dropdown-font-size:.87rem;--bs-dropdown-border-color:#e4e7ec;--bs-dropdown-border-radius:11px;
  --bs-dropdown-link-color:#344054;--bs-dropdown-link-hover-color:#101828;--bs-dropdown-link-hover-bg:#f4f5f9;
  --bs-dropdown-link-active-color:#101828;--bs-dropdown-link-active-bg:#eef0f4;
  --bs-dropdown-item-padding-x:12px;--bs-dropdown-item-padding-y:9px;--bs-dropdown-item-border-radius:8px;
  --bs-dropdown-divider-bg:#f0f2f6;--bs-dropdown-divider-margin-y:5px;
  top:calc(100% + 7px);right:0;left:auto;box-shadow:0 16px 38px -14px rgba(16,24,40,.28)}
.split-more[open] .dropdown-menu{display:block}
.split .dropdown-item{line-height:normal}
.split .dropdown-item:focus-visible{outline:2px solid #4f46e5;outline-offset:-2px}
.split .dropdown-divider{opacity:1}
/* outline variant: the border is darkened to 3:1 against the page */
.split-ghost .btn{--bs-btn-color:#344054;--bs-btn-bg:#fff;--bs-btn-hover-color:#344054;--bs-btn-hover-bg:#fafbfd;
  --bs-btn-active-color:#344054;--bs-btn-active-bg:#fafbfd}
.split-ghost .split-main{border:1.5px solid #838b9b;border-right:0}
.split-ghost .split-more summary.btn{--bs-btn-color:#475467;--bs-btn-bg:#fff;--bs-btn-hover-color:#475467;
  --bs-btn-hover-bg:#f4f5f9;--bs-btn-active-bg:#f4f5f9;border:1.5px solid #838b9b;border-left:1px solid #d0d5dd}
@media (prefers-reduced-motion:reduce){.split .btn,.split-more summary svg{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div class="inline-flex items-stretch">
  <button type="button" class="cursor-pointer rounded-l-[10px] bg-[#4f46e5] px-5 py-3 text-[.9rem] font-bold text-white transition-[background-color] duration-[180ms] hover:bg-[#4338ca] focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-[#4f46e5] motion-reduce:transition-none">Publish</button>
  <details class="group relative">
    <summary aria-label="More publish options" class="grid h-full w-10 cursor-pointer list-none place-items-center rounded-r-[10px] border-l border-white/22 bg-[#4338ca] text-white transition-[background-color] duration-[180ms] hover:bg-[#3730a3] focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-[#4f46e5] motion-reduce:transition-none [&::-webkit-details-marker]:hidden"><svg viewBox="0 0 24 24" aria-hidden="true" class="block size-[15px] fill-none stroke-current stroke-[2.4] transition-transform duration-200 group-open:rotate-180 motion-reduce:transition-none"><path d="M6 9l6 6 6-6"/></svg></summary>
    <div class="absolute top-[calc(100%+7px)] right-0 z-[5] min-w-[210px] rounded-[11px] border border-[#e4e7ec] bg-white p-1.5 shadow-[0_16px_38px_-14px_rgba(16,24,40,.28)]">
      <button type="button" class="block w-full cursor-pointer rounded-lg px-3 whitespace-nowrap py-[9px] text-left text-[.87rem] text-[#344054] hover:bg-[#f4f5f9] hover:text-[#101828] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#4f46e5]">Publish and notify subscribers</button>
      <button type="button" class="block w-full cursor-pointer rounded-lg px-3 whitespace-nowrap py-[9px] text-left text-[.87rem] text-[#344054] hover:bg-[#f4f5f9] hover:text-[#101828] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#4f46e5]">Schedule for later…</button>
      <hr class="my-[5px] border-0 border-t border-[#f0f2f6]">
      <button type="button" class="block w-full cursor-pointer rounded-lg px-3 whitespace-nowrap py-[9px] text-left text-[.87rem] text-[#344054] hover:bg-[#f4f5f9] hover:text-[#101828] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#4f46e5]">Save as draft</button>
    </div>
  </details>
</div>

<div class="inline-flex items-stretch">
  <button type="button" class="cursor-pointer rounded-l-[10px] border-[1.5px] border-r-0 border-[#838b9b] bg-white px-5 py-3 text-[.9rem] font-bold text-[#344054] transition-[background-color] duration-[180ms] hover:bg-[#fafbfd] focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-[#4f46e5] motion-reduce:transition-none">Export CSV</button>
  <details class="group relative">
    <summary aria-label="More export options" class="grid h-full w-10 cursor-pointer list-none place-items-center rounded-r-[10px] border-[1.5px] border-l border-[#838b9b] border-l-[#d0d5dd] bg-white text-[#475467] transition-[background-color] duration-[180ms] hover:bg-[#f4f5f9] focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-[#4f46e5] motion-reduce:transition-none [&::-webkit-details-marker]:hidden"><svg viewBox="0 0 24 24" aria-hidden="true" class="block size-[15px] fill-none stroke-current stroke-[2.4] transition-transform duration-200 group-open:rotate-180 motion-reduce:transition-none"><path d="M6 9l6 6 6-6"/></svg></summary>
    <div class="absolute top-[calc(100%+7px)] right-0 z-[5] min-w-[210px] rounded-[11px] border border-[#e4e7ec] bg-white p-1.5 shadow-[0_16px_38px_-14px_rgba(16,24,40,.28)]">
      <button type="button" class="block w-full cursor-pointer rounded-lg px-3 whitespace-nowrap py-[9px] text-left text-[.87rem] text-[#344054] hover:bg-[#f4f5f9] hover:text-[#101828] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#4f46e5]">Export as JSON</button>
      <button type="button" class="block w-full cursor-pointer rounded-lg px-3 whitespace-nowrap py-[9px] text-left text-[.87rem] text-[#344054] hover:bg-[#f4f5f9] hover:text-[#101828] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#4f46e5]">Export as XLSX</button>
      <hr class="my-[5px] border-0 border-t border-[#f0f2f6]">
      <button type="button" class="block w-full cursor-pointer rounded-lg px-3 whitespace-nowrap py-[9px] text-left text-[.87rem] text-[#344054] hover:bg-[#f4f5f9] hover:text-[#101828] focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[#4f46e5]">Copy to clipboard</button>
    </div>
  </details>
</div>

About this set

Two split buttons that pair a main action with a short list of related ones. The solid indigo button publishes, and its chevron opens Publish and notify subscribers, Schedule for later and Save as draft. The outlined button exports a CSV, and its chevron offers JSON, XLSX and Copy to clipboard. Use a split button when one action is the obvious default but a few variants of it should stay one click away. The main action is an ordinary button. The chevron is the summary of a native details element, so opening and closing the menu needs no JavaScript: click, Enter or Space toggles it, and the chevron turns over while the menu is open. The menu holds plain buttons separated by a divider, positioned under the right edge of the split. The outlined variant has its border darkened to meet 3:1 against the page. The Bootstrap version is a .btn-group whose details element shows a stock .dropdown-menu with .dropdown-item buttons while it is open, so it does not need the Bootstrap JavaScript bundle, and the Tailwind version uses the group-open variant for the chevron.

What’s included

  • Solid and outlined split buttons
  • Menu opens from a native details and summary pair, no JavaScript
  • Chevron rotates while the menu is open
  • Menu items are real buttons with a divider
  • Outline border darkened to 3:1 against the page

Accessibility

The main action is a button with visible text, and the chevron summary has an aria-label such as More publish options; browsers expose it as expanded or collapsed. Tab reaches the main button, the chevron and, once open, each menu item, and Enter or Space opens and closes the menu. Every control shows a 2px indigo focus outline. The chevron rotation and colour transitions are removed under prefers-reduced-motion. Because it is a disclosure, the menu does not close on Escape or on an outside click.

Customise it

Change #4f46e5, #4338ca and #3730a3 in the .split-main and summary rules for the solid colour, and #838b9b on .split-ghost for the outline. In Bootstrap the same colours are --bs-btn-bg and --bs-btn-hover-bg on .split .btn and the menu uses the --bs-dropdown-* variables; in Tailwind edit the bg- and border- utilities.