---
title: "Theme Switch"
description: "Açık ve koyu tema arasında geçiş. Güneş ve ay tek bir dönüşle yer değiştirir, sayfa da bir View Transition ile yeni temaya geçer."
---

<Callout type="info" title="Ne zaman kullanılır">
Uygulama kendi açık/koyu görünüm seçimini sunuyorsa, araç çubuğunda veya ayarlar sayfasında. Elle yazılmış bir güneş/ay butonu ya da kendi sayfa geçişiniz yerine bunu kullanın.
</Callout>

## Kurulum

```bash
npx shadcn@latest add @ikas/theme-switch
```

Registry tanımı yoksa önce [Kurulum](https://builders.ikas.com/tr/docs/app-development/ui-kit/installation) adımlarını tamamlayın.

<Callout type="warning" title="@ikas/theme'i yeniden kurun">
Sayfa geçişlerinin keyframe'leri (`theme-*`) `@ikas/theme` ile gelir. Temayı bu sürümden önce kurduysanız `npx shadcn@latest add @ikas/theme` ile yeniden kurun; aksi halde geçiş animasyonu oynamaz.
</Callout>

## Örnekler

### Sayfa geçişleri

`transition` yeni temanın sayfaya nasıl gireceğini belirler: `reveal` düğmeden büyüyen bir daire, `eclipse` sağ kenardan kayan bir disk, `split` dikey bir çizgiden iki yana açılma, `rise` alttan kavisle yükselme. Eğriler temanın `--ease-*` token'larından gelir.

**Örnek: Sayfa geçişleri** (`theme-switch/transitions`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/theme-switch/transitions)

```tsx
import * as React from "react"

import { ThemeSwitch, type Theme, type ThemeTransition } from "@/components/ikas/theme-switch"

/** Follows the `dark` class on <html>, so every switch on the page stays in sync. */
function usePageTheme() {
  const [theme, setTheme] = React.useState<Theme>("light")
  React.useEffect(() => {
    const root = document.documentElement
    const sync = () => setTheme(root.classList.contains("dark") ? "dark" : "light")
    sync()
    const observer = new MutationObserver(sync)
    observer.observe(root, { attributes: true, attributeFilter: ["class"] })
    return () => observer.disconnect()
  }, [])
  function apply(next: Theme) {
    document.documentElement.classList.toggle("dark", next === "dark")
    setTheme(next)
  }
  return [theme, apply] as const
}

const transitions: { value: ThemeTransition; label: string; hint: string }[] = [
  { value: "reveal", label: "Daire", hint: "Düğmeden büyür" },
  { value: "eclipse", label: "Tutulma", hint: "Yandan kayan bir disk" },
  { value: "split", label: "Ortadan", hint: "Dikey çizgiden iki yana" },
  { value: "rise", label: "Yükselme", hint: "Alttan kavisle yükselir" },
]

export default function ThemeSwitchTransitions() {
  const [theme, setTheme] = usePageTheme()

  return (
    <div className="grid w-full max-w-lg grid-cols-1 gap-3 sm:grid-cols-2">
      {transitions.map((t) => (
        <div key={t.value} className="flex items-center justify-between gap-3 rounded-xl bg-card p-3 shadow-card">
          <div className="min-w-0">
            <p className="text-sm font-medium">{t.label}</p>
            <p className="truncate text-xs text-muted-foreground">{t.hint}</p>
          </div>
          <ThemeSwitch theme={theme} onThemeChange={setTheme} transition={t.value} iconOnly />
        </div>
      ))}
      <div className="flex justify-center sm:col-span-2">
        <ThemeSwitch theme={theme} onThemeChange={setTheme} />
      </div>
    </div>
  )
}
```

### Araç çubuğunda

`iconOnly` ile kare ikon butonu olur. Diğer ikon butonlarıyla aynı satırda `variant="ghost"` verin.

**Örnek: Araç çubuğunda** (`theme-switch/toolbar`) · [Canlı önizleme](https://builders.ikas.com/ui-preview/demo/theme-switch/toolbar)

```tsx
import * as React from "react"
import { BellIcon } from "lucide-react"

import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { ThemeSwitch, type Theme } from "@/components/ikas/theme-switch"

/** Follows the `dark` class on <html>, so every switch on the page stays in sync. */
function usePageTheme() {
  const [theme, setTheme] = React.useState<Theme>("light")
  React.useEffect(() => {
    const root = document.documentElement
    const sync = () => setTheme(root.classList.contains("dark") ? "dark" : "light")
    sync()
    const observer = new MutationObserver(sync)
    observer.observe(root, { attributes: true, attributeFilter: ["class"] })
    return () => observer.disconnect()
  }, [])
  function apply(next: Theme) {
    document.documentElement.classList.toggle("dark", next === "dark")
    setTheme(next)
  }
  return [theme, apply] as const
}

export default function ThemeSwitchToolbar() {
  const [theme, setTheme] = usePageTheme()

  return (
    <div className="w-full max-w-lg overflow-hidden rounded-xl bg-card shadow-card">
      <div className="flex items-center justify-between gap-3 border-b px-4 py-2.5">
        <p className="text-sm font-semibold">Kampanyalar</p>
        <div className="flex items-center gap-1">
          <ThemeSwitch theme={theme} onThemeChange={setTheme} transition="rise" iconOnly variant="ghost" />
          <Button variant="ghost" size="icon-sm" aria-label="Bildirimler">
            <BellIcon data-anim="ring" />
          </Button>
          <Avatar size="sm" className="ml-1">
            <AvatarFallback>AY</AvatarFallback>
          </Avatar>
        </div>
      </div>
      <div className="grid gap-2 p-4">
        <div className="h-3 w-2/3 rounded-full bg-muted" />
        <div className="h-3 w-1/2 rounded-full bg-muted" />
        <div className="h-3 w-3/5 rounded-full bg-muted" />
      </div>
    </div>
  )
}
```

## Temayı uygulama

ThemeSwitch temayı kendisi değiştirmez; yeni değeri `onThemeChange` ile verir. Temayı orada uygulayın: `<html>` üzerinde `dark` sınıfı veya next-themes kullanıyorsanız `setTheme`. `onThemeChange` sayfa geçişinin içinde çalışır, bu yüzden değişiklik eşzamanlı olmalıdır.

```tsx
<ThemeSwitch
  theme={theme}
  onThemeChange={(next) => {
    document.documentElement.classList.toggle("dark", next === "dark")
    setTheme(next)
  }}
/>
```

## Kullanım ilkeleri

**Yapın:**

- Uygulamada tek bir geçiş türü seçin ve her yerde onu kullanın.
- Araç çubuğunda iconOnly ve ghost varyantını kullanın.
- Seçimi kaydedin; kullanıcı bir sonraki açılışta aynı temayı görsün.

**Yapmayın:**

- Kendi güneş/ay ikonunuzu veya sayfa geçişinizi yazmayın.
- onThemeChange içinde temayı asenkron uygulamayın; geçiş eski ekranı yakalar.

## Erişilebilirlik

- Buton `aria-pressed` taşır; koyu temada basılı okunur.
- `iconOnly` iken buton duruma göre "Koyu temaya geç" ya da "Açık temaya geç" etiketiyle okunur.
- `prefers-reduced-motion` açıkken veya `startViewTransition` desteklemeyen tarayıcılarda tema animasyonsuz, anında değişir.

## API

### `ThemeSwitch`

`Button`'ın `variant`, `size` ve diğer prop'larını da alır.

| Prop | Tip | Varsayılan | Açıklama |
| --- | --- | --- | --- |
| `theme` (zorunlu) | `Theme` | - | Mevcut görünüm: light veya dark. İkonu ve aria-pressed değerini belirler. |
| `onThemeChange` (zorunlu) | `(theme: Theme) => void` | - | Bir sonraki görünümle çağrılır; temayı burada uygulayın. Sayfa geçişinin içinde çalışır. |
| `transition` | `ThemeTransition` | `"reveal"` | Yeni temanın sayfaya girişi: reveal, eclipse, split, rise veya animasyonsuz none. |
| `iconOnly` | `boolean` | `false` | Metinsiz kare ikon butonu. |
| `label` | `React.ReactNode` | `"Temayı değiştir"` | İkonun yanındaki görünür metin. |
