Tasarım sistemlerinde ve bileşen kitaplıklarında özel özellikler kullanmanın avantajları
Ben Dave. Nordhealth'te kıdemli kullanıcı arabirimi tasarımcısı olarak çalışıyorum. Nord adlı tasarım sistemimizin tasarım ve geliştirme süreçlerinde çalışıyorum. Bu süreçler, bileşen kitaplığımız için web bileşenleri oluşturmayı da kapsıyor. CSS özel niteliklerini kullanarak web bileşenlerini stillendirmeyle ilgili sorunları nasıl çözdüğümüzü ve tasarım sistemleri ile bileşen kitaplıklarında özel nitelikler kullanmanın diğer avantajlarından bazılarını paylaşmak istedim.
Web Bileşenleri'ni nasıl oluştururuz?
Web Bileşenlerimizi oluşturmak için durum, kapsamlı stiller, şablon oluşturma ve daha fazlası gibi birçok ortak metin kodu sağlayan bir kitaplık olan Lit'i kullanıyoruz. Lit yalnızca hafif olmakla kalmaz, aynı zamanda yerel JavaScript API'leri üzerine de kuruludur. Bu sayede, tarayıcının halihazırda sahip olduğu özelliklerden yararlanan yalın bir kod paketi sunabiliriz.
import {html, css, LitElement} from 'lit';
export class SimpleGreeting extends LitElement {
static styles = css`:host { color: blue; font-family: sans-serif; }`;
static properties = {
name: {type: String},
};
constructor() {
super();
this.name = 'there';
}
render() {
return html`Hey
${this.name}, welcome to Web Components!`;
}
}
customElements.define('simple-greeting', SimpleGreeting);
Ancak Web Bileşenleri'nin en cazip özelliği, mevcut JavaScript çerçevelerinin neredeyse tamamıyla, hatta çerçeve olmadan bile çalışabilmesidir. Ana JavaScript paketi sayfada referans gösterildikten sonra web bileşeni kullanmak, yerel bir HTML öğesi kullanmaya çok benzer. Yerel bir HTML öğesi olmadığını gösteren tek gerçek işaret, etiketlerin içindeki tutarlı kısa çizgidir. Bu, tarayıcıya bunun bir web bileşeni olduğunu belirten bir standarttır.
Gölge DOM stili kapsülleme
Yerel HTML öğelerinin Shadow DOM'u olduğu gibi Web Bileşenleri'nin de Shadow DOM'u vardır. Gölge DOM, bir öğe içindeki gizli bir düğüm ağacıdır. Bunu görselleştirmenin en iyi yolu, web denetleyicinizi açıp "Gölge DOM ağacını göster" seçeneğini etkinleştirmektir. Bunu yaptıktan sonra, denetleyicide yerel bir giriş öğesine bakmayı deneyin. Artık bu girişi açma ve içindeki tüm öğeleri görme seçeneğiniz olacak. Bunu web bileşenlerimizden biriyle de deneyebilirsiniz. Gölge DOM'unu görmek için özel giriş bileşenimizi incelemeyi deneyin.
Gölge DOM'un avantajlarından (veya bakış açınıza bağlı olarak dezavantajlarından) biri, stil kapsüllemedir. Web Bileşeninizde CSS yazarsanız bu stiller dışarı sızamaz ve ana sayfayı veya diğer öğeleri etkileyemez. Tamamen bileşenin içinde yer alırlar. Ayrıca, ana sayfa veya üst web bileşeni için yazılan CSS, web bileşeninize sızamaz.
Stillerin bu şekilde kapsüllenmesi, bileşen kitaplığımızda bir avantajdır. Bu sayede, üst sayfaya uygulanan stillerden bağımsız olarak, bileşenlerimizden biri kullanıldığında istediğimiz gibi görüneceğinden daha emin olabiliriz. Ayrıca, tüm web bileşenlerimizin köküne veya "ana makinesine" all: unset; ekleriz.
:host {
all: unset;
display: block;
box-sizing: border-box;
text-align: start;
/* ... */
}
Ancak web bileşeninizi kullanan birinin belirli stilleri değiştirmek için geçerli bir nedeni varsa ne olur? Belki de bağlamı nedeniyle daha fazla kontrasta ihtiyaç duyan bir metin satırı veya daha kalın olması gereken bir kenarlık vardır. Bileşeninize hiçbir stil uygulanamıyorsa bu stil seçeneklerini nasıl etkinleştirebilirsiniz?
CSS özel özellikleri işte bu noktada devreye girer.
CSS özel özellikleri
Özel özellikler, adlarına uygun şekilde tamamen kendiniz adlandırabileceğiniz ve gereken değeri uygulayabileceğiniz CSS özellikleridir. Tek şart, bu değişkenlerin önüne iki kısa çizgi eklemenizdir. Özel niteliğinizi tanımladıktan sonra, değeri var() işlevini kullanarak CSS'nizde kullanabilirsiniz.
:root {
--n-color-accent: rgb(53, 89, 199);
/* ... */
}
.n-color-accent-text {
color: var(--n-color-accent);
}
Devralma söz konusu olduğunda, tüm özel özellikler devralınır. Bu, normal CSS özelliklerinin ve değerlerinin tipik davranışına uygundur. Üst öğeye veya öğenin kendisine uygulanan tüm özel özellikler, diğer özelliklerde değer olarak kullanılabilir. Tasarım jetonlarımız için özel nitelikleri yoğun bir şekilde kullanıyoruz. Bu nitelikleri CSS çerçevemizi kullanarak kök öğeye uyguluyoruz. Bu sayede, sayfadaki tüm öğeler bu jeton değerlerini kullanabilir. Bu öğeler web bileşeni, CSS yardımcı sınıfı veya jeton listemizden bir değer almak isteyen bir geliştirici olabilir.
var() işlevini kullanarak özel özellikleri devralma özelliği, web bileşenlerimizin gölge DOM'unu delip geliştiricilerin bileşenlerimizi stillendirirken daha ayrıntılı kontrol sahibi olmasını sağlar.
Nord Web Bileşeni'ndeki özel özellikler
Tasarım sistemimiz için bir bileşen geliştirirken CSS'sine dikkatli bir yaklaşım sergiliyoruz. Yalın ancak bakımı kolay bir kod hedefliyoruz. Tasarım jetonlarımız, kök öğedeki ana CSS çerçevemizde özel özellikler olarak tanımlanır.
:root {
--n-space-m: 16px;
--n-space-l: 24px;
/* ... */
--n-color-background: rgb(255, 255, 255);
--n-color-border: rgb(216, 222, 228);
/* ... */
}
Bu jeton değerleri daha sonra bileşenlerimizde referans olarak kullanılır. Bazı durumlarda değeri doğrudan CSS özelliğine uygularız ancak diğer durumlarda yeni bir bağlamsal özel özellik tanımlar ve değeri buna uygularız.
:host {
--n-tab-group-padding: 0;
--n-tab-list-background: var(--n-color-background);
--n-tab-list-border: inset 0 -1px 0 0 var(--n-color-border);
/* ... */
}
.n-tab-group-list {
box-shadow: var(--n-tab-list-border);
background-color: var(--n-tab-list-background);
gap: var(--n-space-s);
/* ... */
}
Ayrıca, bileşene özgü olan ancak jetonlarımızda bulunmayan bazı değerleri soyutlayıp bağlamsal bir özel niteliğe dönüştüreceğiz. Bileşene göre bağlamsal olan özel özellikler bize iki önemli avantaj sağlar. İlk olarak, bu değer bileşenin içindeki birden fazla özelliğe uygulanabileceğinden CSS'mizle daha "kuru" olabileceğimiz anlamına gelir.
.n-tab-group-list::before {
/* ... */
padding-inline-start: var(--n-tab-group-padding);
}
.n-tab-group-list::after {
/* ... */
padding-inline-end: var(--n-tab-group-padding);
}
İkinci olarak, bileşen durumu ve varyasyon değişikliklerini çok temiz hale getirir. Örneğin, bir fareyle üzerine gelme veya etkin durum ya da bu durumda bir varyasyon için stil oluştururken tüm bu özellikleri güncellemek için yalnızca özel niteliğin değiştirilmesi gerekir.
:host([padding="l"]) {
--n-tab-group-padding: var(--n-space-l);
}
Ancak en güçlü avantajı, bir bileşende bu bağlamsal özel özellikleri tanımladığımızda her bir bileşenimiz için özel bir CSS API oluşturmamızdır. Bu API, söz konusu bileşenin kullanıcısı tarafından kullanılabilir.
<nord-tab-group label="Title">
<!-- ... -->
</nord-tab-group>
<style>
nord-tab-group {
--n-tab-group-padding: var(--n-space-xl);
}
</style>
Yukarıdaki örnekte, bağlamsal özel niteliği bir seçici kullanılarak değiştirilmiş web bileşenlerimizden biri gösterilmektedir. Bu yaklaşımın sonucunda, kullanıcıya yeterli stil esnekliği sağlarken gerçek stillerin çoğunu kontrol altında tutan bir bileşen elde edilir. Ayrıca bileşen geliştiricileri olarak, kullanıcı tarafından uygulanan bu stilleri yakalayabiliriz. Bu özelliklerden birini ayarlamak veya genişletmek istersek kullanıcının kodunu değiştirmesine gerek kalmadan bunu yapabiliriz.
Bu yaklaşımın, yalnızca tasarım sistemi bileşenlerimizin oluşturucuları olarak bizim için değil, aynı zamanda bu bileşenleri ürünlerimizde kullanan geliştirme ekibimiz için de son derece etkili olduğunu düşünüyoruz.
Özel özellikleri daha ileriye taşıma
Bu makalenin yazıldığı tarih itibarıyla, bu bağlamsal özel nitelikleri belgelerimizde açıklamıyoruz. Ancak daha geniş geliştirme ekibimizin bu özellikleri anlayıp bunlardan yararlanabilmesi için açıklamayı planlıyoruz. Bileşenlerimiz, npm'de haklarında bilinmesi gereken her şeyi içeren bir manifest dosyasıyla paketlenir. Ardından, doküman sitemiz dağıtıldığında manifesto dosyasını veri olarak kullanırız. Bu işlem, Eleventy ve Global Veri özelliği kullanılarak yapılır. Bu bağlamsal özel özellikleri manifest veri dosyasına eklemeyi planlıyoruz.
İyileştirmek istediğimiz bir diğer alan da bu bağlamsal özel özelliklerin değerleri nasıl devraldığıdır. Örneğin, iki ayırıcı bileşenin rengini ayarlamak istiyorsanız artık seçicilerle bu bileşenlerin her ikisini de özel olarak hedeflemeniz veya özel niteliği doğrudan stil özelliğine sahip öğeye uygulamanız gerekir. Bu durum sorunsuz görünebilir ancak geliştirici bu stilleri kapsayıcı bir öğede, hatta kök düzeyinde tanımlayabilirse daha faydalı olur.
<nord-divider></nord-divider>
<section>
<nord-divider></nord-divider>
<!-- ... -->
</section>
<style>
nord-divider {
--n-divider-color: var(--n-color-status-danger);
}
section {
padding: var(--n-space-s);
background: var(--n-color-surface-raised);
}
section nord-divider {
--n-divider-color: var(--n-color-status-success);
}
</style>
Özel özellik değerini doğrudan bileşende ayarlamanızın nedeni, bunları bileşen ana makine seçicisini kullanarak aynı öğede tanımlamamızdır. Doğrudan bileşende kullandığımız global tasarım jetonları bu sorundan etkilenmeden doğrudan geçer ve hatta üst öğelerde kesilebilir. Her iki dünyanın da en iyi yönlerinden nasıl yararlanabiliriz?
Özel ve herkese açık özel nitelikler
Özel gizli nitelikler, Lea Verou tarafından bir araya getirilen bir kavramdır. Bu kavram, bileşenin kendisinde bağlamsal bir "gizli" özel nitelik olup yedekli bir "herkese açık" özel niteliğe ayarlanır.
:host {
--_n-divider-color: var(--n-divider-color, var(--n-color-border));
--_n-divider-size: var(--n-divider-size, 1px);
}
.n-divider {
border-block-start: solid var(--_n-divider-size) var(--_n-divider-color);
/* ... */
}
Bağlamsal özel niteliklerimizi bu şekilde tanımlamak, daha önce yaptığımız her şeyi yapmaya devam edebileceğimiz anlamına gelir. Örneğin, global jeton değerlerini devralabilir ve bileşen kodumuzda değerleri yeniden kullanabiliriz. Ancak bileşen, bu niteliğin kendisindeki veya herhangi bir üst öğedeki yeni tanımlarını da sorunsuz bir şekilde devralır.
<nord-divider></nord-divider>
<section>
<nord-divider></nord-divider>
<!-- ... -->
</section>
<style>
nord-divider {
--n-divider-color: var(--n-color-status-danger);
}
section {
padding: var(--n-space-s);
background: var(--n-color-surface-raised);
--n-divider-color: var(--n-color-status-success);
}
</style>
Bu yöntemin gerçekten "gizli" olmadığı iddia edilse de, endişelendiğimiz bir soruna oldukça zarif bir çözüm olduğunu düşünüyoruz. Fırsat bulduğumuzda bu sorunu bileşenlerimizde ele alacağız. Böylece geliştirme ekibimiz, mevcut koruma önlemlerimizden yararlanmaya devam ederken bileşen kullanımı üzerinde daha fazla kontrole sahip olacak.
CSS özel nitelikleriyle Web Bileşenleri'ni nasıl kullandığımızla ilgili bu analizi faydalı bulduğunuzu umuyoruz. Düşüncelerinizi bizimle paylaşın. Bu yöntemlerden herhangi birini kendi çalışmalarınızda kullanmaya karar verirseniz bana Twitter'da @DavidDarnes adresinden ulaşabilirsiniz. Nordhealth'i Twitter'da @NordhealthHQ adresinden takip edebilirsiniz. Bu tasarım sistemini bir araya getirmek ve bu belgede bahsedilen özellikleri uygulamak için çok çalışan ekibimin diğer üyelerini de Twitter'da bulabilirsiniz: @Viljamis, @WickyNilliams ve @eric_habich.