CSS :has(), :is(), :where() Seçicileri

CSS :has(), :is(), :where() Seçicileri
Modern CSS'in en heyecan verici gelişmelerinden biri, ileri seviye pseudo-class seçicileridir. :has() "parent selector" olarak CSS'in yıllardır eksik olan parçasını tamamlarken, :is() ve :where() seçici yazımını sadeleştirir ve bakımı kolaylaştırır. Bu rehberde üç seçicinin detaylı kullanımını, aralarındaki farkları ve gerçek dünya örneklerini keşfedeceğiz.
1. :is() — Seçici Gruplandırma
:is() pseudo-class'ı, birden fazla seçiciyi gruplandırarak CSS'i daha okunabilir ve kısa hale getirir. Özellikle iç içe (nested) seçicilerde tekrar yazmayı ortadan kaldırır. Specificity olarak, parametre listesindeki en yüksek specificity'ye sahip seçicinin değerini alır.
/* Eski yöntem — tekrarlı ve uzun */
header a:hover,
nav a:hover,
footer a:hover,
aside a:hover {
color: #3b82f6;
text-decoration: underline;
}
/* :is() ile sadeleştirilmiş */
:is(header, nav, footer, aside) a:hover {
color: #3b82f6;
text-decoration: underline;
}
/* İç içe seçicilerde büyük fayda */
/* Eski yöntem */
.article h1,
.article h2,
.article h3,
.article h4 {
line-height: 1.3;
margin-top: 2rem;
}
/* :is() ile */
.article :is(h1, h2, h3, h4) {
line-height: 1.3;
margin-top: 2rem;
}
/* Karmaşık kombinasyonlar */
:is(.card, .panel, .modal) :is(h2, h3) {
font-weight: 700;
color: var(--color-heading);
}
/* Form elemanları */
:is(input, textarea, select):is(:focus, :focus-visible) {
border-color: var(--color-primary);
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Hata durumları */
:is(input, textarea, select):is(.error, :invalid) {
border-color: #ef4444;
}
:is() forgiving selector list kullanır. Yani parametre listesindeki bir seçici geçersizse, diğerleri yine de çalışır. Bu, :is(-webkit-something, .class) gibi tarayıcıya özgü seçicilerle güvenle kullanılabilmesi anlamına gelir.
2. :where() — Sıfır Specificity
:where(), :is() ile aynı gruplandırma işlevini görür ancak kritik bir farkla: specificity'si her zaman sıfırdır. Bu özellik, CSS kütüphaneleri, reset stilleri ve utility class'lar yazarken sonradan kolayca override edilebilir stiller oluşturmak için idealdir.
/* :where() ile sıfır specificity reset */
:where(h1, h2, h3, h4, h5, h6) {
margin: 0;
font-weight: inherit;
font-size: inherit;
}
:where(ul, ol) {
list-style: none;
padding: 0;
margin: 0;
}
:where(a) {
color: inherit;
text-decoration: none;
}
/* Kütüphane stilleri — kolayca override edilebilir */
:where(.btn) {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-weight: 500;
cursor: pointer;
border: none;
}
:where(.btn-primary) {
background: #3b82f6;
color: white;
}
/* Kullanıcı kendi stilini kolayca override edebilir */
.btn-primary {
background: #8b5cf6; /* Bu override çalışır çünkü :where specificity = 0 */
}
/* :is() vs :where() Specificity karşılaştırma */
/* :is(.container, #app) .title → specificity: (1,0,1) — #app'ın specificity'si alınır */
/* :where(.container, #app) .title → specificity: (0,0,1) — her zaman sıfır */
3. :has() — Parent/Relational Selector
:has(), CSS tarihinde bir devrimdir. İlk kez bir elemanı, içerdiği çocuklara veya kardeş elemanlara göre seçebiliyoruz. "Parent selector" olarak bilinse de aslında bir "relational pseudo-class"tır ve çok daha güçlüdür.
/* İçinde img olan card'lara özel stil */
.card:has(img) {
padding: 0;
overflow: hidden;
}
.card:has(img) .card-content {
padding: 1.5rem;
}
/* İçinde img olmayan card */
.card:not(:has(img)) {
border-left: 4px solid var(--color-primary);
}
/* Focus olan input'un label'ını renklendir */
.form-group:has(input:focus) label {
color: var(--color-primary);
font-weight: 600;
}
/* Hatalı input'un tüm grubunu stillendir */
.form-group:has(:invalid) {
border-color: #ef4444;
}
.form-group:has(:invalid) label {
color: #ef4444;
}
.form-group:has(:invalid) .error-message {
display: block;
}
/* Checkbox checked olduğunda kardeş elemanı stillendir */
.todo-item:has(input[type="checkbox"]:checked) .todo-text {
text-decoration: line-through;
opacity: 0.6;
}
/* Boş state — içinde .item olmayan liste */
.list:not(:has(.item)) .empty-state {
display: flex;
}
.list:has(.item) .empty-state {
display: none;
}
/* Sticky header — scroll durumunu CSS ile yönetme */
html:has(dialog[open]) {
overflow: hidden; /* Dialog açıkken scroll'u kapat */
}
/* Quantity selector */
.cart-item:has(.quantity-input:invalid) .add-button {
opacity: 0.5;
pointer-events: none;
}
:has() güçlü ama performans açısından dikkatli kullanılmalıdır. Çok geniş seçicilerden kaçının: *:has(img) yerine .card:has(img) gibi spesifik seçiciler tercih edin. Tarayıcılar optimizasyon yapsa da, gereksiz geniş sorgular sayfa performansını etkileyebilir.
4. Üç Seçiciyi Birlikte Kullanma
Bu üç seçici birlikte kullanıldığında, daha önce JavaScript gerektiren birçok senaryoyu saf CSS ile çözebilirsiniz. Özellikle form validasyonu, dinamik UI durumları ve koşullu stiller için güçlü kombinasyonlar oluşturulabilir.
/* Gelişmiş Form Validasyonu — Tamamen CSS */
.form-field:has(:is(input, textarea, select):focus) {
--field-border: var(--color-primary);
--field-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}
.form-field:has(:is(input, textarea, select):user-invalid) {
--field-border: #ef4444;
--field-shadow: 0 0 0 3px rgba(239, 68, 68, 0.15);
}
/* Dark mode component adaptasyonu */
:where([data-theme="dark"]) :is(.card, .panel, .modal) {
background: var(--color-bg-secondary);
border-color: var(--color-border);
}
/* Navigation — aktif sayfa vurgusu */
nav:has(a[aria-current="page"]) {
border-bottom: 2px solid var(--color-primary);
}
nav a[aria-current="page"] {
color: var(--color-primary);
font-weight: 600;
}
/* Sidebar açık/kapalı durumu */
.layout:has(.sidebar[data-expanded="true"]) .main-content {
margin-left: 280px;
}
.layout:has(.sidebar:not([data-expanded="true"])) .main-content {
margin-left: 64px;
}
:is() ve :where() arasında seçim yaparken şu kuralı uygulayın: Kendi proje stillerinizde :is(), başkalarının override etmesi gereken kütüphane/reset stillerinde :where() kullanın.
Sonuç
:has(), :is() ve :where() seçicileri CSS'in ifade gücünü büyük ölçüde artırmıştır. :is() ile tekrar yazmayı ortadan kaldırır, :where() ile kolayca override edilebilir stiller oluşturur, :has() ile daha önce JavaScript gerektiren koşullu stilleri saf CSS'e taşırsınız. Bu üç seçici modern CSS'in olmazsa olmazlarıdır ve hemen projelerinize entegre etmeye başlayabilirsiniz.