Simgeleri kullanma ve yönetme

Simge tekniği seçme

Simge depolama ve oluşturma ayrı seçeneklerdir. Örneğin, bir SVG sprite, CSS simge sistemine bir simge sağlayabilir veya <use> ile referans verilebilir.

  • CSS simge sistemi: CSS'nin hangi simgeyi ne zaman göstereceğini seçmesi gerektiğinde kullanın. Tek renkli simge için maske, tam renkli simge için arka plan resmi kullanın.
  • <use> ile SVG: Yerleşik stil seçenekleri yeterli olduğunda bir sembolü yeniden kullanmak için bu seçeneği kullanın. CSS, referans verilen SVG'nin içeriğini rastgele biçimlendiremez veya canlandıramaz ancak sembol bunları kullanacak şekilde tasarlandığında devralınan özellikler ve özel özellikler iletilebilir. Örneğin, bir yol, CSS'nin simgenin o bölümünü renklendirmesine izin vermek için fill="var(--icon-accent, currentColor)" kullanabilir.
  • Satır içi SVG: SVG içeriklerinin rastgele stilini veya animasyonunu oluşturmanız gerektiğinde kullanın.
  • CSS filtreleriyle <img>: Simgeleri yeniden renklendirmek için bu yöntemi kullanmayın. Filtreler, resme rastgele bir CSS renk değerini güvenilir bir şekilde uygulayamaz. Bu nedenle, yeniden renklendirme zahmetli ve esnek değildir.

Karar vermek için iki soru sorun. CSS'nin simgeyi seçmesi veya değiştirmesi gerekiyor mu (bileşen varsayılanları, durum değişiklikleri, temalar)? Cevabınız evet ise CSS simge sistemini kullanın. Simgeye renk dışında bir şey eklemeniz gerekiyor mu? Evet ise ve simge kaynağını kontrol ediyorsanız sembolü özel özelliklerle tasarlayın ve <use> kullanın. Kaynağı kontrol etmiyorsanız SVG'yi satır içine alın. Üçüncü taraf simge setleri nadiren özel özellikler sunar. Bu nedenle, <use> ile yalnızca renkleri değiştirebilirsiniz.

Yerleştirilmiş veya uzak simgeler

Hem CSS simge sistemi hem de <use>, bir veri URI'sinden, tek bir dosyadan veya bir sprite'tan simge alabilir. Yerleştirme (veri URI'leri veya satır içi SVG), istek göndermeyi önler ve ilk boyamayla oluşturulur ancak CSS veya HTML'yi şişirir ve ayrı olarak önbelleğe alınamaz. Bu nedenle, her sayfada görünen küçük bir simge grubu için uygundur. Bağımsız dosyalar ayrı ayrı önbelleğe alınır ve her biri bir istek maliyetiyle talep üzerine yüklenir. Sprite, tüm grup için önbelleğe alınabilir tek bir istektir ancak kullanılsın veya kullanılmasın her simgeyi indirir ve <use> yalnızca aynı kaynaktan bir sprite'a başvurabilir. Birkaç temel simge için yerleştirmeyi, daha büyük gruplar için ise bir sprite veya ayrı dosyaları tercih edin.

Bir sayfa aynı anda binlerce simge oluşturuyorsa gerçek sayfayı ve öğeleri karşılaştırın. Bir stres testinde, satır içi SVG'nin bu ölçekte küçük bir performans avantajı vardı ancak sonuçlar tarayıcıya ve uygulamaya bağlıdır. Cloud Four'un SVG simgesi stres testi

Birincil Yaklaşım: CSS Tabanlı Simge Motoru

Simgeler, tasarım jetonlarıdır: Her simge, bir resim içeren özel bir özelliktir ve herhangi bir öğe, --icon-start veya --icon-end ayarlanarak bir simge gösterebilir. Motor, üç CSS özelliğini birleştirir:

  • Kayıtlı özel özellikler (@property), --icon-start ve --icon-end'nin devralınmasını engeller. Bu nedenle, bir bileşende ayarlanan simge alt bileşenlerine aktarılmaz.
  • Kapsayıcı stil sorguları (@container style(...)), bir öğeye simge atandığını algılar ve sınıf veya işaretleme değişikliği olmadan öğeyi oluşturur.
  • CSS maskeleri ve currentColor, simgeyi öğenin metin rengini takip eden bir sözde öğede oluşturur. Boyut, renk, boşluk ve dikey ofset, mantıklı yedeklerle devralınabilir --icon-style-* özelliklerinden okunur. Böylece, tüm alt ağaç bir üst öğeden yeniden stillendirilebilir. --icon-style- ön eki, bu ayarların --icon-* altında bulunan simge jetonlarıyla çakışmasını önler.

Simge tasarım jetonlarını tanımlama

SVG simgelerini ve tüm --icon-style-* geçersiz kılmaları, satır içi SVG veri URI'lerini veya köke göre URL'leri (ör. url("/icons/trash.svg")) kullanarak yeniden kullanılabilir özel özellikler olarak tanımlayın. Bir simge harici bir SVG dosyasından yüklendiğinde köke göre URL veya yerleşik veri URI'si kullanın. Özel bir nitelik aracılığıyla kullanılan göreli URL, özel niteliğin tanımlandığı stil sayfası yerine simgenin kullanıldığı stil sayfasına göre çözümlenebilir. Bu stil sayfaları farklı dizinlerdeyse URL yanlış dosyaya çözümlenebilir.

Bir URL'yi simge jetonunu tanımlayan stil sayfasına göre tutmak için kayıtlı bir mülk yardımcı olur: Kayıtlı bir <url> veya <image> öğesinin hesaplanan değeri, onu tanımlayan stil sayfasına göre çözümlenir. Jetonu initial-value içindeki URL ile <image> olarak kaydetmeye GÜVENMEYİN. Chrome ve Safari, initial-value URL'lerini dokümana göre çözümler. Bu nedenle, bu özellik yalnızca Firefox'ta çalışır. Chrome, söz dizimi <url> ile başlamadığı sürece :root üzerinde tanımlanan değerleri de dokümana göre çözümler.

Farklı dizinlerdeki stil sayfaları gibi göreceli URL'lerin kaçınılmaz olduğu durumlarda jetonu <url> ile başlayan bir söz dizimiyle kaydedin ve gerçek URL'yi :root üzerinde bildirin. Bu, her tarayıcıda bildirimde bulunan stil sayfasına göre çözümlenir:

@property --icon-caret {
  /* The syntax must start with <url>. A syntax that starts with <image> resolves against the document in Chrome. */
  syntax: "<url> | <image> | none";
  inherits: true;
  initial-value: none;
}

:root {
  /* Declared here, the URL resolves against this stylesheet */
  --icon-caret: url("icons/caret.svg");
}

Artık her simge iki yerde bulunur ve çok daha fazla ortak kod vardır. Bu nedenle, bu yöntem yalnızca bir projenin farklı dizinlerde stil sayfaları ve yönetilebilir sayıda simgesi olduğunda kullanışlıdır. On kadar simgeden sonra, her bir @property öğesini manuel olarak yazmak yerine aynı söz dizimini kullanarak CSS.registerProperty ile bir komut dosyasından --icon-* jetonlarını (--icon-style-* hariç) kaydedin. Kayıtlı özellikler geneldir ve gölge köklerde çalışmaz. Bu nedenle her projeye uygun olmayabilir. Kök göreli URL'ler veya veri URI'ları bir seçenekse bunlar daha basittir ve her yerde çalışır.

:root {
  /* Optional: override the built-in fallbacks (1em, currentColor, 0.3em, -0.02em) */
  --icon-style-size: 1em;
  --icon-style-spacing: 0.4em;
  --icon-trash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0-1 14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2L4 6h16Z'/%3E%3C/svg%3E");
  --icon-favorite: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z'/%3E%3C/svg%3E");
}

Jetonlardan simge oluşturma

Bir öğe, --icon-start (başta) veya --icon-end (sonda) parametresini yukarıdaki jetonlardan birine ayarlayarak simge gösterir. İki CSS parçası, bu atamayı oluşturulmuş bir simgeye dönüştürür.

Öncelikle --icon-start ve --icon-end'yi @property ile kaydedin. "*" söz dizimi her değeri kabul eder ve inherits: false önemli kısımdır: Bu olmadan, bir düğmede ayarlanan simge her alt öğeye devralınır ve her biri simgeyi de oluşturur.

@property --icon-start {
  syntax: "*";
  inherits: false;
}

@property --icon-end {
  syntax: "*";
  inherits: false;
}

İkinci olarak, kapsayıcı stili sorgusu, özelliğin bir öğede ne zaman ayarlandığını algılar ve simgeyi bir sözde öğede oluşturur. Sözde öğe --icon-start değerini devralmadığından (devralmayan olarak kaydedildi) ilk bildirim, değeri inherit ile açıkça yönlendirir. Kalan bildirimler boyutlandırma, maskeleme, hizalama ve boşlukları işler. Dolgu varsayılan olarak currentColor olduğundan simge, öğenin metin rengini alır ve geçişlerini takip eder.

/* Style query for elements configuring a start icon */
@container style(--icon-start) {
  ::before {
    --icon-start: inherit; /* Forward the value inside the query block */
    content: "" / var(--icon-style-alt, "");
    display: inline-block;
    width: var(--icon-style-size, 1em);
    height: var(--icon-style-size, 1em);
    background-color: var(--icon-style-color, currentColor);
    mask: var(--icon-start) no-repeat center / contain;
    /* Center on the cap height, nudged slightly up for optical balance */
    vertical-align: calc(0.5cap - var(--icon-style-size, 1em) / 2 + var(--icon-style-voffset, -0.02em));
    flex: none;
    pointer-events: none;
    forced-color-adjust: none;
  }

  /* Automatically add spacing ONLY if the element has other text or sibling content */
  :where(:not(.icon, :empty))::before {
    margin-inline-end: var(--icon-style-spacing, 0.3em);
  }
}

Her beyan, kendi yerini kazanır:

  • vertical-align: middle, x yüksekliğinin yarısına hizalanır. Bu, çoğu simge seti için belirgin şekilde alçaktır. 0.5cap üzerinde ortalama, --icon-style-size büyüdükçe simgenin merkezini sabit tutar ve küçük negatif --icon-style-voffset, gözün bir simgenin üstündeki boşluğu altındaki boşluktan daha fazla ağırlıklandırmasını telafi eder.
  • flex: none: Boş bir sözde öğenin minimum boyutu sıfırdır. Bu nedenle, esnek bir düğmenin içinde etiket uzun olduğunda ilk olarak bu öğe küçülür ve potansiyel olarak tamamen kaybolur. Simge boyutu, düzen baskısının müdahale etmemesi gereken bir tasarım kararıdır.
  • pointer-events: none: Simgenin tıklamaları yakalamasını veya event.target olmasını engeller.
  • forced-color-adjust: none: Zorunlu renkler modunda tarayıcı, background-color değerini değiştirerek maskelenmiş bir simgenin kaybolmasına neden olur. Maske dolgusu korunur ve currentColor, zorunlu metin rengini kullanmaya devam eder.
  • content: "" / var(--icon-style-alt, ""): Oluşturulan içerik için alternatif metin, yalnızca simge içeren bir kontrolün, --icon-style-alt aracılığıyla simgeden erişilebilir özellikli adını almasına olanak tanır (aşağıdaki Yalnızca Simge İçeren Kontroller bölümüne bakın). Varsayılan boş alt özelliği, dekoratif simgeleri yardımcı teknolojiden gizler.

Sondaki simge için --icon-start ve ::before yerine --icon-end ve ::after ile aynı kalıbı kullanın. mask: var(--icon-end) kullanın ve margin-inline-end yerine margin-inline-start ile boşluk uygulayın.

Bağımsız ve yalnızca simge içeren öğeler

Bağımsız simgeler veya yalnızca simge içeren düğmeler için .icon özelliğini doğrudan boş öğeye (yalnızca simge içeren bir kontrol için düğmenin kendisine) uygulayın ve simgesini --icon ile ayarlayın. Burada --icon, daha üst düzey bir özelliktir. .icon:empty kuralı, bu özelliği bir başta yer alan simge için --icon-start ile eşler:

.icon:empty {
  --icon-start: var(--icon);
}

Kullanım Örnekleri

Stil sayfasındaki bileşen varsayılanları

Simgelerin özel özellik olarak kullanılmasının temel avantajı, basamaklı düzene dahil olmalarıdır. Bir seçiciye simge ayarladığınızda, işaretleme değişikliği olmadan eşleşen her öğe bu simgeyi alır:

.button-delete {
  --icon-start: var(--icon-trash);
}

a[href^="http"]:not([href*="example.com"]) {
  --icon-end: var(--icon-external);
}
<button class="button-delete">Delete item</button>

Duruma göre değişen simgeler

Simge yalnızca bir özellik olduğundan durum değişikliği sıradan bir CSS geçersiz kılma işlemidir. Gösterilecek veya gizlenecek ikinci bir öğe ve açılıp kapatılacak bir sınıf yoktur:

.button-expand {
  --icon-start: var(--icon-caret-right);
}

.button-expand[aria-expanded="true"] {
  --icon-start: var(--icon-caret-down);
}

Aynı durum sunum için de geçerlidir: Araç çubuğu gibi bir kapsayıcıda --icon-style-size, --icon-style-color veya --icon-style-spacing değerini ayarladığınızda içindeki her simge güncellenir.

Satır içi stillere sahip tek seferlik simgeler

Hiçbir bileşen kuralının kapsamadığı bir öğe, aynı özelliği satır içi olarak ayarlayabilir (ör. <button style="--icon-start: var(--icon-trash)">). Bunu varsayılan olarak değil, kaçış yolu olarak kullanın: Aynı simge aynı tür öğede birden fazla kez göründüğünde, simgeyi bir stil sayfası kuralına taşıyın.

Yalnızca Simge İçeren Kontroller

Yalnızca simge içeren düğmeler, yukarıda açıklanan .icon sınıfını ve --icon kullanır. aria-label ile düğmeye erişilebilir özellikli ad verin veya simgenin --icon-style-alt aracılığıyla bir ad sağlamasına izin verin. Bunlar olmadan CSS tarafından oluşturulan simge dekoratiftir. Bağımsız bilgilendirici simgeler için .icon öğesini role="img" ve aria-label ile gösterin:

.button-close {
  --icon: var(--icon-close);
  --icon-style-alt: "Close";
}
<!-- Icon-only interactive control, icon and name from the stylesheet -->
<button class="icon button-close"></button>

<!-- Icon-only interactive control, named via aria-label -->
<button class="icon" style="--icon: var(--icon-trash);" aria-label="Delete item"></button>

<!-- Standalone informative icon -->
<span class="icon" style="--icon: var(--icon-favorite);" role="img" aria-label="Favorite"></span>

Doğrudan SVG ile çalışma

CSS simge sistemi uygun olmadığında satır içi SVG ve <use> öğelerini alternatif olarak değil, birlikte kullanın. Bir simge sayfada ilk kez göründüğünde SVG'yi satır içine alın ve her tekrarda <use href="#id"> ile referans verin. Yerel referans için istek gönderilmez ve çapraz kaynak kısıtlaması yoktur. Birçok sayfada paylaşılan simgeler için harici bir sprite (<use href="/icons.svg#id">) ayırın.

<use> ile referans verilen içerik bir gölge ağaca kopyalanır. Bu nedenle, sayfa CSS'si öğelerini seçemez. Devralma kapsamına girenler: color, currentColor ve tüm özel özellikler. Modern CSS, SVG sunum özelliklerinin yanı sıra birçok geometri özelliğini de değiştirebilir. Bu sayede, kontrol ettiğiniz bir simge tam olarak ihtiyacınız olan kancaları gösterebilir (ör. belirli bir yolda stroke-width="var(--icon-stroke, 2)", hatta d: var(--icon-path, path(…))).

CSS d, Safari'de desteklenmez. Bu nedenle, bu özelleştirmeyi yalnızca bu destek boşluğu kabul edilebilir olduğunda kullanın. Simgeyi kontrol etmiyorsanız satır içine alın.

Bilmeniz gereken <use> önemli nokta:

  • viewBox, <symbol> veya referans verilen <svg> üzerinde olmalıdır. <use> öğesini sarmalayan dış <svg>, referanstan almaz. Bu nedenle, sarmalayıcıda width ve height değerlerini ayarlayın ve viewBox değerini sembolle birlikte tutun.
  • Stiller, sınırları her iki yönde de aşmaz: Harici bir sprite içindeki <style> sayfaya uygulanmaz ve sayfa stil sayfaları, referans verilen ağaç içinde uygulanmaz. Yalnızca devralınan özellikler ve özel özellikler aktarılır.
  • Harici referanslar yalnızca aynı kaynaklıdır; çapraz kaynaklı sprite'lar sessizce başarısız olur.
  • Dokümana satır içi olarak eklenen sprite'lar, sayfanın id ad alanını paylaşır. Bu nedenle, çakışmaları önlemek için ad alanı sembol kimlikleri kullanılır.

Dekoratif SVG'leri aria-hidden="true" ile işaretleyin ve CSS simge sisteminde olduğu gibi, kapsayıcı denetime kendi erişilebilir özellikli adını verin.

Yedek Stratejiler

Kayıtlı Özel Özellikler

@property olmadan --icon-start ve --icon-end, normal devralan özel özellikler gibi davranır. Bu nedenle, bir öğede ayarlanan simge, alt öğelerinde de oluşturulur. Bunu önlemek için stil sorgusundaki (@container style(--icon-start) { * { --icon-start: initial; } }) alt öğelerin özelliklerini sıfırlayın. Aşağıdaki Safari geçici çözümünde de aynı yaklaşım kullanılır.

Kapsayıcı stil sorguları

Kapsayıcı stil sorguları olmayan tarayıcıları desteklemek için yedek kanca olarak sınıfları veya bileşen seçicileri kullanın. Özel özelliklerde simge seçimini koruyun. Satır içi stil seçiciler, CSS kuralları aracılığıyla atanan simgeleri tek başına kaçırır.

:where(.icon-start, .icon:empty)::before {
  content: image-set(linear-gradient(transparent 0 100%), var(--icon-start)) / var(--icon-style-alt, "");
  /* icon styles */
}

:where(.icon-start)::before {
  --icon-start: inherit;
}

:where(.icon:empty)::before {
  --icon-start: var(--icon);
}

:where(.icon-start:not(.icon, :empty))::before {
  margin-inline-end: var(--icon-style-spacing, 0.3em);
}

Sondaki simgeler için .icon-start, --icon-start ve ::before yerine .icon-end, --icon-end ve ::after ile aynı sınıf kancası yedeklerini kullanın. Boş olmayan içeriklerde margin-inline-end yerine margin-inline-start ile boşluk uygulayın.

Bu image-set() kalıbı, özel özellik ayarlanmadığında content değerini geçersiz kılar. Bu nedenle, none değerine geri döner ve sözde öğe, sınıf eklemeden veya kaldırmadan kaybolur. Sözde öğenin diğer stillerini kaldırmaz veya diğer ::before ya da ::after kurallarıyla çakışmaları önlemez. Bu nedenle, bu kancaları yalnızca söz konusu sözde öğelerin simgeler için kullanılabildiği yerlerde kullanın.

Safari Tarayıcı Geçici Çözümleri

Safari'de simgeleri etkileyen iki oluşturma hatası vardır: WebKit #301609, kullanıcı etkileşimine kadar simgelerin görünmesini engelleyebilir ve #320220 da simgelerin oluşturulmasını etkiler. Aşağıdaki geçici çözüm her iki sorunu da giderir:

@keyframes webkit-301609 {}

@supports ((-webkit-nbsp-mode: normal) or (-webkit-touch-callout: none) or (font: -apple-system-body)) and (content-visibility: auto) {
  @layer webkit-301609-fix {
    ::before, ::after {
      animation: webkit-301609 0s;
    }
  }
}

@supports sorgusu yalnızca Safari'yi hedefler ve @layer animasyon çakışmalarını azaltır.

WebKit hatası #325900, bazı durumlarda (ör. fareyle üzerine gelindiğinde simgeyi değiştirme) --icon-start öğesinin doğrudan geçersiz kılınmasının geçerli olmasını engelleyebilir. Bu durum, devralmayan kayıtlı mülkler için geçici çözümü etkiler. Safari için --icon-start özelliğini devralma olarak kaydedin ve stil sorgusundaki alt öğelerde sıfırlayın:

@supports ((-webkit-nbsp-mode: normal) or
           (-webkit-touch-callout: none) or
           (font: -apple-system-body)) and (content-visibility: auto) {
  /* Override previous declaration */
  @property --icon-start {
    syntax: "*";
    inherits: true;
  }

  @layer webkit-325900-fix {
    @container style(--icon-start) {
      * {
        --icon-start: initial;
      }
    }
  }
}

Sıfırlama işlemi, simge değerinin alt öğelere yayılmasını engeller. Katmanlı olmayan simge bildirimlerinin sıfırlama işlemine göre öncelikli olması için bu kuralı bir katmanda tutun.