CSS animasyonu iki parçadan oluşur: Hareketin nasıl görüneceğini anlatan bir @keyframes kuralı ve bu kuralı bir öğeye bağlayıp süresini, tekrarını ve yönünü ayarlayan animation özelliği. @keyframes içinde yüzde değerleriyle ara durumlar tanımlarsınız; animation ise bu durumların ne kadar sürede, kaç kez ve hangi yönde oynatılacağını söyler. Geçişlerden farkı, bir tetikleyiciye ihtiyaç duymaması ve ikiden fazla ara durum içerebilmesidir.
Bu yazıda @keyframes sözdizimini, anahtar karelerin eksik ya da tekrar eden tanımlarda nasıl birleştiğini, animation kısayolunun değerleri hangi sırayla ayrıştırdığını, animation-fill-mode değerlerinin animasyon öncesi ve sonrası stile etkisini, birden çok animasyonu aynı öğeye bağlamayı ve prefers-reduced-motion ile erişilebilir hareket tasarlamayı MDN belgelerine göre ele alıyoruz. İki durum arasında tek seferlik yumuşak değişim için daha basit bir araç olan geçişler CSS transition yazısında anlatılıyor.
İlk animasyon: @keyframes ve animation-name
@keyframes kuralı bir ad alır ve içinde anahtar kare seçicileri barındırır. Her seçici, animasyon süresinin yüzde kaçında hangi stilin geçerli olacağını söyler. from yüzde 0 ile, to yüzde 100 ile aynıdır. Ad büyük küçük harfe duyarlı bir tanımlayıcı ya da dizgedir; animation-name ile aynı biçimde yazılmalıdır.
p {
animation-duration: 3s;
animation-name: slide-in;
}
@keyframes slide-in {
from {
translate: 150vw 0;
scale: 200% 1;
}
to {
translate: 0 0;
scale: 100% 1;
}
}
/* sonuç: paragraf sağ kenarın dışından gelir, 3 saniyede yerine oturur */Bu örnek MDN rehberindeki kayan metin örneğinin uyarlamasıdır. Başlangıç karesinde paragraf görünüm alanının 1,5 katı kadar sağa itilir ve yatayda iki kat genişletilir; bitiş karesinde kendi yerine ve normal boyutuna döner. Animasyon bittiğinde öğe kendi kuralındaki değerlere döner; burada bitiş karesi zaten varsayılan durum olduğu için bir sıçrama görülmez.
Anahtar kareleri istediğiniz sırada yazabilirsiniz; tarayıcı onları zamandaki yerlerine göre sıralar. Birden fazla yüzdeyi virgülle aynı bloğa bağlamak da mümkündür. MDN'nin ikinci örneği bir adı yüzde 25 ve 75 noktalarında normal boyutta tutar, yüzde 50 noktasında iki katına çıkarıp rengini değiştirir:
@keyframes grow-shrink {
25%,
75% {
scale: 100%;
}
50% {
scale: 200%;
color: magenta;
}
}
p span {
display: inline-block;
animation-duration: 3s;
animation-name: grow-shrink;
}span öğesine display: inline-block verilmesinin nedeni MDN'de açıkça yazar: Dönüşüm özellikleri, yerine konmayan (non-replaced) satır içi içeriği etkilemez. Düz bir span üzerinde scale çalıştırırsanız hiçbir değişiklik görmezsiniz. Ayrıca MDN bu örnekte font-size yerine scale kullanıldığını, çünkü kutu modelini etkileyen özellikleri canlandırmanın performansı olumsuz etkilediğini belirtir.
Anahtar karelerin birleşme kuralları

@keyframes içinde neler olduğunu bilmek, "animasyonum neden yarım çalışıyor" sorusunun çoğu zaman cevabıdır. MDN beş kuralı ayrıca açıklar:
- Başlangıç ya da bitiş eksikse:
0%ya da100%karesi yazılmamışsa tarayıcı öğenin mevcut stillerini başlangıç ya da bitiş olarak kullanır. Bu sayede yalnızca orta noktayı tanımlayarak bir öğeyi kendi durumundan çıkarıp geri getirebilirsiniz. - Canlandırılamayan özellikler: Anahtar karedeki canlandırılamayan özellikler yok sayılır, diğerleri yine canlanır.
- Aynı adda iki @keyframes: Ayrıştırıcının en son gördüğü kural kullanılır.
@keyframeskuralları birbirleriyle basamaklanmaz; bir animasyon karelerini asla iki ayrı kuraldan toplamaz. - Aynı yüzde iki kez yazılırsa: O yüzdeye ait tüm kareler birlikte değerlendirilir ve kural içinde basamaklanma olur; aynı özellik için sonraki değer kazanır.
- !important: Anahtar kare içinde
!importantile işaretlenen bildirimler tamamen yok sayılır.
@keyframes ornek {
0% {
top: 0;
}
50% {
top: 30px;
left: 20px;
}
50% {
top: 10px;
}
100% {
top: 0;
}
}
/* yüzde 50 noktasında kullanılan değerler: top: 10px ve left: 20px */Her karede yer almayan özellikler, mümkünse yalnızca tanımlı oldukları kareler arasında ara değerlerle hesaplanır. MDN örneğinde top yalnızca yüzde 0, 30 ve 100 karelerinde, left ise yüzde 0, 68, 72 ve 100 karelerinde yazılır; her özellik kendi karelerini kullanır. Bu, iki özelliği farklı ritimlerle hareket ettirmenin pratik bir yoludur.
animation kısayolu ve değerlerin sırası
animation kısayolu dokuz alt özelliği tek satırda toplar. Yazılmayan her bileşen başlangıç değerine döner. Aşağıdaki tablo MDN'de listelenen başlangıç değerlerini gösterir:
| Alt özellik | Başlangıç değeri | Görevi |
|---|---|---|
| animation-name | none | Bağlanacak @keyframes adı |
| animation-duration | 0s | Bir döngünün süresi |
| animation-timing-function | ease | Hız eğrisi |
| animation-delay | 0s | Başlamadan önceki bekleme |
| animation-iteration-count | 1 | Kaç kez oynatılacağı |
| animation-direction | normal | Oynatma yönü |
| animation-fill-mode | none | Öncesi ve sonrası stil |
| animation-play-state | running | Oynuyor ya da duraklatılmış |
| animation-timeline | auto | İlerlemeyi yöneten zaman çizelgesi |
Zaman değerleri için geçişlerle aynı kural geçerlidir: Zaman olarak ayrıştırılabilen ilk değer süre, ikincisi gecikmedir. Animasyon adı için ise ek bir kural vardır. Kısayoldaki bir değer ad dışındaki bir alt özelliğe ait olarak okunabiliyorsa önce oraya atanır. Örneğin @keyframes kuralınıza ease ya da infinite gibi bir ad verirseniz kısayol bunu zamanlama fonksiyonu ya da tekrar sayısı olarak okur. MDN bu yüzden adı her zaman en sona yazmayı önerir; birden fazla animasyonu virgülle ayırdığınızda da kural aynıdır.
p {
animation-duration: 3s;
animation-name: slide-in;
animation-iteration-count: infinite;
animation-direction: alternate;
}
/* aynı tanım kısayolla, ad en sonda */
p {
animation: 3s infinite alternate slide-in;
}
/* tüm bileşenler: süre | eğri | gecikme | tekrar | yön | dolgu | durum | ad */
.tam {
animation: 3s ease-in 1s 2 reverse both paused slide-in;
}Kısayolun bir yan etkisi daha vardır. MDN'ye göre animation, animation-range-start, animation-range-end, animation-composition ve animation-trigger gibi bazı özellikleri kendi içinde ayarlayamaz ama başlangıç değerlerine sıfırlar. Bu özellikleri kısayolun içine yazmak bildirimin tamamını geçersiz kılar. Bu yüzden onları kısayoldan sonra ya da daha yüksek özgüllükle yazmanız gerekir. Aynı durum kaydırmaya bağlı animasyonlarda animation-timeline için de önemlidir; ayrıntılar scroll-driven animations yazısında.
Süre, gecikme ve tekrar
animation-duration bir döngünün süresidir ve birimi zorunludur (s ya da ms). Negatif değer bildirimi geçersiz kılar. Süre 0s olduğunda animasyon teknik olarak yine çalışır ve başlangıç ile bitiş olayları tetiklenir, ama görünür bir hareket olmaz. Bu durumda ne görüneceğini dolgu modu belirler. auto değeri zamana bağlı animasyonlarda 0s ile eşdeğerdir.
animation-delay animasyon öğeye uygulandıktan sonra ne kadar bekleneceğini söyler. Negatif gecikme, animasyonu hemen ama döngünün içinden başlatır: -1s yazarsanız animasyon ilk saniyesini geçmiş gibi başlar. Bu, aynı animasyonu kullanan birkaç öğeyi farklı evrelerden başlatmak için kullanışlıdır.
animation-iteration-count varsayılan olarak 1'dir. infinite sonsuz tekrar demektir. Tam sayı olmayan değerler de geçerlidir: 0.5 döngünün yarısını oynatır, 2.5 iki buçuk döngü oynatır. Negatif değerler geçersizdir.
.nokta {
animation: 1.2s ease-in-out infinite alternate nabiz;
}
.nokta:nth-child(2) {
animation-delay: -0.4s;
}
.nokta:nth-child(3) {
animation-delay: -0.8s;
}
/* sonuç: üç nokta aynı animasyonu farklı evrelerden, beklemeden oynatır */
@keyframes nabiz {
from {
opacity: 0.3;
}
to {
opacity: 1;
}
}animation-direction: yön ve döngü
normal her döngüyü baştan sona oynatır ve her yeni döngüde başa sıçrar. reverse her döngüyü sondan başa oynatır; MDN, bu durumda zamanlama fonksiyonlarının da ters çevrildiğini, örneğin ease-in eğrisinin ease-out gibi davrandığını belirtir. alternate her döngüde yön değiştirir ve ilk döngü ileri oynar; alternate-reverse da yön değiştirir ama ilk döngü geri oynar. Tek ya da çift döngü sayımı 1'den başlar.
MDN rehberindeki örnek, sonsuz tekrarlanan kayan metnin her döngüde başa sıçramasının garip göründüğünü, alternate eklenince metnin ekranda ileri geri gidip geldiğini gösterir. Sürekli tekrar eden yumuşak hareketlerde alternate çoğu zaman doğal sonucu verir.
animation-fill-mode: animasyondan önce ve sonra

Varsayılan none değerinde animasyon çalışmadığı sürece öğeye hiçbir stil uygulamaz. Gecikme süresince öğe kendi kurallarıyla görünür; animasyon bitince yine kendi kurallarına döner. Bir öğeyi görünmezden görünür hale getiren bir animasyon yazıp sonunda öğenin aniden kaybolduğunu görüyorsanız sebep genellikle budur.
| Değer | Gecikme süresince | Animasyon bittikten sonra |
|---|---|---|
| none | Öğenin kendi stili | Öğenin kendi stili |
| forwards | Öğenin kendi stili | Son karşılaşılan anahtar karenin değerleri korunur |
| backwards | İlk ilgili anahtar karenin değerleri uygulanır | Öğenin kendi stili |
| both | İlk ilgili anahtar kare | Son karşılaşılan anahtar kare |
"Son karşılaşılan kare" her zaman 100% değildir; yöne ve tekrar sayısına bağlıdır. MDN tablosuna göre normal yönde son kare 100%, reverse yönde 0% olur. alternate ile çift sayıda döngüde 0%, tek sayıda döngüde 100% karesinde kalınır; alternate-reverse için bu durum tersine döner. Benzer şekilde backwards için ilk ilgili kare, normal ve alternate yönlerde 0%, reverse ve alternate-reverse yönlerde 100% karesidir.
@keyframes buyu {
0% {
font-size: 0;
}
100% {
font-size: 40px;
}
}
.demo:hover .buyur {
animation: 3s buyu;
/* sonuç: 3 saniye büyür, sonra eski boyutuna döner */
}
.demo:hover .buyur-ve-kalir {
animation: 3s forwards buyu;
/* sonuç: büyür ve 40px olarak kalır */
}Duraklatma ve anahtar kare başına zamanlama
animation-play-state running ya da paused değerini alır. Duraklatılan bir animasyon yeniden başlatıldığında baştan değil, kaldığı yerden devam eder. Bu özellik kullanıcıya hareketi durdurma imkânı sunmanın en basit yoludur:
.serit {
animation: 20s linear infinite kaydir;
}
.serit:hover,
.serit:focus-within {
animation-play-state: paused;
/* sonuç: fare üzerindeyken ya da içerideki öğe odaktayken durur */
}
@keyframes kaydir {
from {
translate: 0 0;
}
to {
translate: -100% 0;
}
}Zamanlama fonksiyonu tüm animasyon için değil, anahtar kareler arasındaki her aralık için uygulanır. Bir anahtar karenin içine animation-timing-function yazarsanız o eğri, ilgili özellik için bu kareden bir sonraki kareye kadar kullanılır. MDN, karenin içindeki bu yazımın aynı adlı özellik değil kurala özel bir tanımlayıcı olduğunu ve 100% ya da to karesine yazılan eğrinin hiçbir zaman kullanılmayacağını belirtir, çünkü ondan sonra gelen bir aralık yoktur.
@keyframes zipla {
0% {
translate: 0 0;
animation-timing-function: ease-out;
}
50% {
translate: 0 -40px;
animation-timing-function: ease-in;
}
100% {
translate: 0 0;
}
}
/* yukarı çıkış ease-out, aşağı iniş ease-in eğrisiyle */Birden çok animasyon ve aynı özellik çakışması
Bir öğeye virgülle ayırarak birden fazla animasyon bağlayabilirsiniz. Her animasyonun süresi, tekrarı ve yönü bağımsızdır. Alt özellikleri ayrı ayrı yazdığınızda değerler animation-name listesindeki sırayla eşleşir. Değer listesi kısaysa baştan tekrar edilir: Üç animasyon ve iki süre (2.5s, 5s) varsa üçüncü animasyon yeniden ilk süreyi, yani 2,5 saniyeyi alır. Fazla değerler ise yok sayılır.
.gunes {
animation:
4s linear 0s infinite alternate dogus,
24s linear 0s infinite renk-dongusu;
}
@keyframes dogus {
from {
transform: translateY(110vh);
}
to {
transform: translateY(0);
}
}
@keyframes renk-dongusu {
from {
filter: hue-rotate(0deg);
}
to {
filter: hue-rotate(360deg);
}
}Bu örnekte iki animasyon farklı özellikleri (transform ve filter) hareket ettirdiği için birbirini etkilemez. İki animasyon aynı özelliği hedeflediğinde ise listede sonra gelen kazanır. MDN örneğinde hem dikey hem yatay hareket transform ile yazıldığında yalnızca son animasyonun yatay hareketi görünür. Bunu önlemenin iki yolu vardır: Hareketleri ayrı translate, rotate ve scale özelliklerine bölmek ya da animation-composition kullanmak.
animation-composition kısayolun parçası değildir ve üç değer alır. Varsayılan replace değerinde animasyon değeri öğenin kendi değerinin yerine geçer. add iki değeri art arda ekler, accumulate ise birleştirir. MDN'nin örneğinde öğenin kendi değeri filter: blur(5px), anahtar karedeki değer blur(10px) olduğunda yüzde 0 noktasındaki sonuç replace ile blur(10px), add ile blur(5px) blur(10px), accumulate ile 5 + 10 = blur(15px) olur. Destek durumu için uyumluluk tablosuna bakın.
display: none ile animasyon
Animasyonlar da display özelliğini canlandırabilir ve geçişlerdeki özel kural burada da geçerlidir: none durumundan görünür bir değere giderken değer yüzde 0 noktasında, tersinde yüzde 100 noktasında değişir; böylece içerik animasyon boyunca görünür kalır. Geçişlerin aksine animasyonlarda @starting-style gerekmez, çünkü başlangıç durumu zaten 0% karesinde yazılıdır.
div.belir {
display: block;
animation: belir 0.7s ease-in forwards;
}
@keyframes belir {
0% {
opacity: 0;
display: none;
}
100% {
opacity: 1;
display: block;
}
}Erişilebilirlik ve performans
MDN, yanıp sönen ve parlayan animasyonların dikkat eksikliği yaşayan kullanıcılar için sorun yaratabileceğini, bazı hareket türlerinin vestibüler bozukluk, epilepsi ve migren için tetikleyici olabileceğini vurgular. Önerisi iki yönlüdür: Animasyonu durdurmak ya da kapatmak için bir yol sunmak ve prefers-reduced-motion medya sorgusuyla hareket azaltma tercihine saygı göstermek.
.kutu {
animation: nabiz-buyut 1s linear infinite both;
}
@media (prefers-reduced-motion: reduce) {
.kutu {
animation: sol 4s linear infinite both;
/* sonuç: ölçekleme yerine daha sakin bir solma */
}
}Bu desen MDN'deki örneğin uyarlamasıdır: Medya sorgusundaki kural aynı özgüllükte olduğu ve kaynakta daha sonra geldiği için tercih açıkken kazanır. Performans açısından MDN, kutu modeli özelliklerini (genişlik, yükseklik, margin gibi) canlandırmanın yerleşimin yeniden hesaplanmasına ve yeniden boyamaya yol açtığını, bunun yerine transform özelliğinin canlandırılmasını önerir. Dönüşüm fonksiyonları transform yazısında anlatılıyor; sayfa hızı ölçümleri ise Core Web Vitals rehberinin konusu.
Sık yapılan hatalar

- Süre yazmamak: Kısayolda süre yoksa
0skullanılır ve hareket görünmez. - Adı başa ya da ortaya yazmak: Ad bir anahtar sözcükle karışabilir; adı kısayolun sonuna yazın.
- Ad uyuşmazlığı:
@keyframesadı büyük küçük harfe duyarlıdır;Slideveslidefarklı adlardır. - Animasyon bitince sıçrama: Son karede kalmak için
animation-fill-mode: forwardsgerekir. - Satır içi öğede transform: Düz satır içi öğeler dönüşümden etkilenmez;
inline-blockverin. - Kısayoldan önce yazılan alt özellikler:
animationkısayolu sonradan yazılırsa sıfırladığı özellikleri (örneğinanimation-timeline) geri alır. - Anahtar karede !important: Yok sayılır; özgüllük sorununu başka yoldan çözün.
Sık Sorulan Sorular
Animasyonu JavaScript olmadan başlatabilir miyim?
Evet. animation özelliği bir öğeye uygulandığı anda animasyon başlar; sayfa yüklendiğinde de, bir sözde sınıfla (örneğin :hover) ya da bir class eklendiğinde de çalışır. JavaScript yalnızca class eklemek ya da animationstart, animationiteration, animationend olaylarını dinlemek için gerekir.
animation-iteration-count: infinite ile alternate birlikte nasıl çalışır?
Animasyon sonsuza kadar tekrar eder ve her döngüde yön değiştirir. Böylece her tekrarda başa sıçramak yerine ileri geri yumuşakça gidip gelir.
Bir animasyon bittikten sonra son durumu nasıl korurum?
animation-fill-mode: forwards ya da both kullanın. Son karenin hangisi olduğunun yön ve tekrar sayısına bağlı olduğunu unutmayın.
CSS değişkenini anahtar karelerde canlandırabilir miyim?
Kayıtsız özel özellikler ayrık canlanır ve yüzde 50 noktasında atlar. Yumuşak bir hareket için değişkeni @property ile bir türle kaydetmeniz gerekir; bu konu @property yazısında işleniyor.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: CSS öğrenme rehberi · CSS transition · CSS transform · Scroll-driven animations · @property · filter ve backdrop-filter · @media sözdizimi