<div class="gui-split-button">
<button>View Cart</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup">
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" />
</svg>
Checkout
</button></li>
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" />
</svg>
Quick Pay
</button></li>
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z" />
</svg>
Save for later
</button></li>
</ul>
</span>
</div>
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup">
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
Schedule for later
</button></li>
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
Delete
</button></li>
<li><button>
<svg aria-hidden="true" viewBox="0 0 24 24">
<path d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z" />
</svg>
Save draft
</button></li>
</ul>
</span>
</div>
<div class="gui-split-button">
<button>Squash</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions">
<svg aria-hidden="true" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
</svg>
<ul class="gui-popup">
<li><button>
Create a merge commit
</button></li>
<li><button>
Rebase
</button></li>
</ul>
</span>
</div>
. gui-split-button {
--theme : hsl ( 220 75 % 50 % );
--theme-hover : hsl ( 220 75 % 45 % );
--theme-active : hsl ( 220 75 % 40 % );
--theme-text : hsl ( 220 75 % 25 % );
--theme-border : hsl ( 220 50 % 75 % );
--ontheme : hsl ( 220 90 % 98 % );
--popupbg : hsl ( 220 0 % 100 % );
--border : 1 px solid var ( --theme-border );
--radius : 6 px ;
--in-speed : 500 ms ;
--out-speed : 100 ms ;
display : inline-flex ;
border-radius : var ( --radius );
background : var ( --theme );
color : var ( --ontheme );
fill : var ( --ontheme );
touch-action : manipulation ;
user-select : none ;
-webkit- tap-highlight-color : transparent ;
@media (--dark) {
--theme : hsl ( 220 50 % 60 % );
--theme-hover : hsl ( 220 50 % 65 % );
--theme-active : hsl ( 220 75 % 70 % );
--theme-text : hsl ( 220 10 % 85 % );
--theme-border : hsl ( 220 20 % 70 % );
--ontheme : hsl ( 220 90 % 5 % );
--popupbg : hsl ( 220 10 % 30 % );
}
& button {
cursor : pointer ;
appearance : none ;
background : none ;
border : none ;
display : inline-flex ;
align-items : center ;
gap : 1 ch ;
white-space : nowrap ;
font-family : inherit ;
font-size : inherit ;
font-weight : 500 ;
padding-block : 1.25 ch ;
padding-inline : 2.5 ch ;
color : var ( --ontheme );
outline-color : var ( --theme );
outline-offset : -5 px ;
&:is(:hover, :focus-visible) {
background : var ( --theme-hover );
color : var ( --ontheme );
& > svg {
stroke : currentColor ;
fill : none ;
}
}
& : active {
background : var ( --theme-active );
}
}
& > button {
border-radius : var ( --radius ) 0 0 var ( --radius );
@supports ( border-start-start-radius : 1 px ) {
border-end-start-radius : var ( --radius );
border-start-start-radius : var ( --radius );
}
}
@ media ( --light ) {
& > button ,
& button : is ( : focus-visible , : hover ) {
text-shadow : 0 1 px 0 var ( --theme-active );
}
& > . gui-popup-button > svg ,
& button : is ( : focus-visible , : hover ) > svg {
filter : drop-shadow ( 0 1 px 0 var ( --theme-active ));
}
}
& svg {
inline-size : 2 ch ;
box-sizing : content-box ;
stroke-linecap : round ;
stroke-linejoin : round ;
stroke-width : 2 px ;
}
}
. gui-popup-button {
inline-size : 4 ch ;
cursor : pointer ;
position : relative ;
display : inline-flex ;
align-items : center ;
justify-content : center ;
border-inline-start : var ( --border );
border-radius : 0 var ( --radius ) var ( --radius ) 0 ;
@supports ( border-start-start-radius : 1 px ) {
border-inline-start : var ( --border );
border-start-end-radius : var ( --radius );
border-end-end-radius : var ( --radius );
}
& : is ( : hover , : focus-within ) {
background : var ( --theme-hover );
}
/* fixes iOS trying to be helpful */
& : focus {
outline : none ;
}
& : active {
background : var ( --theme-active );
}
& : focus-within {
& > svg {
transition-duration : var ( --in-speed );
transform : rotateZ ( .5 turn );
}
& > . gui-popup {
transition-duration : var ( --in-speed );
opacity : 1 ;
transform : translateY ( 0 );
pointer-events : auto ;
}
}
@ media ( --motionOK ) {
& > svg {
transition : transform var ( --out-speed ) ease ;
}
& > . gui-popup {
transform : translateY ( 5 px );
transition :
opacity var ( --out-speed ) ease ,
transform var ( --out-speed ) ease ;
}
}
}
. gui-popup {
--shadow : 220 70 % 15 % ;
--shadow-strength : 1 % ;
opacity : 0 ;
pointer-events : none ;
position : absolute ;
inset-block-end : 80 % ;
inset-inline-start : -1.5 ch ;
list-style-type : none ;
background : var ( --popupbg );
color : var ( --theme-text );
padding-inline : 0 ;
padding-block : .5 ch ;
border-radius : var ( --radius );
overflow : hidden ;
display : flex ;
flex-direction : column ;
font-size : .9 em ;
transition : opacity var ( --out-speed ) ease ;
box-shadow :
0 -2 px 5 px 0 hsl ( var ( --shadow ) / calc ( var ( --shadow-strength ) + 5 % )),
0 1 px 1 px -2 px hsl ( var ( --shadow ) / calc ( var ( --shadow-strength ) + 10 % )),
0 2 px 2 px -2 px hsl ( var ( --shadow ) / calc ( var ( --shadow-strength ) + 12 % )),
0 5 px 5 px -2 px hsl ( var ( --shadow ) / calc ( var ( --shadow-strength ) + 13 % )),
0 9 px 9 px -2 px hsl ( var ( --shadow ) / calc ( var ( --shadow-strength ) + 14 % )),
0 16 px 16 px -2 px hsl ( var ( --shadow ) / calc ( var ( --shadow-strength ) + 20 % ))
;
/* fixes iOS trying to be helpful */
&:focus { outline : none }
@ media ( --dark ) {
--shadow-strength : 5 %;
--shadow : 220 3 % 2 %;
& button : not ( : focus-visible , : hover ) {
text-shadow : 0 1 px 0 var ( --ontheme );
}
& button : not ( : focus-visible , : hover ) > svg {
filter : drop-shadow ( 0 1 px 0 var ( --ontheme ));
}
}
@ media ( width <= 400px ) {
inset-inline-start : -200 %;
}
& svg {
fill : var ( --popupbg );
stroke : var ( --theme );
@media ( prefers-color-scheme : dark ) {
stroke : var ( --theme-border );
}
}
& button {
color : var ( --theme-text );
width : 100 % ;
}
}
import $ from 'blingblingjs'
import { rovingIndex } from 'roving-ux'
const splitButtons = $ ( '.gui-split-button' )
const popupButtons = $ ( '.gui-popup-button' )
// popup activating roving index for it's buttons
popupButtons . forEach ( element =>
rovingIndex ({
element ,
target : 'button' ,
}))
// support escape key
popupButtons . on ( 'keyup' , e => {
if ( e . code === 'Escape' )
e . target . blur ()
})
popupButtons . on ( 'focusin' , e => {
e . currentTarget . setAttribute ( 'aria-expanded' , true )
})
popupButtons . on ( 'focusout' , e => {
e . currentTarget . setAttribute ( 'aria-expanded' , false )
})
// respond to any button interaction
splitButtons . on ( 'click' , event => {
if ( event . target . nodeName !== 'BUTTON' ) return
console . info ( event . target . innerText )
})
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License , and code samples are licensed under the Apache 2.0 License . For details, see the Google Developers Site Policies . Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2023-07-05 UTC.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2023-07-05 UTC."],[],[]]