Animasikan ke tinggi: otomatis; (dan kata kunci ukuran intrinsik lainnya) di CSS

Gunakan properti interpolate-size atau fungsi calc-size() untuk mengaktifkan transisi dan animasi yang lancar dari panjang ke kata kunci ukuran intrinsik dan sebaliknya.

Dipublikasikan: 17 September 2024

Pengantar

Fitur CSS yang sering diminta adalah kemampuan untuk menganimasikan ke height: auto. Variasi kecil dari permintaan tersebut adalah mentransisikan properti width, bukan height, atau mentransisikan ke salah satu ukuran intrinsik lainnya yang diwakili oleh kata kunci seperti min-content, max-content, dan fit-content.

Misalnya, dalam demo berikut, akan lebih baik jika label dianimasikan dengan lancar ke lebar alaminya saat mengarahkan kursor ke ikon.

CSS yang digunakan adalah sebagai berikut:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease; /* 👈 Transition the width */

    &:hover,
    &:focus-visible {
        width: max-content; /* 👈 Doesn't work with transitions */
    }
}

Meskipun transition dideklarasikan untuk mentransisikan properti width, dan width: auto dideklarasikan di :hover, tidak ada transisi yang lancar. Sebaliknya, perubahannya tiba-tiba.

Menganimasikan ke dan dari kata kunci ukuran intrinsik dengan interpolate-size

Dukungan Browser

  • Chrome: 129.
  • Edge: tidak didukung.
  • Firefox: tidak didukung.
  • Safari: tidak didukung.

Sumber

Properti interpolate-size CSS memberi Anda kontrol atas apakah animasi dan transisi kata kunci ukuran intrinsik CSS harus diizinkan atau tidak.

Nilai defaultnya adalah numeric-only yang tidak mengaktifkan interpolasi. Saat menetapkan properti ke allow-keywords, Anda memilih untuk menggunakan interpolasi dari panjang ke kata kunci ukuran intrinsik CSS jika browser dapat menganimasikan kata kunci tersebut.

Sesuai spesifikasi:

  • numeric-only: <intrinsic-size-keyword> tidak dapat diinterpolasi.
  • allow-keywords: Dua nilai dapat diinterpolasi jika salah satunya adalah <intrinsic-size-keyword> dan yang lainnya adalah <length-percentage>. […]

Karena properti interpolate-size adalah properti yang diwarisi, Anda dapat mendeklarasikannya di :root untuk mengaktifkan transisi ke dan dari kata kunci ukuran intrinsik untuk seluruh dokumen. Ini adalah pendekatan yang direkomendasikan.

/* Opt-in the whole page to interpolate sizes to/from keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

Dalam demo berikut, aturan ini ditambahkan ke kode. Akibatnya, animasi ke dan dari width: auto berfungsi dengan baik (di browser dengan dukungan):

Batasi jangkauan keikutsertaan dengan mempersempit pemilih

Jika Anda ingin membatasi keikutsertaan allow-keywords hanya ke sub-pohon dokumen, sesuaikan pemilih dari :root ke elemen yang ingin Anda targetkan saja. Misalnya, jika <header> halaman Anda tidak kompatibel dengan jenis transisi ini, Anda dapat membatasi keikutsertaan hanya ke elemen <main> dan turunannya sebagai berikut:

main { /* 👈 Scope the opt-in to only <main> and its descendants */
    interpolate-size: allow-keywords;
}

Mengapa tidak mengizinkan animasi ke dan dari kata kunci ukuran secara default?

Masukan umum tentang mekanisme keikutsertaan ini adalah bahwa browser hanya boleh mengizinkan transisi dan animasi dari kata kunci ukuran intrinsik ke panjang secara default.

Opsi untuk mengaktifkan perilaku ini telah diteliti selama pengembangan fitur. Grup kerja menemukan bahwa mengaktifkannya secara default tidak kompatibel dengan versi sebelumnya karena banyak style sheet mengasumsikan bahwa kata kunci ukuran intrinsik (seperti auto atau min-content) tidak dapat dianimasikan. Anda dapat menemukan detailnya di komentar ini tentang masalah Grup Kerja CSS yang relevan.

Oleh karena itu, properti tersebut merupakan keikutsertaan. Berkat sifat pewarisannya, memilih untuk menyertakan seluruh dokumen hanyalah deklarasi interpolate-size: allow-sizes di :root seperti yang dijelaskan sebelumnya.

Menganimasikan ke dan dari kata kunci ukuran intrinsik dengan calc-size()

Dukungan Browser

  • Chrome: 129.
  • Edge: 129.
  • Firefox: tidak didukung.
  • Safari: tidak didukung.

Sumber

Cara lain untuk mengaktifkan interpolasi ke dan dari kata kunci ukuran intrinsik adalah dengan menggunakan fungsi calc-size(). Hal ini memungkinkan matematika dilakukan pada ukuran intrinsik dengan cara yang aman dan terdefinisi dengan baik.

Fungsi ini menerima dua argumen, secara berurutan:

  • Dasar ukuran penghitungan, yang dapat berupa <intrinsic-size-keyword>, tetapi juga calc-size() bertingkat.
  • Penghitungan ukuran kalkulasi, yang memungkinkan Anda melakukan penghitungan menggunakan dasar ukuran kalkulasi. Untuk merujuk pada dasar calc-size, gunakan kata kunci size.

Berikut beberapa contohnya:

width: calc-size(auto, size);        // = the auto width, unaltered
width: calc-size(min-content, size); // = the min-content width, unaltered

Menambahkan calc-size() ke demo asli, kodenya akan terlihat seperti ini:

nav a {
    width: 80px;
    overflow-x: clip;
    transition: width 0.35s ease;

    &:hover,
    &:focus-visible {
        width: calc-size(max-content, size); /* 👈 */
    }
}

Secara visual, hasilnya sama persis seperti saat menggunakan interpolate-size. Jadi, dalam kasus khusus ini, Anda harus menggunakan interpolate-size.

Keunggulan calc-size() adalah kemampuannya untuk melakukan penghitungan, yang tidak dapat dilakukan dengan interpolate-size:

width: calc-size(auto, size - 10px); // = The auto width minus 10 pixels
width: calc-size(min-content, size + 1rem); // = The min-content width plus 1rem
width: calc-size(max-content, size * .5);   // = Half the max-content width

Misalnya, jika Anda ingin semua paragraf di halaman diukur ke kelipatan terdekat 50px, Anda dapat menggunakan kode berikut:

p {
    width: calc-size(fit-content, round(up, size, 50px));
    height: calc-size(auto, round(up, size, 50px));
}

calc-size() juga memungkinkan Anda melakukan interpolasi antara dua calc-size() jika kedua basis ukuran penghitungannya sama. Hal ini juga tidak dapat dilakukan dengan interpolate-size.

#element {
    width: min-content; /* 👈 */
    transition: width 0.35s ease;

    &:hover {
        width: calc-size(min-content, size + 10px); /* 👈 */
    }
}

Mengapa tidak mengizinkan <intrinsic-size-keyword> di calc()?

Pertanyaan yang biasanya muncul dengan calc-size() adalah mengapa CSS Working Group tidak menyesuaikan fungsi calc() untuk mendukung kata kunci ukuran intrinsik.

Salah satu alasannya adalah Anda tidak diizinkan untuk mencampur dan mencocokkan kata kunci ukuran intrinsik saat melakukan penghitungan. Misalnya, Anda mungkin tergoda untuk menulis calc(max-content - min-content) yang terlihat valid, tetapi sebenarnya tidak. calc-size() menerapkan ketepatan karena, tidak seperti calc(), hanya menerima satu <intrinsic-size-keyword> sebagai argumen pertamanya.

Alasan lainnya adalah kesadaran konteks. Beberapa algoritma tata letak memiliki perilaku khusus untuk kata kunci ukuran intrinsik tertentu. calc-size() ditentukan secara eksplisit untuk mewakili ukuran intrinsik, bukan <length>. Berkat ini, algoritma tersebut dapat memperlakukan calc-size(<intrinsic-size-keyword>, …) sebagai <intrinsic-size-keyword>, sehingga mempertahankan perilaku khususnya untuk kata kunci tersebut.

Pendekatan mana yang akan digunakan?

Pada umumnya, deklarasikan interpolate-size: allow-keywords di :root. Ini adalah cara termudah untuk mengaktifkan animasi ke dan dari kata kunci ukuran intrinsik karena pada dasarnya merupakan satu baris.

/* Opt-in the whole page to animating to/from intrinsic sizing keywords */
:root {
    interpolate-size: allow-keywords; /* 👈 */
}

Potongan kode ini adalah peningkatan progresif yang bagus, karena browser yang tidak mendukungnya akan kembali menggunakan tanpa transisi.

Jika Anda memerlukan kontrol yang lebih terperinci atas berbagai hal–seperti melakukan penghitungan–atau ingin menggunakan perilaku yang hanya dapat dilakukan calc-size(), Anda dapat menggunakan calc-size().

#specific-element {
    width: 50px;

    &:hover {
        width: calc-size(fit-content, size + 1em); /* 👈 Only calc-size() can do this */
    }
}

Namun, penggunaan calc-size() dalam kode akan mengharuskan Anda menyertakan penggantian untuk browser yang tidak mendukung calc-size(). Misalnya, menambahkan deklarasi ukuran tambahan, atau kembali ke deteksi fitur menggunakan @supports.

width: fit-content;
width: calc-size(fit-content, size + 1em);
       /* 👆 Browsers with no calc-size() support will ignore this second declaration,
             and therefore fall back to the one on the line before it. */

Demo lainnya

Berikut beberapa demo lainnya yang menggunakan interpolate-size: allow-keywords untuk keuntungannya.

Notifikasi

Demo berikut adalah fork dari demo @starting-style ini. Kode disesuaikan untuk memungkinkan item dengan tinggi yang bervariasi ditambahkan.

Untuk mencapai hal ini, seluruh halaman memilih untuk menyesuaikan ukuran interpolasi kata kunci dan height di setiap elemen .item ditetapkan ke auto. Jika tidak, kodenya sama persis dengan sebelum melakukan forking.

:root {
    interpolate-size: allow-keywords; /* 👈 */
}

.item {
    height: auto; /* 👈 */

    @starting-style {
        height: 0px;
    }
}

Menganimasikan elemen <details>

Kasus penggunaan umum saat Anda ingin menggunakan jenis interpolasi ini adalah untuk menganimasikan widget pengungkapan atau akordeon eksklusif saat dibuka. Di HTML, Anda menggunakan elemen <details> untuk ini.

Dengan interpolate-size: allow-keywords, Anda dapat melakukan banyak hal:

@supports (interpolate-size: allow-keywords) {
    :root {
        interpolate-size: allow-keywords;
    }
    
    details {
        transition: height 0.5s ease;
        height: 2.5rem;
        
        &[open] {
            height: auto;
            overflow: clip; /* Clip off contents while animating */
        }
    }
}

Namun, seperti yang dapat Anda lihat, animasi hanya berjalan saat widget pengungkapan terbuka. Untuk memenuhi hal ini, Chrome sedang mengerjakan pseudo ::details-content yang akan dikirimkan di Chrome akhir tahun ini (dan akan dibahas dalam postingan mendatang). Dengan menggabungkan interpolate-size: allow-keywords dan ::details-content, Anda bisa mendapatkan animasi di kedua arah: