說明如何建構無障礙分割按鈕元件的基礎總覽。
在這篇文章中,我想分享有關建立分割按鈕的方法。 立即試用。
如果您喜歡看影片,請參考這篇文章的 YouTube 版本:
總覽
分割按鈕是按鈕 隱藏主要按鈕和一系列其他按鈕。很實用 在次要巢狀結構中呈現一般動作、較不常使用 如果想讓設計變得既繁瑣,就一定要有分割按鈕 感覺很少進階分割按鈕甚至可能會記住上次的使用者動作 然後把這些元素提升為主要位置
電子郵件應用程式提供常見的分割按鈕。主要動作 ,但您可以稍後再傳送,也可以改為儲存草稿:
共用動作區域很實用,因為使用者不需要四處看看。他們 但請注意,分割按鈕內含重要電子郵件動作
零件
在探討分割按鈕之前 提供更完整的使用者體驗 VisBug 的無障礙功能 這裡會使用檢查工具顯示元件的微距檢視 定義各主要部分的 HTML、樣式和無障礙功能
頂層分割按鈕容器
最高等級的元件為內嵌 Flexbox,類別為
gui-split-button
,包含主要動作
和 .gui-popup-button
。
主要動作按鈕
初始顯示和可聚焦的 <button>
需符合
兩個相應邊角形狀
對焦、
懸停和
有效互動的對象
都包含在 .gui-split-button
內。
彈出式切換鈕
「彈出式按鈕」支援元素用於啟用及分配至
次要按鈕請注意,這不是 <button>
,且無法聚焦。不過
是 .gui-popup
的定位錨定標記,用於 :focus-within
的主機
顯示彈出式視窗
彈出式資訊卡
此為浮動資訊卡子項
.gui-popup-button
,定位絕對和
在語意上納入按鈕清單
次要動作
可聚焦的 <button>
,字型大小比主要字型小一些
動作按鈕:包含圖示和隨附的
設為主要按鈕樣式
自訂屬性
下列變數有助於建立色彩協調,並集中存放於 修改元件中使用的值
@custom-media --motionOK (prefers-reduced-motion: no-preference);
@custom-media --dark (prefers-color-scheme: dark);
@custom-media --light (prefers-color-scheme: light);
.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: 1px solid var(--theme-border);
--radius: 6px;
--in-speed: 50ms;
--out-speed: 300ms;
@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%);
}
}
版面配置和顏色
標記
元素一開始會以含有自訂類別名稱的 <div>
開頭。
<div class="gui-split-button"></div>
新增主要按鈕和 .gui-popup-button
元素。
<div class="gui-split-button">
<button>Send</button>
<span class="gui-popup-button" aria-haspopup="true" aria-expanded="false" title="Open for more actions"></span>
</div>
請注意 ARI 屬性 aria-haspopup
和 aria-expanded
。這些信號
讓螢幕閱讀器必須瞭解分割的功能和狀態
按鈕體驗。title
屬性對所有人都有幫助。
新增 <svg>
圖示和 .gui-popup
容器元素。
<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"></ul>
</span>
</div>
如果是簡單的彈出式刊登位置,.gui-popup
是指按鈕的子項
。這項策略唯一的攔截行動是.gui-split-button
容器無法使用 overflow: hidden
,因為該容器會
因為視覺的呈現。
包含 <li><button>
內容的 <ul>
會宣告自身為「按鈕」
清單」也就是呈現的介面
<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>Schedule for later</button>
</li>
<li>
<button>Delete</button>
</li>
<li>
<button>Save draft</button>
</li>
</ul>
</span>
</div>
我們為次要按鈕加上了圖示,希望您能更賞心悅目,同時增添趣味色彩。 來自 https://heroicons.com。圖示為兩者皆可選用 例如主要和次要按鈕
<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>
樣式
設定好 HTML 和內容後,樣式即可開始提供顏色和版面配置。
設定分割按鈕容器的樣式
inline-flex
顯示類型適用於這個包裝元件
應配合其他分割按鈕、動作或元素。
.gui-split-button {
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;
}
<button>
樣式
按鈕很適合用來掩蓋所需的程式碼數量。您可能需要 復原或取代瀏覽器預設樣式,但您也需強制執行 沿用、新增互動狀態,並根據不同的使用者偏好加以調整 輸入類型按鈕樣式可以快速累積。
這些按鈕與一般按鈕不同,因為兩者共用同一個背景 宣告物件通常按鈕會有其背景和文字顏色。 不過,使用者可以分享這些圖片,而且只在互動時套用自己的背景。
.gui-split-button button {
cursor: pointer;
appearance: none;
background: none;
border: none;
display: inline-flex;
align-items: center;
gap: 1ch;
white-space: nowrap;
font-family: inherit;
font-size: inherit;
font-weight: 500;
padding-block: 1.25ch;
padding-inline: 2.5ch;
color: var(--ontheme);
outline-color: var(--theme);
outline-offset: -5px;
}
透過少數 CSS 新增互動狀態 虛擬類別及使用比對功能 狀態的自訂屬性:
.gui-split-button button {
…
&:is(:hover, :focus-visible) {
background: var(--theme-hover);
color: var(--ontheme);
& > svg {
stroke: currentColor;
fill: none;
}
}
&:active {
background: var(--theme-active);
}
}
主要按鈕需要幾種特殊的樣式,才能完成設計效果:
.gui-split-button > button {
border-end-start-radius: var(--radius);
border-start-start-radius: var(--radius);
& > svg {
fill: none;
stroke: var(--ontheme);
}
}
最後,在風格上,淺色主題按鈕和圖示 shadow:
.gui-split-button {
@media (--light) {
& > button,
& button:is(:focus-visible, :hover) {
text-shadow: 0 1px 0 var(--theme-active);
}
& > .gui-popup-button > svg,
& button:is(:focus-visible, :hover) > svg {
filter: drop-shadow(0 1px 0 var(--theme-active));
}
}
}
理想的按鈕應著重於微互動和極細的細節。
關於「:focus-visible
」的附註
請注意,按鈕樣式如何使用 :focus-visible
,而非 :focus
。:focus
是提供無障礙功能不可或缺的一項功能
因此無論使用者是否需要看見
但這不會套用至任何焦點
下方的影片試圖細分這種微互動,如下所示
:focus-visible
是智慧替代選項。
設定彈出式按鈕的樣式
用於將圖示置中及錨定彈出式按鈕清單的 4ch
Flexbox。喜歡
主要按鈕會顯示為透明,直到使用者停留在主要按鈕或進行互動
並延伸至填滿
.gui-popup-button {
inline-size: 4ch;
cursor: pointer;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
border-inline-start: var(--border);
border-start-end-radius: var(--radius);
border-end-end-radius: var(--radius);
}
運用 CSS 將遊標懸停、焦點和有效狀態圖層
Nesting 和
:is()
功能選取器:
.gui-popup-button {
…
&:is(:hover,:focus-within) {
background: var(--theme-hover);
}
/* fixes iOS trying to be helpful */
&:focus {
outline: none;
}
&:active {
background: var(--theme-active);
}
}
這些樣式是顯示及隱藏彈出式視窗的主要誘因。當
.gui-popup-button
已對其任何子項套用 focus
,設定 opacity
,位置
圖示和彈出式視窗上都要有 pointer-events
.gui-popup-button {
…
&:focus-within {
& > svg {
transition-duration: var(--in-speed);
transform: rotateZ(.5turn);
}
& > .gui-popup {
transition-duration: var(--in-speed);
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
}
}
完成「內切」和「整理」風格後,最後一項決定是有條件限制的 轉場效果 (視使用者的動作偏好設定而定):
.gui-popup-button {
…
@media (--motionOK) {
& > svg {
transition: transform var(--out-speed) ease;
}
& > .gui-popup {
transform: translateY(5px);
transition:
opacity var(--out-speed) ease,
transform var(--out-speed) ease;
}
}
}
如果著重在程式碼上,您會發現對使用者的不透明度仍存在轉換 偏好減少動態影像的人
設定彈出式視窗樣式
.gui-popup
元素是使用自訂屬性的浮動資訊卡按鈕清單
配合主要單元
按鈕和品牌元素請注意,各圖示的對比度較低
看起來最薄,陰影則帶有品牌藍色提示。就像按鈕一樣
這些細節都要歸功於這些細微的細節,才是健全的 UI 和使用者體驗。
.gui-popup {
--shadow: 220 70% 15%;
--shadow-strength: 1%;
opacity: 0;
pointer-events: none;
position: absolute;
bottom: 80%;
left: -1.5ch;
list-style-type: none;
background: var(--popupbg);
color: var(--theme-text);
padding-inline: 0;
padding-block: .5ch;
border-radius: var(--radius);
overflow: hidden;
display: flex;
flex-direction: column;
font-size: .9em;
transition: opacity var(--out-speed) ease;
box-shadow:
0 -2px 5px 0 hsl(var(--shadow) / calc(var(--shadow-strength) + 5%)),
0 1px 1px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 10%)),
0 2px 2px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 12%)),
0 5px 5px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 13%)),
0 9px 9px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 14%)),
0 16px 16px -2px hsl(var(--shadow) / calc(var(--shadow-strength) + 20%))
;
}
這些圖示和按鈕都有加上品牌顏色,讓各種暗色的風格煥然一新 和淺色主題卡:
.gui-popup {
…
& svg {
fill: var(--popupbg);
stroke: var(--theme);
@media (prefers-color-scheme: dark) {
stroke: var(--theme-border);
}
}
& button {
color: var(--theme-text);
width: 100%;
}
}
深色主題彈出式視窗有文字和圖示陰影,並額外增添一點 強烈方塊陰影:
.gui-popup {
…
@media (--dark) {
--shadow-strength: 5%;
--shadow: 220 3% 2%;
& button:not(:focus-visible, :hover) {
text-shadow: 0 1px 0 var(--ontheme);
}
& button:not(:focus-visible, :hover) > svg {
filter: drop-shadow(0 1px 0 var(--ontheme));
}
}
}
一般 <svg>
圖示樣式
所有圖示的大小都與先前用於 font-size
按鈕的按鈕類似
使用 ch
做為
inline-size
。每種方法也提供一些樣式,讓圖示的外框變得柔軟且
流暢。
.gui-split-button svg {
inline-size: 2ch;
box-sizing: content-box;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2px;
}
由右至左版面配置
邏輯屬性會處理所有複雜工作。
以下是使用的邏輯屬性清單:
- display: inline-flex
可建立內嵌 Flex 元素。
- padding-block
和 padding-inline
為配對,而非 padding
簡而言之,您可以取得填充邏輯面的好處。
- border-end-start-radius
和
朋友將
圓角化。
- 使用 inline-size
而非 width
,可確保尺寸不會與實際尺寸相關聯。
- border-inline-start
會在起點加上框線,可能位於右側或左側,視指令碼方向而定。
JavaScript
下列 JavaScript 幾乎全都是強化無障礙功能。我兩個 輔助程式庫可簡化工作 BlingBlingJS 精簡基本用法 DOM 查詢和簡易的事件監聽器設定, roving-ux 改善無障礙環境 為彈出式視窗的鍵盤和遊戲手把互動操作。
import $ from 'blingblingjs'
import {rovingIndex} from 'roving-ux'
const splitButtons = $('.gui-split-button')
const popupButtons = $('.gui-popup-button')
匯入上述程式庫後,選取並儲存至 變數,升級體驗時,幾項功能尚未完成。
旋轉索引
當鍵盤或螢幕閱讀器聚焦 .gui-popup-button
時,我們希望
將焦點移至第一個 (或最近聚焦) 按鈕
.gui-popup
。我們可以利用程式庫利用 element
和 target
達成此目的
參數。
popupButtons.forEach(element =>
rovingIndex({
element,
target: 'button',
}))
元素現在會將焦點傳遞至目標 <button>
子項,並啟用
標準方向鍵瀏覽選項。
正在切換「aria-expanded
」
儘管在視覺上可以看出彈出式視窗顯示及隱藏,但螢幕閱讀器的不只是視覺提示。這裡使用 JavaScript 可切換螢幕閱讀器適用屬性,藉此強化以 CSS 驅動的 :focus-within
互動。
popupButtons.on('focusin', e => {
e.currentTarget.setAttribute('aria-expanded', true)
})
popupButtons.on('focusout', e => {
e.currentTarget.setAttribute('aria-expanded', false)
})
啟用 Escape
鍵
使用者的注意力會刻意傳送到陷阱,也就是說,我們需要
提供出發前往的選項最常見的方法是允許使用 Escape
鍵。
做法是留意是否按下了彈出式按鈕
子項會向上顯示在這個父項上方。
popupButtons.on('keyup', e => {
if (e.code === 'Escape')
e.target.blur()
})
如果彈出式按鈕偵測到任何按下 Escape
鍵,焦點會從本身移除
同時
blur()
。
分割按鈕點擊次數
最後,如果使用者點選、輕觸或鍵盤與按鈕互動,
應用程式需要執行適當的動作使用事件啟動功能
但這次在 .gui-split-button
容器上擷取按鈕
來自子項彈出式視窗或主要動作的點擊。
splitButtons.on('click', event => {
if (event.target.nodeName !== 'BUTTON') return
console.info(event.target.innerText)
})
結論
現在你知道我怎麼了,這樣會如何 🙂?
讓我們來體驗多元的方法,瞭解透過網路建立內容的所有方式。 建立示範、將 Twitter 推文連結,我們就會為您新增 前往下方的社群重混專區!