justify-content dan align-items

Pola perataan pada kedua sumbu dan pola pemusatan yang andal.

Daftar isi

Perataan adalah salah satu alasan flexbox diadopsi secara luas, karena dua properti kontainer, yaitu justify-content dan align-items, mengatur kedudukan item pada kedua sumbu tanpa pengukuran manual. Topik ini membahas nilai-nilai kedua properti tersebut, pengecualian per item melalui align-self, pola pemusatan dua sumbu, serta penerapannya pada navbar dan baris kartu. Demo interaktif di tengah topik memperlihatkan efek setiap nilai secara langsung.

Perataan pada sumbu main dengan justify-content#

Properti justify-content mendistribusikan ruang kosong (free space) pada sumbu main setelah ukuran seluruh item dihitung. Properti ini dideklarasikan pada kontainer dan memengaruhi kedudukan seluruh itemnya sekaligus. Apabila item sudah mengisi kontainer sepenuhnya, tidak ada ruang yang tersisa dan seluruh nilainya menghasilkan susunan yang sama. Efek perataan baru terlihat ketika total ukuran item lebih kecil daripada kontainernya.

NilaiEfek pada sumbu main
flex-startItem menempel pada awal sumbu main
flex-endItem menempel pada ujung sumbu main
centerItem berkelompok di tengah sumbu main
space-betweenRuang dibagi rata antar item tanpa ruang di ujung
space-aroundSetiap item diberi ruang di kedua sisinya dengan ruang ujung setengahnya
space-evenlySemua celah berukuran sama, termasuk di kedua ujung

Perbedaan space-around dan space-evenly paling sering tertukar. Nilai space-around memberi setiap item ruang di kiri dan kanannya. Ruang di tepi luar menjadi setengah dari ruang antar item karena celah antar item merupakan tempat bertemunya setengah porsi milik dua item bertetangga. Nilai space-evenly membagi ruang menjadi celah yang benar-benar sama besar, termasuk celah di kedua ujung. Pada kelompok tiga item, space-around menghasilkan empat celah dengan dua ukuran berbeda, sedangkan space-evenly menghasilkan empat celah berukuran sama.

Perataan pada sumbu cross dengan align-items#

Properti align-items bekerja pada sumbu cross dan nilai defaultnya adalah stretch. Dengan nilai tersebut, item tanpa ukuran tetap pada arah cross meregang mengisi kontainer, sehingga kartu dalam satu baris otomatis sama tinggi. Tinggi barisnya sendiri mengikuti item tertinggi, lalu item yang meregang menyesuaikan diri dengan tinggi baris tersebut. Nilai flex-start, flex-end, dan center menempatkan item pada awal, ujung, dan tengah sumbu cross tanpa meregangkannya. Nilai baseline meratakan garis dasar teks baris pertama setiap item dan berguna untuk kelompok label dengan ukuran huruf berbeda.

Pengecualian untuk satu item ditangani properti align-self yang dideklarasikan pada item itu sendiri. Nilainya sama dengan align-items, ditambah nilai default auto yang berarti mengikuti kontainer. Penulisan align-self pada satu item menimpa nilai kontainer tanpa mengubah item lainnya. Pola yang umum adalah kontainer menggunakan stretch agar seluruh kartu sama tinggi, sementara satu item tertentu diberi align-self: center agar tetap pendek dan berada di tengah. Kombinasi ini menghilangkan kebutuhan pembungkus tambahan hanya demi satu pengecualian.

css Contoh: satu item mengecualikan diri dari stretch
.baris {
  display: flex;
  align-items: stretch;
}

.baris .lencana {
  align-self: center;
}

Demo interaktif#

Efek setiap nilai paling cepat dipahami dengan mencobanya secara langsung. Demo berikut menyediakan satu kontainer flex berisi tiga item beserta dua kelompok tombol pemilih. Tombol pada kelompok pertama mengubah justify-content kontainer, sedangkan tombol pada kelompok kedua mengubah align-items. Klik tombolnya satu per satu, amati perpindahan ketiga item pada setiap kombinasi, lalu bandingkan pasangan space-around dan space-evenly yang perbedaannya berada di tepi kontainer.

Kode sumber demo
<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Demo justify-content dan align-items</title>
  <link rel="stylesheet" href="index.css">
</head>
<body>
  <p class="label">justify-content</p>
  <div class="tombol" data-target="justify">
    <button type="button" data-nilai="flex-start">flex-start</button>
    <button type="button" data-nilai="center">center</button>
    <button type="button" data-nilai="flex-end">flex-end</button>
    <button type="button" data-nilai="space-between">space-between</button>
    <button type="button" data-nilai="space-around">space-around</button>
    <button type="button" data-nilai="space-evenly">space-evenly</button>
  </div>

  <p class="label">align-items</p>
  <div class="tombol" data-target="align">
    <button type="button" data-nilai="stretch">stretch</button>
    <button type="button" data-nilai="flex-start">flex-start</button>
    <button type="button" data-nilai="center">center</button>
    <button type="button" data-nilai="flex-end">flex-end</button>
  </div>

  <div class="panggung">
    <div class="kontainer" id="kontainer">
      <div class="item">Satu</div>
      <div class="item">Dua</div>
      <div class="item">Tiga</div>
    </div>
  </div>

  <script src="index.js"></script>

<style>
/* Dasar demo */
body {
  font-family: system-ui, sans-serif;
  margin: 12px;
  color: #1f2933;
}

.label {
  margin: 8px 0 4px;
  font-weight: 600;
  font-size: 14px;
}

.tombol button {
  margin: 2px 4px 2px 0;
  padding: 4px 10px;
  font-size: 13px;
  border: 1px solid #94a3b8;
  background-color: #f8fafc;
  border-radius: 6px;
  cursor: pointer;
}

/* Tombol yang nilainya sedang aktif */
.tombol button.aktif {
  background-color: #0b5394;
  border-color: #0b5394;
  color: #ffffff;
}

/* Panggung berisi kontainer flex bertinggi tetap agar perataan cross terlihat */
.panggung {
  margin-top: 12px;
}

.kontainer {
  display: flex;
  align-items: stretch;
  height: 200px;
  border: 2px solid #0b5394;
  background-color: #f1f5f9;
  padding: 8px;
}

.item {
  padding: 10px 18px;
  background-color: #dbeafe;
  border: 1px solid #0b5394;
  border-radius: 6px;
}

</style>
<style>
/* Dasar demo */
body {
  font-family: system-ui, sans-serif;
  margin: 12px;
  color: #1f2933;
}

.label {
  margin: 8px 0 4px;
  font-weight: 600;
  font-size: 14px;
}

.tombol button {
  margin: 2px 4px 2px 0;
  padding: 4px 10px;
  font-size: 13px;
  border: 1px solid #94a3b8;
  background-color: #f8fafc;
  border-radius: 6px;
  cursor: pointer;
}

/* Tombol yang nilainya sedang aktif */
.tombol button.aktif {
  background-color: #0b5394;
  border-color: #0b5394;
  color: #ffffff;
}

/* Panggung berisi kontainer flex bertinggi tetap agar perataan cross terlihat */
.panggung {
  margin-top: 12px;
}

.kontainer {
  display: flex;
  align-items: stretch;
  height: 200px;
  border: 2px solid #0b5394;
  background-color: #f1f5f9;
  padding: 8px;
}

.item {
  padding: 10px 18px;
  background-color: #dbeafe;
  border: 1px solid #0b5394;
  border-radius: 6px;
}

</style>
<script>
const kontainer = document.getElementById('kontainer');

// Setiap kelompok tombol mengubah satu properti perataan pada kontainer
document.querySelectorAll('.tombol').forEach((kelompok) => {
  const properti = kelompok.dataset.target === 'justify' ? 'justifyContent' : 'alignItems';

  kelompok.querySelectorAll('button').forEach((tombol) => {
    tombol.addEventListener('click', () => {
      kontainer.style[properti] = tombol.dataset.nilai;
      kelompok.querySelectorAll('button').forEach((b) => b.classList.remove('aktif'));
      tombol.classList.add('aktif');
    });
  });
});

</script>
<script>
const kontainer = document.getElementById('kontainer');

// Setiap kelompok tombol mengubah satu properti perataan pada kontainer
document.querySelectorAll('.tombol').forEach((kelompok) => {
  const properti = kelompok.dataset.target === 'justify' ? 'justifyContent' : 'alignItems';

  kelompok.querySelectorAll('button').forEach((tombol) => {
    tombol.addEventListener('click', () => {
      kontainer.style[properti] = tombol.dataset.nilai;
      kelompok.querySelectorAll('button').forEach((b) => b.classList.remove('aktif'));
      tombol.classList.add('aktif');
    });
  });
});

</script></body>
</html>

Skrip pada demo hanya mengubah properti style kontainer, sehingga seluruh perilaku yang terlihat sepenuhnya berasal dari CSS. Nilai yang sedang aktif ditandai dengan latar tombol yang berubah, sehingga kombinasi yang diterapkan selalu terbaca. Kombinasi yang dipilih bertahan sampai tombol lain ditekan, sehingga efek masing-masing nilai dapat diamati satu per satu. Perhatikan pula bahwa perataan cross membutuhkan tinggi kontainer yang dinyatakan, karena stretch dan center tidak memiliki ruang untuk bekerja bila tingginya mengikuti isi. Kontainer pada demo sengaja diberi tinggi tetap justru karena alasan itu.

Pola pemusatan dua sumbu#

Pemusatan elemen pada kedua sumbu sekaligus adalah pola yang paling sering dicari dari flexbox. Cara paling langsung adalah mengombinasikan justify-content: center pada sumbu main dengan align-items: center pada sumbu cross. Kontainer yang menerapkan keduanya menempatkan satu kartu sambutan atau kotak dialog tepat di tengah, mendatar maupun menurun, tanpa mengukur ukuran elemennya sama sekali.

css Contoh: pemusatan dua sumbu pada layar sambutan
.layar-sambutan {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 300px;
}

Cara kedua adalah margin auto pada item. Margin dengan nilai auto menyerap seluruh ruang kosong di sisinya, sehingga margin: auto pada satu item memusatkannya di kedua sumbu sekaligus. Cara ini lebih fleksibel ketika posisi bergeser karena kebutuhan desain, misalnya satu tombol yang didorong ke kanan dengan margin-left: auto sementara item lain tetap di kiri. Ruang kosong yang sudah diserap margin tidak tersisa untuk justify-content, sehingga kedua mekanisme itu saling menggantikan dan bukan bertumpuk.

Pola nyata: navbar dua ujung dan kartu dengan tombol di dasar#

Dua pola yang paling sering digunakan merangkum materi topik ini. Kedua pola sering muncul bersamaan pada satu halaman, misalnya bilah navigasi di atas dan baris kartu artikel di bawahnya. Pola pertama adalah navbar dengan logo di kiri dan menu di kanan, yang dibangun dengan justify-content: space-between agar kedua kelompok menempel di ujung yang berlawanan dan align-items: center agar keduanya sejajar secara visual. Pola kedua adalah baris kartu yang tombolnya selalu menempel di dasar meskipun panjang teksnya berbeda, yaitu persoalan klasik pada kartu yang tingginya disamakan oleh stretch.

css Contoh: kartu dengan tombol yang menempel di dasar
.kartu {
  display: flex;
  flex-direction: column;
}

.kartu .tombol {
  margin-top: auto;
}

Kartu disusun sebagai kontainer flex dengan arah column, sehingga judul, teks, dan tombol tersusun menurun. Deklarasi margin-top: auto pada tombol menyerap seluruh ruang kosong di atasnya, sehingga tombol selalu menempel di dasar kartu berapa pun panjang teksnya. Tanpa pola ini, tombol pada kartu berteks pendek naik ke atas dan baris kartu terlihat tidak seragam. Kedua pola tersebut hanya memerlukan pemahaman arah sumbu dan penyerapan ruang kosong yang sudah dibahas di atas, sehingga keduanya dapat langsung disalin ke komponen apa pun yang bentuknya serupa.

Kesalahan umum#

Kesalahan pertama adalah menukar peran kedua properti setelah flex-direction: column. Pada kontainer kolom, justify-content mengatur kedudukan menurun, sedangkan align-items mengatur kedudukan mendatar. Cara mengingatnya adalah justify untuk sumbu main dan align untuk sumbu cross, apa pun arah kontainernya. Pemeriksaan pertama ketika perataan tidak berjalan adalah arah kontainernya, bukan nilai properti perataannya.

Kesalahan kedua adalah heran karena item tidak meregang meskipun align-items: stretch aktif. Peregangan hanya terjadi pada item tanpa ukuran tetap pada arah cross, sehingga height yang terpasang pada item menghalanginya. Hapus ukuran tetap itu atau alihkan pengaturannya ke align-self pada item yang memang perlu. Periksa pula tinggi kontainernya, karena perataan cross tanpa ruang tersisa tidak menghasilkan apa pun.

Kesalahan ketiga adalah menggantungkan pemusatan pada nilai space-* untuk kelompok yang jumlahnya berubah. Jumlah item menentukan pembagian ruangnya, sehingga satu item tunggal pada space-between menempel di awal, sedangkan dua item menempel di kedua tepi dengan celah di tengah. Pemusatan yang andal menggunakan center, sedangkan nilai space-* disisipkan ketika pembagian ruang itulah yang memang diinginkan.

Ringkasan#

  • justify-content mendistribusikan ruang kosong pada sumbu main dengan enam nilai, mulai dari menempel ujung hingga pembagian merata.
  • align-items meratakan item pada sumbu cross dengan stretch sebagai default, sedangkan align-self memberi pengecualian per item.
  • Pemusatan dua sumbu dapat dilakukan dengan kombinasi justify-content: center dan align-items: center maupun dengan margin auto pada item.
  • Navbar dua ujung dan kartu dengan tombol di dasar adalah pola nyata yang menempatkan kedua properti tersebut.

Referensi#