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

Gunakan properti interpolate-size atau fungsi calc-size() untuk memungkinkan transisi dan animasi yang mulus dari panjang ke kata kunci ukuran intrinsik dan kembali.

Dipublikasikan: 17 September 2024

Pengantar

Fitur CSS yang sering diminta adalah kemampuan untuk menganimasikan ke height: auto. Sedikit variasi dari permintaan tersebut adalah untuk mentransisikan properti width, bukan height, atau untuk bertransisi ke salah satu ukuran intrinsik lain 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 pengukuran 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 pengubahan ukuran kata kunci 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 dalam komentar tentang masalah Kelompok 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 pengukuran intrinsik dengan calc-size()

Dukungan Browser

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

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 perhitungan 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 dengan 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 sering muncul di calc-size() adalah mengapa Kelompok Kerja CSS 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() menegakkan kebenaran 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?

Dalam sebagian besar kasus, deklarasikan interpolate-size: allow-keywords pada :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; /* 👈 */
}

Kode ini adalah {i>progressive enhancement <i}yang bagus, karena browser yang tidak mendukungnya akan kembali menggunakan tanpa transisi.

Jika memerlukan kontrol yang lebih mendetail 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 melakukan fallback 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 lain yang menggunakan interpolate-size: allow-keywords.

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 interpolasi kata kunci ukuran dan height di setiap elemen .item disetel ke auto. Jika tidak, kode tersebut akan sama persis dengan kode dari sebelum fork.

: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 menganimasikan widget pengungkapan atau akordeon eksklusif saat terbuka. 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: