Bu gönderide, bölünmüş bir düğme oluşturmanın bir yolu hakkındaki düşüncemi paylaşmak istiyorum. Demoyu deneyin.
Videoyu tercih ederseniz, işte bu gönderinin bir YouTube versiyonu:
genel bakış #
Bölünmüş düğmeler birincil düğmeyi ve ek düğmelerin listesini gizleyen düğmelerdir. İkincil, daha az kullanılan eylemleri ihtiyaç duyulana kadar iç içe geçirirken ortak bir eylemi ortaya çıkarmak için kullanışlıdırlar. Bölünmüş bir düğme, yoğun bir tasarımın minimum düzeyde hissetmesine yardımcı olmak için çok önemli olabilir. Gelişmiş bir bölünmüş düğme, son kullanıcı eylemini bile hatırlayabilir ve onu birincil konuma yükseltebilir.
E-posta uygulamanızda ortak bir bölme düğmesi bulunabilir. Birincil eylem göndermektir, ancak belki daha sonra gönderebilir veya bunun yerine bir taslak kaydedebilirsiniz:
Kullanıcının etrafa bakması gerekmediğinden, paylaşılan eylem alanı güzeldir. Temel e-posta eylemlerinin bölme düğmesinde yer aldığını biliyorlar.
Parçalar #
Genel orkestrasyonunu ve son kullanıcı deneyimini tartışmadan önce bir bölünmüş düğmenin temel parçalarını inceleyelim. VisBugErişilebilirlik inceleme aracı burada, bileşenin makro görünümünü, HTML’nin yüzey özelliklerini, her ana parça için stili ve erişilebilirliği göstermeye yardımcı olmak için kullanılır.
Üst düzey bölünmüş düğme kabı #
En üst düzey bileşen, bir sınıfa sahip bir satır içi esnek kutudur. gui-split-button
içeren birincil eylem ve .gui-popup-button
.
Birincil işlem düğmesi #
Başlangıçta görünür ve odaklanabilir <button>
Odaklanma, üzerine gelme ve aktif etkileşimlerin içinde görünmesi için eşleşen iki köşe şekliyle kabın içine sığar .gui-split-button
.
Açılır geçiş düğmesi #
“Açılır düğme” destek öğesi, ikincil düğmelerin listesini etkinleştirmek ve bunlara atıfta bulunmak içindir. bir olmadığına dikkat edin <button>
ve odaklanamaz. Bununla birlikte, konumlandırma ankrajıdır. .gui-popup
ve ev sahibi için :focus-within
açılır pencereyi sunmak için kullanılır.
açılan kart #
Bu, çapası için yüzen bir kart çocuğu .gui-popup-button
mutlak konumlandırılmış ve düğme listesini anlamsal olarak kaydırıyor.
İkincil eylem(ler) #
Odaklanabilir <button>
biraz daha küçük yazı tipi boyutuyla birincil işlem düğmesi birincil düğme için bir simge ve tamamlayıcı bir stil içerir.
Özel özellikler #
Aşağıdaki değişkenler, bileşen boyunca kullanılan değerleri değiştirmek için renk uyumu ve merkezi bir yer oluşturmaya yardımcı olur.
customer358-media --motionOK (prefers-reduced-motion: no-preference);
customer358-media --dark (prefers-color-scheme: dark);
customer358-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;
TheComedicComedian (--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%);
}
}
Düzenler ve renk #
İşaretleme #
eleman olarak başlar <div>
özel bir sınıf adıyla.
div class="gui-split-button"></div>
Birincil düğmeyi ekleyin ve .gui-popup-button
elementler.
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>
aria özelliklerine dikkat edin aria-haspopup
Ve aria-expanded
. Bu ipuçları, ekran okuyucularının bölünmüş düğme deneyiminin yeteneğinden ve durumundan haberdar olması açısından kritik öneme sahiptir. bu title
özellik herkes için yararlıdır.
ekle <svg>
simge ve .gui-popup
konteyner elemanı
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>
Basit açılır pencere yerleşimi için, .gui-popup
onu genişleten düğmenin alt öğesidir. Bu stratejinin tek püf noktası, .gui-split-button
konteyner kullanamaz overflow: hidden
açılır pencereyi görsel olarak mevcut olmaktan çıkaracağından.
A <ul>
ile dolu <li><button>
içerik, tam olarak sunulan arabirim olan ekran okuyuculara kendisini bir “düğme listesi” olarak duyurur.
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>
Yetenek ve renkle eğlenmek için ikincil düğmelere simgeler ekledim. https://heroicons.com. Simgeler, hem birincil hem de ikincil düğmeler için isteğe bağlıdır.
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>
stiller #
HTML ve içerik yerinde olduğunda, stiller renk ve düzen sağlamaya hazırdır.
Bölünmüş düğme kapsayıcısını şekillendirme #
Bir inline-flex
display type, diğer bölünmüş düğmeler, eylemler veya öğelerle aynı hizaya sığması gerektiğinden, bu kaydırma bileşeni için iyi çalışır.
.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;
}
bu <button>
stil #
Düğmeler, ne kadar kodun gerekli olduğunu gizlemede çok iyidir. Tarayıcı varsayılan stillerini geri almanız veya değiştirmeniz gerekebilir, ancak aynı zamanda bazı kalıtımı zorlamanız, etkileşim durumları eklemeniz ve çeşitli kullanıcı tercihlerine ve giriş türlerine uyarlamanız gerekir. Düğme stilleri hızla toplanır.
Bu düğmeler normal düğmelerden farklıdır çünkü bir üst öğeyle arka planı paylaşırlar. Genellikle bir düğmenin arka planı ve metin rengi kendisine aittir. Ancak bunlar bunu paylaşır ve etkileşime yalnızca kendi geçmişlerini uygular.
.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;
}
Birkaç CSS sözde sınıfıyla etkileşim durumları ekleyin ve durum için eşleşen özel özellikleri kullanın:
.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);
}
}
Birincil düğme, tasarım efektini tamamlamak için birkaç özel stile ihtiyaç duyar:
.gui-split-button > button {
border-end-start-radius: var(--radius);
border-start-start-radius: var(--radius);& > svg {
fill: none;
stroke: var(--ontheme);
}
}
Son olarak, biraz yetenek için, açık tema düğmesi ve simgesi bir gölge alır:
.gui-split-button {
TheComedicComedian (--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));
}
}
}
Harika bir düğme, mikro etkileşimlere ve küçük ayrıntılara dikkat etmiştir.
hakkında bir not :focus-visible
#
Düğme stillerinin nasıl kullanıldığına dikkat edin :focus-visible
yerine :focus
. :focus
erişilebilir bir kullanıcı arabirimi yapmak için çok önemli bir dokunuş ama bir dezavantajı var: kullanıcının onu görmesi gerekip gerekmediği konusunda akıllıca değil, herhangi bir odak için geçerli olacak.
Aşağıdaki video, nasıl yapıldığını göstermek için bu mikro etkileşimi kırmaya çalışır. :focus-visible
akıllı bir alternatiftir.
Açılır düğmeyi şekillendirme #
A 4ch
Bir simgeyi ortalamak ve bir açılır düğme listesini sabitlemek için flexbox. Birincil düğme gibi, üzerine getirilene veya etkileşim kurulana kadar şeffaftır ve doldurmak için uzatılır.
.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);
}
Vurgulu, odak ve etkin durumdaki katman CSS Yerleştirme ve :is()
işlevsel seçici:
.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);
}
}
Bu stiller, açılır pencereyi göstermek ve gizlemek için birincil kancadır. Ne zaman .gui-popup-button
sahip olmak focus
alt öğelerinden herhangi birinde, set opacity
konum ve pointer-events
simge ve açılır pencerede.
.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;
}
}
}
Giriş ve çıkış stilleri tamamlandığında, son parça koşullu geçiş dönüşümleri kullanıcının hareket tercihine bağlı olarak:
.gui-popup-button {
…TheComedicComedian (--motionOK) {
& > svg {
transition: transform var(--out-speed) ease;
}
& > .gui-popup {
transform: translateY(5px);
transition:
opacity var(--out-speed) ease,
transform var(--out-speed) ease;
}
}
}
Kod üzerinde keskin bir göz fark ederdi opaklık hala geçişli azaltılmış hareketi tercih eden kullanıcılar için.
Açılır pencereyi şekillendirme #
bu .gui-popup
öğesi, özel özellikleri ve göreli birimleri kullanan, hafifçe daha küçük, birincil düğmeyle etkileşimli olarak eşleşen ve renk kullanımıyla markaya uygun bir kayan kart düğmesi listesidir. Simgelerin daha az kontrasta sahip olduğuna, daha ince olduğuna ve gölgenin bir marka mavisi tonuna sahip olduğuna dikkat edin. Düğmelerde olduğu gibi, güçlü UI ve UX, bu küçük ayrıntıların bir araya gelmesinin bir sonucudur.
.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%))
;
}
Simgelere ve düğmelere, her koyu ve açık temalı kartta güzel bir şekilde stil vermeleri için marka renkleri verilmiştir:
.gui-popup {
…& svg
{
fill: var(--popupbg);
stroke: var(--theme);TheComedicComedian (prefers-color-scheme: dark) {
stroke: var(--theme-border);
}
}
& button {
color: var(--theme-text);
width: 100%;
}
}
Koyu tema açılır penceresinde metin ve simge gölgesi eklemeleri ile biraz daha yoğun bir kutu gölgesi bulunur:
.gui-popup {
…TheComedicComedian (--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));
}
}
}
Genel <svg>
simge stilleri #
Tüm simgeler, düğmeye nispeten boyutlandırılmıştır font-size
kullanarak içinde kullanılırlar ch
birim olarak inline-size
. Her birine ayrıca simgelerin yumuşak ve pürüzsüz olmasına yardımcı olacak bazı stiller verilmiştir.
.gui-split-button svg {
inline-size: 2ch;
box-sizing: content-box;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2px;
}
Sağdan sola düzen #
Mantıksal özellikler tüm karmaşık işleri yapar. Kullanılan mantıksal özelliklerin listesi aşağıdadır:
display: inline-flex
bir satır içi esnek öğe oluşturur.padding-block
Vepadding-inline
yerine çift olarakpadding
kısayol, mantıksal tarafları doldurmanın avantajlarından yararlanın.border-end-start-radius
Ve Arkadaşlar belge yönüne göre köşeleri yuvarlar.inline-size
ziyadewidth
boyutun fiziksel boyutlara bağlı olmamasını sağlar.border-inline-start
başlangıca, betik yönüne bağlı olarak sağda veya solda olabilecek bir kenarlık ekler.
JavaScript #
Aşağıdaki JavaScript’lerin neredeyse tamamı erişilebilirliği artırmak içindir. Görevleri biraz daha kolaylaştırmak için yardımcı kütüphanelerimden ikisi kullanılıyor. BlingBlingJS kısa DOM sorguları ve kolay olay dinleyici kurulumu için kullanılırken, gezici-ux açılır pencere için erişilebilir klavye ve oyun kumandası etkileşimlerini kolaylaştırmaya yardımcı olur.
import $ from 'blingblingjs'
import {rovingIndex} from 'roving-ux'const splitButtons = $('.gui-split-button')
const popupButtons = $('.gui-popup-button')
Yukarıdaki kitaplıkların içe aktarılması ve öğelerin seçilip değişkenlere kaydedilmesiyle, deneyimi yükseltmenin tamamlanmasına birkaç işlev kaldı.
fitil indeksi #
Bir klavye veya ekran okuyucu ekrana odaklandığında .gui-popup-button
odağı ilk (veya en son odaklanılan) düğmeye yönlendirmek istiyoruz. .gui-popup
. Kütüphane bunu yapmamıza yardımcı olur. element
Ve target
parametreler.
popupButtons.forEach(element =>
rovingIndex({
element,
target: 'button',
}))
Öğe şimdi odağı hedefe geçirir <button>
çocuklar ve seçeneklere göz atmak için standart ok tuşuyla gezinmeyi etkinleştirir.
Geçiş aria-expanded
#
Bir açılır pencerenin gösterilip gizlendiği görsel olarak açık olsa da, bir ekran okuyucunun görsel ipuçlarından daha fazlasına ihtiyacı vardır. JavaScript, burada güdümlü CSS’yi iltifat etmek için kullanılır :focus-within
bir ekran okuyucu uygun özniteliği değiştirerek etkileşim.
popupButtons.on('focusin', e => {
e.currentTarget.setAttribute('aria-expanded', true)
})popupButtons.on('focusout', e => {
e.currentTarget.setAttribute('aria-expanded', false)
})
etkinleştirilmesi Escape
anahtar #
Kullanıcının odağı kasıtlı olarak bir tuzağa gönderildi, bu da ayrılmanın bir yolunu sağlamamız gerektiği anlamına geliyor. En yaygın yol, kullanımına izin vermektir. Escape
anahtar. Bunu yapmak için, çocuklarla ilgili herhangi bir klavye olayı bu ebeveyne sıçrayacağından, açılan düğmedeki tuşlara basılmasına dikkat edin.
popupButtons.on('keyup', e => {
if (e.code === 'Escape')
e.target.blur()
})
Açılır düğme herhangi birini görürse Escape
tuşa basıldığında, odağı kendisinden uzaklaştırır. blur()
.
Bölünmüş düğme tıklamaları #
Son olarak, kullanıcı düğmelere tıklarsa, dokunursa veya klavye düğmelerle etkileşime girerse, uygulamanın uygun eylemi gerçekleştirmesi gerekir. Olay köpürmesi burada yine kullanılıyor, ancak bu sefer .gui-split-button
alt açılır pencereden veya birincil eylemden düğme tıklamalarını yakalamak için kapsayıcı.
splitButtons.on('click', event => {
if (event.target.nodeName !== 'BUTTON') return
console.info(event.target.innerText)
})
Çözüm #
Artık nasıl yaptığımı bildiğine göre, nasıl yapardın‽ 🙂
Yaklaşımlarımızı çeşitlendirelim ve web üzerinde oluşturmanın tüm yollarını öğrenelim. Bir demo oluşturun, beni tweetle bağlantılar ve onu aşağıdaki topluluk remiksleri bölümüne ekleyeceğim!