Open demo

CSS code

HTML
<button class="xp xp-new" type="button"><span class="xp-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></span><span class="xp-lbl"><span>New project</span></span></button>
<button class="xp xp-dark" type="button"><span class="xp-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v11M7 11l5 5 5-5M5 20h14"/></svg></span><span class="xp-lbl"><span>Download</span></span></button>
<button class="xp xp-danger" type="button"><span class="xp-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 7h14M9 7V5h6v2M7 7l1 13h8l1-13"/></svg></span><span class="xp-lbl"><span>Delete</span></span></button>
<button class="xp xp-ok" type="button"><span class="xp-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg></span><span class="xp-lbl"><span>Approve</span></span></button>
CSS
/* Expanding icon buttons: the label sits in a grid track that animates from 0fr to 1fr on hover and focus.
   The label text is always in the accessible name, even while it is collapsed. */
.xp{display:inline-flex;align-items:center;height:48px;padding:0;border:0;border-radius:999px;overflow:hidden;
  font:inherit;font-size:.9rem;font-weight:700;line-height:normal;cursor:pointer;transition:background-color .2s,box-shadow .2s}
.xp:focus-visible{outline:2px solid #4f46e5;outline-offset:3px}
.xp-ic{width:48px;height:48px;display:grid;place-items:center;flex-shrink:0}
.xp svg{display:block;width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:2.2}
.xp-lbl{display:grid;grid-template-columns:0fr;transition:grid-template-columns .28s cubic-bezier(.4,0,.2,1)}
.xp-lbl span{overflow:hidden;white-space:nowrap}
.xp:hover .xp-lbl,.xp:focus-visible .xp-lbl{grid-template-columns:1fr}
.xp:hover .xp-lbl span,.xp:focus-visible .xp-lbl span{padding-right:22px}
.xp-new{background:#4f46e5;color:#fff}
.xp-new:hover{background:#4338ca}
.xp-dark{background:#101828;color:#fff}
.xp-dark:hover{background:#344054}
.xp-danger{background:#fff;color:#344054;box-shadow:inset 0 0 0 1.5px #838b9b}
.xp-danger:hover{color:#b91c1c;box-shadow:inset 0 0 0 1.5px #ef4444}
.xp-ok{background:#dcfce7;color:#15803d}
.xp-ok:hover{background:#bbf7d0}
@media (prefers-reduced-motion:reduce){.xp,.xp-lbl{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<button class="btn rounded-pill xp xp-new" type="button"><span class="xp-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></span><span class="xp-lbl"><span>New project</span></span></button>
<button class="btn rounded-pill xp xp-dark" type="button"><span class="xp-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v11M7 11l5 5 5-5M5 20h14"/></svg></span><span class="xp-lbl"><span>Download</span></span></button>
<button class="btn rounded-pill xp xp-danger" type="button"><span class="xp-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 7h14M9 7V5h6v2M7 7l1 13h8l1-13"/></svg></span><span class="xp-lbl"><span>Delete</span></span></button>
<button class="btn rounded-pill xp xp-ok" type="button"><span class="xp-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 13l4 4L19 7"/></svg></span><span class="xp-lbl"><span>Approve</span></span></button>
CSS
/* Expanding icon buttons: a .btn.rounded-pill restyled through --bs-btn-* variables; the label sits in a grid
   track that animates from 0fr to 1fr on hover and focus, and is always in the accessible name. */
.xp{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-font-size:.9rem;--bs-btn-font-weight:700;
  --bs-btn-line-height:normal;--bs-btn-border-width:0;
  --bs-btn-focus-box-shadow:0 0 0 3px #f4f5f9,0 0 0 5px #4f46e5;
  display:inline-flex;align-items:center;height:48px;overflow:hidden;transition:background-color .2s,box-shadow .2s}
.xp-ic{width:48px;height:48px;display:grid;place-items:center;flex-shrink:0}
.xp svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:2.2}
.xp-lbl{display:grid;grid-template-columns:0fr;transition:grid-template-columns .28s cubic-bezier(.4,0,.2,1)}
.xp-lbl span{overflow:hidden;white-space:nowrap}
.xp:hover .xp-lbl,.xp:focus-visible .xp-lbl{grid-template-columns:1fr}
.xp:hover .xp-lbl span,.xp:focus-visible .xp-lbl span{padding-right:22px}
.xp-new{--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}
.xp-dark{--bs-btn-color:#fff;--bs-btn-bg:#101828;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#344054;
  --bs-btn-active-color:#fff;--bs-btn-active-bg:#344054}
.xp-danger{--bs-btn-color:#344054;--bs-btn-bg:#fff;--bs-btn-hover-color:#b91c1c;--bs-btn-hover-bg:#fff;
  --bs-btn-active-color:#b91c1c;--bs-btn-active-bg:#fff;box-shadow:inset 0 0 0 1.5px #838b9b}
.xp-danger:hover{box-shadow:inset 0 0 0 1.5px #ef4444}
.xp-danger:focus-visible{box-shadow:inset 0 0 0 1.5px #838b9b,var(--bs-btn-focus-box-shadow)}
.xp-ok{--bs-btn-color:#15803d;--bs-btn-bg:#dcfce7;--bs-btn-hover-color:#15803d;--bs-btn-hover-bg:#bbf7d0;
  --bs-btn-active-color:#15803d;--bs-btn-active-bg:#bbf7d0}
@media (prefers-reduced-motion:reduce){.xp,.xp-lbl{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<button class="group inline-flex h-12 cursor-pointer items-center overflow-hidden rounded-full text-[.9rem] font-bold transition-[background-color,box-shadow] duration-200 focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-[#4f46e5] motion-reduce:transition-none bg-[#4f46e5] text-white hover:bg-[#4338ca]" type="button"><span class="grid size-12 shrink-0 place-items-center"><svg viewBox="0 0 24 24" aria-hidden="true" class="block size-[19px] fill-none stroke-current stroke-[2.2]"><path d="M12 5v14M5 12h14"/></svg></span><span class="grid grid-cols-[0fr] transition-[grid-template-columns] duration-[280ms] ease-[cubic-bezier(.4,0,.2,1)] group-hover:grid-cols-[1fr] group-focus-visible:grid-cols-[1fr] motion-reduce:transition-none"><span class="overflow-hidden whitespace-nowrap group-hover:pr-[22px] group-focus-visible:pr-[22px]">New project</span></span></button>
<button class="group inline-flex h-12 cursor-pointer items-center overflow-hidden rounded-full text-[.9rem] font-bold transition-[background-color,box-shadow] duration-200 focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-[#4f46e5] motion-reduce:transition-none bg-[#101828] text-white hover:bg-[#344054]" type="button"><span class="grid size-12 shrink-0 place-items-center"><svg viewBox="0 0 24 24" aria-hidden="true" class="block size-[19px] fill-none stroke-current stroke-[2.2]"><path d="M12 4v11M7 11l5 5 5-5M5 20h14"/></svg></span><span class="grid grid-cols-[0fr] transition-[grid-template-columns] duration-[280ms] ease-[cubic-bezier(.4,0,.2,1)] group-hover:grid-cols-[1fr] group-focus-visible:grid-cols-[1fr] motion-reduce:transition-none"><span class="overflow-hidden whitespace-nowrap group-hover:pr-[22px] group-focus-visible:pr-[22px]">Download</span></span></button>
<button class="group inline-flex h-12 cursor-pointer items-center overflow-hidden rounded-full text-[.9rem] font-bold transition-[background-color,box-shadow] duration-200 focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-[#4f46e5] motion-reduce:transition-none bg-white text-[#344054] shadow-[inset_0_0_0_1.5px_#838b9b] hover:text-[#b91c1c] hover:shadow-[inset_0_0_0_1.5px_#ef4444]" type="button"><span class="grid size-12 shrink-0 place-items-center"><svg viewBox="0 0 24 24" aria-hidden="true" class="block size-[19px] fill-none stroke-current stroke-[2.2]"><path d="M5 7h14M9 7V5h6v2M7 7l1 13h8l1-13"/></svg></span><span class="grid grid-cols-[0fr] transition-[grid-template-columns] duration-[280ms] ease-[cubic-bezier(.4,0,.2,1)] group-hover:grid-cols-[1fr] group-focus-visible:grid-cols-[1fr] motion-reduce:transition-none"><span class="overflow-hidden whitespace-nowrap group-hover:pr-[22px] group-focus-visible:pr-[22px]">Delete</span></span></button>
<button class="group inline-flex h-12 cursor-pointer items-center overflow-hidden rounded-full text-[.9rem] font-bold transition-[background-color,box-shadow] duration-200 focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-[#4f46e5] motion-reduce:transition-none bg-[#dcfce7] text-[#15803d] hover:bg-[#bbf7d0]" type="button"><span class="grid size-12 shrink-0 place-items-center"><svg viewBox="0 0 24 24" aria-hidden="true" class="block size-[19px] fill-none stroke-current stroke-[2.2]"><path d="M5 13l4 4L19 7"/></svg></span><span class="grid grid-cols-[0fr] transition-[grid-template-columns] duration-[280ms] ease-[cubic-bezier(.4,0,.2,1)] group-hover:grid-cols-[1fr] group-focus-visible:grid-cols-[1fr] motion-reduce:transition-none"><span class="overflow-hidden whitespace-nowrap group-hover:pr-[22px] group-focus-visible:pr-[22px]">Approve</span></span></button>

About this set

Four round 48 pixel icon buttons that widen into pills to reveal their label on hover or keyboard focus: New project in indigo, Download in near-black, Delete in white with a grey border that turns red, and Approve in pale green. Use them in toolbars, floating action areas and table rows where space is tight but a label should still be available on demand. The label sits in a grid track whose grid-template-columns animates from 0fr to 1fr, so the button grows to exactly the width of its text without a hard-coded max-width. The collapsed text is clipped rather than hidden, so it stays in the accessible name and screen readers always announce New project, Download, Delete or Approve. The Delete button’s border was darkened from pale grey to reach 3:1 against the page, and its hover border is a stronger red. Touch screens have no reliable hover, so the label may never show there; pick icons that are clear on their own. The Bootstrap version is a .btn.rounded-pill restyled through the –bs-btn-* variables, and the Tailwind version uses group-hover and group-focus-visible with an arbitrary grid-cols value.

What’s included

  • Icon buttons that expand to show their label
  • Width animates to the text through a 0fr to 1fr grid track
  • Label always present in the accessible name
  • Primary, dark, destructive and approve colours
  • Expansion is instant under reduced motion

Accessibility

Each button contains its label as real text, clipped while collapsed, so it is named without an aria-label, and the icons are aria-hidden. The label expands on :focus-visible as well as :hover, and keyboard focus draws a 2px indigo outline offset by 3px. Under prefers-reduced-motion the grid and colour transitions are removed and the label appears instantly.

Customise it

Change the fills in .xp-new, .xp-dark, .xp-danger and .xp-ok, the 48px size on .xp and .xp-ic, and the .28s timing on .xp-lbl. In Bootstrap edit --bs-btn-bg and --bs-btn-color on each class; in Tailwind edit the bg-, text-, h-12 and duration- utilities.