İçeriğe geç
CSS

CSS Custom Properties (Variables): Dinamik Tema Sistemi

Tarık Tunç
CSS Custom Properties (Variables): Dinamik Tema Sistemi

CSS Custom Properties (Variables): Dinamik Tema Sistemi

CSS Custom Properties (CSS Variables), stil sayfalarında değişken tanımlama ve kullanma imkanı sunan güçlü bir özelliktir. Preprocessor değişkenlerinin aksine, CSS Custom Properties runtime'da çalışır, cascade ve inheritance kurallarına uyar ve JavaScript ile dinamik olarak değiştirilebilir. Bu rehberde tema sistemleri, design token'lar ve dinamik stil yönetimi konularını detaylıca ele alacağız.

1. Custom Properties Temelleri

CSS Custom Properties, -- öneki ile tanımlanır ve var() fonksiyonu ile kullanılır. Genellikle :root pseudo-class'ında global olarak tanımlanır, ancak herhangi bir seçicide tanımlanabilir ve cascade kurallarına uyar.

/* Global Değişkenler — Design Token Sistemi */
:root {
  /* Renk Primitives */
  --color-blue-50: #eff6ff;
  --color-blue-500: #3b82f6;
  --color-blue-700: #1d4ed8;
  --color-gray-50: #f9fafb;
  --color-gray-900: #111827;

  /* Semantic Token'lar */
  --color-primary: var(--color-blue-500);
  --color-primary-hover: var(--color-blue-700);
  --color-bg: #ffffff;
  --color-text: var(--color-gray-900);
  --color-text-muted: #6b7280;
  --color-border: #e5e7eb;

  /* Spacing Scale */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;

  /* Typography */
  --font-sans: 'Inter', system-ui, -apple-system, sans-serif;
  --font-mono: 'Fira Code', 'Cascadia Code', monospace;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;

  /* Border Radius */
  --radius-sm: 0.25rem;
  --radius-md: 0.5rem;
  --radius-lg: 1rem;
  --radius-full: 9999px;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}

/* Kullanım */
.card {
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-lg);
  box-shadow: var(--shadow-sm);
  font-family: var(--font-sans);
}
Bilgi: var() fonksiyonu ikinci parametre olarak fallback değer alır: var(--color-primary, #3b82f6). Değişken tanımlı değilse fallback kullanılır.

2. Dark Mode Tema Sistemi

CSS Custom Properties ile tema değiştirme, sadece değişken değerlerini override etmekle mümkündür. Tüm component'ler semantic token'ları kullandığı sürece, tek bir class veya media query ile tüm sayfa teması değişir.

/* Light Theme (varsayılan) */
:root {
  --color-bg: #ffffff;
  --color-bg-secondary: #f3f4f6;
  --color-text: #111827;
  --color-text-secondary: #6b7280;
  --color-border: #e5e7eb;
  --color-primary: #3b82f6;
  --color-primary-text: #ffffff;
  --shadow-color: rgba(0, 0, 0, 0.1);
}

/* Dark Theme — class bazlı */
[data-theme="dark"] {
  --color-bg: #0f172a;
  --color-bg-secondary: #1e293b;
  --color-text: #f1f5f9;
  --color-text-secondary: #94a3b8;
  --color-border: #334155;
  --color-primary: #60a5fa;
  --color-primary-text: #0f172a;
  --shadow-color: rgba(0, 0, 0, 0.4);
}

/* System preference bazlı */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --color-bg: #0f172a;
    --color-bg-secondary: #1e293b;
    --color-text: #f1f5f9;
    --color-text-secondary: #94a3b8;
    --color-border: #334155;
    --color-primary: #60a5fa;
    --color-primary-text: #0f172a;
    --shadow-color: rgba(0, 0, 0, 0.4);
  }
}
// JavaScript ile tema değiştirme
function toggleTheme() {
  const root = document.documentElement;
  const current = root.getAttribute('data-theme');
  const next = current === 'dark' ? 'light' : 'dark';
  root.setAttribute('data-theme', next);
  localStorage.setItem('theme', next);
}

// Sayfa yüklendiğinde tercih edilen temayı uygula
function initTheme() {
  const saved = localStorage.getItem('theme');
  if (saved) {
    document.documentElement.setAttribute('data-theme', saved);
  }
}
İpucu: Tema değişkenlerini "primitive" ve "semantic" olarak iki katmanda organize edin. Primitive değişkenler (--blue-500) sabit kalır, semantic değişkenler (--color-primary) temaya göre değişir.

3. Component-Level Scoping

Custom Properties'in cascade özelliği, component bazında değişken override etmeyi mümkün kılar. Bu sayede aynı component farklı bağlamlarda farklı görünüme sahip olabilir.

/* Configurable Button Component */
.btn {
  --btn-bg: var(--color-primary);
  --btn-text: var(--color-primary-text);
  --btn-padding-x: var(--space-lg);
  --btn-padding-y: var(--space-sm);
  --btn-radius: var(--radius-md);
  --btn-font-size: var(--font-size-base);

  background: var(--btn-bg);
  color: var(--btn-text);
  padding: var(--btn-padding-y) var(--btn-padding-x);
  border-radius: var(--btn-radius);
  font-size: var(--btn-font-size);
  border: none;
  cursor: pointer;
  transition: opacity 0.2s;
}

.btn:hover {
  opacity: 0.9;
}

/* Varyantlar — sadece değişkenleri override et */
.btn--danger {
  --btn-bg: #ef4444;
  --btn-text: #ffffff;
}

.btn--outline {
  --btn-bg: transparent;
  --btn-text: var(--color-primary);
  border: 2px solid var(--color-primary);
}

.btn--sm {
  --btn-padding-x: var(--space-md);
  --btn-padding-y: var(--space-xs);
  --btn-font-size: var(--font-size-sm);
}

.btn--lg {
  --btn-padding-x: var(--space-2xl);
  --btn-padding-y: var(--space-md);
  --btn-font-size: var(--font-size-lg);
}

4. Dinamik Hesaplamalar ve Animasyonlar

Custom Properties, calc() fonksiyonu ve CSS animasyonlarıyla birlikte kullanıldığında inanılmaz esneklik sağlar. JavaScript'ten tek bir değişkeni değiştirerek birden fazla stil özelliğini aynı anda güncelleyebilirsiniz.

/* Dinamik spacing sistemi */
:root {
  --base-size: 1rem;
  --scale: 1.25;
}

.heading-1 { font-size: calc(var(--base-size) * var(--scale) * var(--scale) * var(--scale)); }
.heading-2 { font-size: calc(var(--base-size) * var(--scale) * var(--scale)); }
.heading-3 { font-size: calc(var(--base-size) * var(--scale)); }

/* @property ile animasyon */
@property --gradient-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.animated-border {
  --gradient-angle: 0deg;
  border: 3px solid transparent;
  background:
    linear-gradient(var(--color-bg), var(--color-bg)) padding-box,
    conic-gradient(from var(--gradient-angle), #3b82f6, #8b5cf6, #ec4899, #3b82f6) border-box;
  animation: rotate-border 3s linear infinite;
}

@keyframes rotate-border {
  to { --gradient-angle: 360deg; }
}

/* Mouse takibi (JavaScript ile) */
.spotlight-card {
  --mouse-x: 50%;
  --mouse-y: 50%;
  background: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    rgba(59, 130, 246, 0.15) 0%,
    transparent 60%
  );
}
Uyarı: @property kuralı henüz tüm tarayıcılarda desteklenmemektedir (Firefox desteği sınırlıdır). Animasyon gerektiren durumlarda tarayıcı uyumluluğunu kontrol edin.

Sonuç

CSS Custom Properties, modern CSS'in temel taşlarından biridir. Design token sistemi oluşturma, tema yönetimi, component bazlı stil yapılandırma ve dinamik animasyonlar gibi pek çok alanda kullanılır. Preprocessor değişkenlerinden farklı olarak runtime'da çalışması, JavaScript ile etkileşim kurabilmesi ve cascade kurallarına uyması onu benzersiz kılar. Projenizde tutarlı bir design token sistemi kurarak bakım maliyetini azaltabilir ve tema desteğini kolayca ekleyebilirsiniz.

Kaynaklar

İlgili Yazılar