Bölme düğmesi bileşeni oluşturma

Erişilebilir bir bölünmüş düğme bileşeninin nasıl oluşturulacağına dair temel bir genel bakış.

Bu yayında, bölünmüş düğme oluşturma yöntemiyle ilgili düşüncelerimi paylaşmak istiyorum . Demoyu deneyin.

Demo

Video tercih ediyorsanız bu yayının YouTube versiyonunu aşağıda bulabilirsiniz:

Genel Bakış

Bölünmüş düğmeler, birincil düğmeyi ve ek düğmelerin listesini gizleyen düğmelerdir. İkincil ve daha az kullanılan işlemleri gerektiğinde kullanmak üzere iç içe yerleştirirken ortak bir işlemi göstermek için kullanışlıdır. Bölünmüş düğme, yoğun bir tasarımın minimal görünmesine yardımcı olmak için çok önemlidir. Gelişmiş bir bölme düğmesi, son kullanıcı işlemini hatırlayıp birincil konuma da yükseltebilir.

E-posta uygulamanızda ortak bir bölme düğmesi bulunur. Birincil işlem göndermektir ancak bunun yerine daha sonra gönderebilir veya taslak kaydedebilirsiniz:

Bir e-posta uygulamasında görünen örnek bir bölünmüş düğme.

Kullanıcının etrafa bakmasına gerek kalmadığı için paylaşılan işlem alanı kullanışlıdır. Kullanıcılar, önemli e-posta işlemlerinin bölünmüş düğmede yer aldığını bilir.

Parçalar

Genel düzenlemelerini ve nihai kullanıcı deneyimini ele almadan önce, bölünmüş düğmenin temel kısımlarını inceleyelim. VisBug'ın erişilebilirlik inceleme aracı, bileşenin makro görünümünü göstermeye yardımcı olmak için burada kullanılıyor. Bu araç, her ana bölüm için HTML, stil ve erişilebilirlik yönlerini ortaya çıkarıyor.

Bölünmüş düğmeyi oluşturan HTML öğeleri.

Üst düzey bölme düğmesi kapsayıcısı

En üst düzey bileşen, birincil işlemi ve .gui-popup-button içeren, gui-split-button sınıfına sahip bir satır içi esnek kutudur.

gui-split-button sınıfı inceleniyor ve bu sınıfta kullanılan CSS özellikleri gösteriliyor.

Birincil işlem düğmesi

Başlangıçta görünür ve odaklanılabilir olan <button>, .gui-split-button içinde görünmesi için odaklanma, fareyle üzerine gelme ve etkin etkileşimleri için iki eşleşen köşe şekliyle kapsayıcıya sığar.

Düğme öğesinin CSS kurallarını gösteren inceleyici.

Pop-up açma/kapatma düğmesi

"Pop-up düğmesi" destek öğesi, ikincil düğmelerin listesini etkinleştirmek ve bu listeye gönderme yapmak için kullanılır. Bu öğenin <button> olmadığını ve odaklanılamadığını fark edin. Ancak, .gui-popup için konumlandırma sabiti ve pop-up'ı göstermek için kullanılan :focus-within'ün ana bilgisayarıdır.

gui-popup-button sınıfının CSS kurallarını gösteren inceleyici.

Pop-up kart

Bu, mutlak konumlandırılmış ve düğme listesini anlamsal olarak sarmalayan, bağlantı noktasına bağlı kayan bir karttır .gui-popup-button.

gui-popup sınıfının CSS kurallarını gösteren inceleyici

İkincil işlemler

Birincil işlem düğmesinden biraz daha küçük bir yazı tipi boyutuna sahip, odaklanılabilir bir <button>, bir simge ve birincil düğmeyle uyumlu bir stil içerir.

Düğme öğesinin CSS kurallarını gösteren inceleyici.

Özel özellikler

Aşağıdaki değişkenler, renk uyumu oluşturmaya ve bileşende kullanılan değerleri değiştirmek için merkezi bir yer oluşturmaya yardımcı olur.

@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%);
  }
}

Düzenler ve renk

Brüt kar

Öğe, özel sınıf adıyla <div> olarak başlar.

<div class="gui-split-button"></div>

Birincil düğmeyi ve .gui-popup-button öğelerini ekleyin.

<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-haspopup ve aria-expanded aria özelliklerine dikkat edin. Bu ipuçları, ekran okuyucuların bölünmüş düğme deneyiminin özelliğini ve durumunu bilmesi için çok önemlidir. title özelliği herkes için faydalıdır.

<svg> simgesi ve .gui-popup kapsayıcı öğesini ekleyin.

<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 pop-up yerleşimi için .gui-popup, pop-up'ı genişleten düğmenin alt öğesidir. Bu stratejinin tek dezavantajı, .gui-split-button kapsayıcısının overflow: hidden kullanamamasıdır. Çünkü bu durumda pop-up görsel olarak görünmez.

<li><button> içerikleriyle dolu bir <ul>, ekran okuyuculara kendisini "düğme listesi" olarak duyurur. Bu da tam olarak sunulan arayüzdü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>Schedule for later</button>
      </li>
      <li>
        <button>Delete</button>
      </li>
      <li>
        <button>Save draft</button>
      </li>
    </ul>
  </span>
</div>

Görünüm katmak ve renklerle eğlenmek için https://heroicons.com adresinden ikincil düğmelere simgeler ekledim. 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 yerleştirildikten sonra stiller renk ve düzen sağlamaya hazır olur.

Bölünmüş düğme kapsayıcısını stilize etme

inline-flex görüntüleme türü, diğer bölünmüş düğmeler, işlemler veya öğelerle satır içi olarak yerleştirilmesi gerektiğinden bu sarmalama bileşeni için uygundur.

.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;
}

Bölünmüş düğme.

<button> stili

Düğmeler, ne kadar kod gerektiğini gizleme konusunda çok iyidir. Tarayıcının varsayılan stillerini geri almanız veya değiştirmeniz gerekebilir. Ancak bazı devralma işlemlerini zorunlu kılmanız, etkileşim durumları eklemeniz ve çeşitli kullanıcı tercihlerine ve giriş türlerine uyum sağlamanız da gerekir. Düğme stilleri hızla artar.

Bu düğmeler, üst öğeyle arka plan paylaştıkları için normal düğmelerden farklıdır. Genellikle bir düğme, arka plan ve metin rengine sahiptir. Ancak bunlar, etkileşimde yalnızca kendi arka planlarını 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ı ile etkileşim durumları ekleyin ve durum için eşleşen özel özellikler 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üğmenin tasarım efektini tamamlamak için birkaç özel stile ihtiyacı vardır:

.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 daha şık bir görünüm için açık tema düğmesi ve simgesine gölge ekleniyor:

.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));
    }
  }
}

İyi bir düğmede mikro etkileşimlere ve küçük ayrıntılara dikkat edilir.

:focus-visible hakkında not

Düğme stillerinde :focus yerine :focus-visible kullanıldığına dikkat edin. :focus Erişilebilir bir kullanıcı arayüzü oluşturmak için önemli bir dokunuş olsa da bir dezavantajı vardır: Kullanıcının bunu görmesi gerekip gerekmediği konusunda akıllı değildir ve herhangi bir odak için geçerli olur.

Aşağıdaki videoda, bu mikro etkileşim ayrıntılı olarak açıklanmaya çalışılıyor ve :focus-visible'nın nasıl akıllı bir alternatif olduğu gösteriliyor.

Pop-up düğmesini stilize etme

Bir simgeyi ortalamak ve pop-up düğme listesini sabitlemek için 4chesnek kutu. Birincil düğme gibi, üzerine gelinene veya etkileşimde bulunulana kadar şeffaftır ve doldurmak için uzatılır.

Pop-up&#39;ı tetiklemek için kullanılan bölünmüş düğmenin ok kısmı.

.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 ve :is() işlevsel seçiciyle fareyle üzerine gelme, odaklanma ve etkin durumları katmanlayın:

.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, pop-up'ı gösterme ve gizleme için kullanılan temel kancalardır. .gui-popup-button öğesinin alt öğelerinden herhangi birinde focus varsa simge ve pop-up'ta opacity, konum ve pointer-events ayarlarını yapın.

.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ıktan sonra son adım, kullanıcının hareket tercihine bağlı olarak dönüşümleri koşullu olarak geçişlendirmektir:

.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;
    }
  }
}

Kodu dikkatle inceleyenler, opacity özelliğinin, hareket azaltmayı tercih eden kullanıcılar için geçiş yapmaya devam ettiğini fark edecektir.

Pop-up'a stil uygulama

.gui-popup öğesi, özel özellikler ve göreli birimler kullanılarak biraz daha küçük hale getirilen, birincil düğmeyle etkileşimli olarak eşleştirilen ve renk kullanımıyla markaya uygun hale getirilen kayan bir kart düğmesi listesidir. Simge kontrastının daha az olduğunu, daha ince olduğunu ve gölgede markanın mavi renginin bir ipucu olduğunu fark edin. Düğmelerde olduğu gibi, güçlü bir kullanıcı arayüzü ve kullanıcı deneyimi, bu küçük ayrıntıların bir araya gelmesiyle oluşur.

Kayar kart öğesi.

.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%))
  ;
}

Simgeler ve düğmeler, her koyu ve açık temalı kartta güzel bir şekilde stilize edilmek için marka renkleriyle verilir:

Ödeme, Hızlı Ödeme ve Daha Sonra Kaydetme ile ilgili bağlantılar ve simgeler.

.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%;
  }
}

Koyu tema pop-up'ında metin ve simge gölge eklemelerinin yanı sıra biraz daha yoğun bir kutu gölgesi bulunur:

Koyu temadaki pop-up.

.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));
    }
  }
}

Genel <svg> simge stilleri

Tüm simgeler, font-size birimini inline-size olarak kullanarak kullanıldıkları düğmeye göre boyutlandırılır.ch Ayrıca, simgelerin yumuşak ve pürüzsüz bir şekilde ana hatlarını çizmeye yardımcı olacak bazı stiller de eklenir.

.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

Tüm karmaşık işleri mantıksal özellikler yapar. Kullanılan mantıksal özelliklerin listesini aşağıda bulabilirsiniz: - display: inline-flex satır içi esnek öğe oluşturur. - padding-block ve padding-inline kısaltması yerine padding kısaltmasını kullanarak 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. - width yerine inline-size kullanılması, boyutun fiziksel boyutlarla ilişkili olmamasını sağlar. - border-inline-start, başlangıca kenarlık ekler. Bu kenarlık, komut dosyasının yönüne bağlı olarak sağda veya solda olabilir.

JavaScript

Aşağıdaki JavaScript'in neredeyse tamamı erişilebilirliği artırmak için kullanılır. Görevleri biraz daha kolaylaştırmak için iki yardımcı kitaplığım kullanılıyor. BlingBlingJS, kısa ve öz DOM sorguları ve kolay etkinlik işleyici kurulumu için kullanılırken roving-ux, pop-up'ta erişilebilir klavye ve gamepad 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 içe aktarıldıktan ve öğeler seçilip değişkenlere kaydedildikten sonra deneyimi yükseltmek için birkaç işlev daha eklemeniz gerekir.

Gezici dizin

Klavye veya ekran okuyucu .gui-popup-button öğesine odaklandığında, odağı .gui-popup içindeki ilk (veya en son odaklanılan) düğmeye yönlendirmek istiyoruz. Kitaplık, element ve target parametreleriyle bunu yapmamıza yardımcı olur.

popupButtons.forEach(element =>
  rovingIndex({
    element,
    target: 'button',
  }))

Öğe artık odağı hedef <button> alt öğelerine geçiriyor ve seçeneklere göz atmak için standart ok tuşuyla gezinmeyi etkinleştiriyor.

aria-expanded özelliğini açma/kapatma

Pop-up'ın gösterilip gizlendiği görsel olarak belli olsa da ekran okuyucu için görsel ipuçlarından daha fazlası gerekir. Burada, ekran okuyucuya uygun bir özelliği değiştirerek CSS destekli :focus-within etkileşimini tamamlamak için JavaScript kullanılır.

popupButtons.on('focusin', e => {
  e.currentTarget.setAttribute('aria-expanded', true)
})

popupButtons.on('focusout', e => {
  e.currentTarget.setAttribute('aria-expanded', false)
})

Escape tuşunu etkinleştirme

Kullanıcının odağı kasıtlı olarak bir tuzağa gönderildi. Bu nedenle, çıkış yolu sağlamamız gerekiyor. En yaygın yöntem, Escape tuşunun kullanılmasına izin vermektir. Bunu yapmak için, alt öğelerdeki tüm klavye etkinlikleri bu üst öğeye aktarılacağından pop-up düğmesindeki tuş vuruşlarını izleyin.

popupButtons.on('keyup', e => {
  if (e.code === 'Escape')
    e.target.blur()
})

Pop-up düğmesi herhangi bir Escape tuşuna basıldığını görürse blur() ile odağı kendisinden kaldırır.

Bölünmüş düğme tıklamaları

Son olarak, kullanıcı düğmeleri tıkladığında, düğmelere dokunduğunda veya klavye ile düğmelerle etkileşimde bulunduğunda uygulamanın uygun işlemi gerçekleştirmesi gerekir. Burada, alt pop-up penceresinden veya birincil işlemden gelen düğme tıklamalarını yakalamak için .gui-split-button kapsayıcısında tekrar etkinlik kabarcıklanması kullanılır.

splitButtons.on('click', event => {
  if (event.target.nodeName !== 'BUTTON') return
  console.info(event.target.innerText)
})

Sonuç

Bunu nasıl yaptığımı öğrendiğinize göre, siz nasıl yapardınız? 🙂

Yaklaşımlarımızı çeşitlendirelim ve web'de içerik oluşturmanın tüm yollarını öğrenelim. Bir demo oluşturun, bağlantıları bana tweet atın. Ben de bu bağlantıları aşağıdaki topluluk remiksleri bölümüne ekleyeyim.

Topluluk remiksleri