Media query

Media query, pemilihan breakpoint, dan strategi mobile-first.

Daftar isi

Media query adalah fitur CSS yang menguji keadaan perangkat, seperti lebar viewport, dan menerapkan gaya tertentu hanya bila ujiannya terpenuhi. Topik ini membahas sintaks aturan @media dengan kondisi lebar, meta tag viewport pada HTML, strategi mobile-first, pemilihan breakpoint berbasis konten, dan fitur media lainnya secara singkat. Setelah membacanya, satu berkas CSS dapat melayani layar sempit hingga layar lebar dengan menambahkan sedikit blok kondisi.

Sintaks aturan @media untuk kondisi lebar#

Aturan @media membungkus sekumpulan deklarasi CSS dan menerapkannya hanya bila kondisi yang dinyatakan terpenuhi. Kondisi yang paling sering dipakai adalah lebar viewport, yang ditulis dengan min-width atau max-width. Deklarasi min-width: 768px berarti aturan berlaku pada lebar 768 piksel ke atas, sedangkan max-width: 768px berlaku pada lebar 768 piksel ke bawah. Kedua bentuk ini menghasilkan urutan penerapan yang berlawanan, sehingga pilihannya menentukan gaya penulisan keseluruhan halaman.

css Contoh: media query dengan min-width
body {
  padding: 16px;
}

@media (min-width: 768px) {
  body {
    padding: 32px;
  }
}

Blok di luar @media berlaku pada semua ukuran, sedangkan blok di dalamnya hanya menambah perubahan pada layar 768 piksel ke atas. Kondisi dapat digabungkan dengan kata kunci and, misalnya @media (min-width: 480px) and (max-width: 767px) untuk menjangkau rentang tertentu saja. Tipe media seperti screen atau print boleh ditulis sebelum kondisinya, tetapi untuk layar biasa kondisi lebar saja sudah cukup.

Browser mengevaluasi ekspresi itu setiap kali viewport berubah ukuran, sehingga halaman merespons perubahan jendela secara langsung tanpa memuat ulang dokumen. Sifat inilah yang membuat desain responsif mengikuti keadaan browser yang sesungguhnya dan bukan hanya ukuran layar yang diprediksi di awal. Perubahan yang dinyatakan pada blok @media berlaku selama kondisinya terpenuhi dan hilang sendiri ketika kondisinya tidak lagi terpenuhi.

Meta tag viewport pada HTML#

Media query pada CSS hanya berperilaku benar bila HTML-nya juga menyatakan lebar viewport dengan benar. Browser perangkat bergerak secara default merender halaman pada lebar maya sekitar 980 piksel, lalu memperkecil seluruh halaman agar muat di layar. Akibatnya, kondisi min-width menguji lebar maya tersebut dan bukan lebar layar sungguhan, sehingga ponsel yang sempit menerima gaya yang ditujukan untuk desktop.

html Contoh: meta tag viewport
<meta name="viewport" content="width=device-width, initial-scale=1">

Tag <meta> di atas ditempatkan di dalam elemen <head> pada setiap halaman yang memakai desain responsif. Nilai width=device-width menyamakan lebar viewport dengan lebar layar perangkat, sedangkan initial-scale=1 menyetel tingkat zoom awal tanpa penskalaan tambahan. Dengan kedua nilai itu, kondisi lebar pada CSS menguji lebar layar yang sesungguhnya, sehingga ponsel menerima gaya layar sempit sebagaimana dimaksudkan.

Masalah ini tidak terjadi pada browser desktop karena browser desktop tidak memakai lebar maya. Karena itu, kegagalan media query yang gejalanya hanya muncul di ponsel sering kali bukan kesalahan CSS-nya, melainkan meta tag yang tertinggal. Memeriksa keberadaan meta tag viewport menjadi langkah pertama sebelum menelusuri blok @media yang tampaknya tidak berjalan.

Strategi mobile-first#

Strategi mobile-first menuliskan gaya dasar untuk layar kecil terlebih dahulu, tanpa media query, kemudian menambah gaya untuk layar lebih besar melalui kondisi min-width. Gaya dasar berlaku pada semua ukuran, sedangkan setiap blok @media hanya melapisi tambahan pada lebar yang lebih besar. Dengan urutan ini, browser perangkat kecil tidak memproses lapisan gaya desktop yang kemudian ditimpa lagi, dan urutan penerapan aturannya mengalir dari kecil ke besar.

css Contoh: kisi kartu mobile-first
.kisi {
  display: grid;
  gap: 16px;
}

@media (min-width: 480px) {
  .kisi {
    grid-template-columns: 1fr 1fr;
  }
}

@media (min-width: 768px) {
  .kisi {
    grid-template-columns: repeat(3, 1fr);
  }
}

Pada contoh tersebut, kartu tersusun satu kolom secara default, dua kolom mulai 480 piksel, dan tiga kolom mulai 768 piksel. Setiap blok hanya menambahkan apa yang berubah, sehingga deklarasi sebelumnya tetap berlaku tanpa perlu ditulis ulang. Bandingkan dengan penulisan max-width dari desktop ke bawah, yang memaksa banyak properti ditimpa dan urutan penerapannya lebih mudah keliru.

Pendekatan ini juga sejalan dengan kenyataan penggunaan, karena sebagian besar kunjungan web datang dari layar kecil. Gaya dasar yang dirancang untuk layar sempit memaksa keputusan prioritas konten sejak awal, yaitu apa yang wajib tampil dan apa yang boleh ditunda ke layar lebar. Hasilnya biasanya berupa halaman yang lebih ringkas dibandingkan halaman desktop yang kemudian disusutkan.

Memilih breakpoint berbasis konten#

Breakpoint sebaiknya dipilih dengan mengamati titik ketika konten mulai tidak nyaman dibaca, bukan dengan menyalin daftar ukuran perangkat. Tanda yang umum dijumpai adalah baris teks yang terlalu panjang, kartu yang terlalu sempit, dan navigasi yang berdesakan. Ketika tanda itu muncul pada lebar tertentu, breakpoint diletakkan di lebar tersebut agar tata letak berubah tepat sebelum kontennya menderita.

Nilai 480, 768, dan 1024 sering dijumpai pada proyek nyata karena kebetulan cocok dengan banyak konten, sehingga ketiganya menjadi titik awal yang lazim. Nilai itu tetap perlu diuji terhadap halaman yang bersangkutan, karena dua halaman dengan konten berbeda dapat membutuhkan titik perubahan yang berbeda pula. Kebiasaan yang sehat adalah memulai tanpa breakpoint, memperlebar jendela secara perlahan, lalu menambahkan breakpoint hanya pada lebar yang tampilannya mulai rusak.

Fitur media selain lebar#

Kondisi pada @media tidak terbatas pada lebar. Fitur orientation membedakan layar mendatar dan layar menegak, sedangkan hover dan pointer menggambarkan kemampuan interaksi perangkat, misalnya @media (hover: hover) untuk gaya yang hanya berlaku pada perangkat yang memiliki kursor. Fitur-fitur ini diuji dengan struktur yang sama, yaitu nama fitur diikuti nilainya di dalam tanda kurung, dan dapat digabungkan dengan kondisi lebar melalui and.

Dua fitur yang membaca preferensi pengguna akan sering dijumpai pada proyek nyata, yaitu prefers-color-scheme untuk tema gelap dan prefers-reduced-motion untuk gerak yang dikurangi. Keduanya berkaitan erat dengan kenyamanan dan aksesibilitas, sehingga masing-masing dibahas mendalam pada modul theming dan modul gerak di jalur ini. Untuk kebutuhan tata letak sehari-hari, kondisi lebar tetap menjadi fitur yang paling sering dipakai dan menjadi fondasi bagi topik-topik lain pada modul ini.

Struktur penulisan seluruh fitur tersebut sama, sehingga pemahaman tentang kondisi lebar langsung dapat dipindahkan ke fitur lainnya. Sebuah fitur juga dapat diuji tanpa nilai, misalnya @media (prefers-reduced-motion) yang bernilai benar bila preferensi itu aktif. Keseragaman bentuk penulisan inilah yang membuat @media tetap mudah dibaca meskipun daftar fiturnya panjang.

Pola nyata: navigasi dan kisi kartu#

Dua pola yang paling sering ditemui adalah navigasi yang menyesuaikan dan kisi kartu yang menambah kolom. Pada layar sempit, tautan navigasi ditumpuk secara vertikal agar mudah disentuh, sedangkan pada layar lebar tautannya disusun berjejer mendatar. Perubahan semacam ini adalah perubahan struktur, sehingga termasuk wilayah kerja media query dan tidak dapat dinyatakan dengan satuan fluid.

css Contoh: navigasi yang berubah susunan
.navigasi ul {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 768px) {
  .navigasi ul {
    flex-direction: row;
    gap: 24px;
  }
}

Kisi kartu mengikuti pola yang sama seperti pada strategi mobile-first, yaitu satu kolom secara default, dua kolom mulai 480 piksel, dan tiga kolom mulai 768 piksel. Demo berikut memperlihatkan pola kisi kartu dalam bentuk paling sederhana, yaitu satu halaman kecil berisi kepala halaman dan dua panel. Persempit dan perlebar jendela pratinjau, lalu amati panel yang tadinya bertumpuk menjadi berdampingan pada lebar 480 piksel ke atas.

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Panel jadwal piket</title>
  <link rel="stylesheet" href="index.css">
</head>
<body>
  <header class="kepala">
    <h1>Papan piket</h1>
    <p>Jadwal kebersihan kelas minggu ini</p>
  </header>
  <main class="panel-ganda">
    <section class="panel">
      <h2>Jadwal piket</h2>
      <p>Senin: Ali, Budi, dan Citra. Selasa: Dewi, Eka, dan Fajar.</p>
    </section>
    <section class="panel">
      <h2>Catatan wali kelas</h2>
      <p>Alat pel dikembalikan setelah dipakai dan kantong sampah diganti setiap hari.</p>
    </section>
  </main>

<style>
/* Demo media query: panel satu kolom menjadi dua kolom pada 480 piksel */
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 16px;
  font-family: sans-serif;
  background: #f1f5f9;
  color: #1e293b;
}

.kepala {
  background: #1d4ed8;
  color: #ffffff;
  border-radius: 8px;
  padding: 14px 16px;
}

.kepala h1 {
  margin: 0 0 4px;
  font-size: 18px;
}

.kepala p {
  margin: 0;
  font-size: 13px;
}

.panel-ganda {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  margin-top: 12px;
}

.panel {
  background: #ffffff;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  padding: 12px 14px;
}

.panel h2 {
  margin: 0 0 6px;
  font-size: 15px;
}

.panel p {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
}

@media (min-width: 480px) {
  .panel-ganda {
    grid-template-columns: 1fr 1fr;
  }
}

</style>
<style>
/* Demo media query: panel satu kolom menjadi dua kolom pada 480 piksel */
* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: 16px;
  font-family: sans-serif;
  background: #f1f5f9;
  color: #1e293b;
}

.kepala {
  background: #1d4ed8;
  color: #ffffff;
  border-radius: 8px;
  padding: 14px 16px;
}

.kepala h1 {
  margin: 0 0 4px;
  font-size: 18px;
}

.kepala p {
  margin: 0;
  font-size: 13px;
}

.panel-ganda {
  display: grid;
  grid-template-columns: 1fr;
  gap: 12px;
  margin-top: 12px;
}

.panel {
  background: #ffffff;
  border: 1px solid #cbd5e1;
  border-radius: 8px;
  padding: 12px 14px;
}

.panel h2 {
  margin: 0 0 6px;
  font-size: 15px;
}

.panel p {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
}

@media (min-width: 480px) {
  .panel-ganda {
    grid-template-columns: 1fr 1fr;
  }
}

</style></body>
</html>

Perhatikan bahwa perubahan hanya dinyatakan satu kali pada blok @media dan sisanya ditangani gaya dasar. Apabila jendela pratinjau dipersempit melewati 480 piksel, panel kembali bertumpuk tanpa ada gaya tambahan yang perlu dihapus. Perilaku bolak-balik itulah yang membuat media query cocok untuk tata letak yang berubah secara diskrit pada lebar tertentu.

Ringkasan#

  • Aturan @media dengan min-width menerapkan gaya mulai lebar tertentu ke atas, sedangkan max-width menerapkannya ke bawah.
  • Meta tag viewport dengan width=device-width membuat kondisi lebar menguji lebar layar sungguhan pada perangkat bergerak.
  • Strategi mobile-first menuliskan gaya dasar untuk layar kecil, lalu melapisi perubahan melalui blok min-width yang menaik.
  • Breakpoint dipilih dari titik ketika konten mulai tidak nyaman, dengan nilai 480, 768, dan 1024 sebagai titik awal yang lazim.

Referensi#