Menambatkan elemen satu sama lain dengan penempatan anchor CSS

Bagaimana saat ini Anda melakukan tethering satu elemen ke elemen lainnya? Anda dapat mencoba melacak posisinya, atau menggunakan beberapa bentuk elemen wrapper.

<!-- index.html -->
<div class="container">
  <a href="/link" class="anchor">I’m the anchor</a>
  <div class="anchored">I’m the anchored thing</div>
</div>
/* styles.css */
.container {
  position: relative;
}
.anchored {
  position: absolute;
}

Solusi tersebut sering kali tidak ideal. Mereka membutuhkan JavaScript atau memperkenalkan markup tambahan. CSS anchor positioning API bertujuan untuk mengatasi hal ini dengan menyediakan CSS API untuk elemen tethering. Elemen ini menyediakan sarana untuk memosisikan dan mengukur satu elemen berdasarkan posisi dan ukuran elemen lain.

Gambar menampilkan jendela browser mockup yang menjelaskan anatomi tooltip.

Dukungan browser

Anda dapat mencoba CSS anchor positioning API di Chrome Canary di belakang tanda "Fitur Platform Web Eksperimental". Untuk mengaktifkan tanda tersebut, buka Chrome Canary dan kunjungi chrome://flags. Kemudian, aktifkan tanda "Experimental web platform features".

Ada juga polyfill dalam pengembangan oleh tim di Oddbird. Pastikan untuk memeriksa repo di github.com/oddbird/css-anchor-positioning.

Anda dapat memeriksa dukungan anchor dengan:

@supports(anchor-name: --foo) {
  /* Styles... */
}

Perhatikan bahwa API ini masih dalam tahap eksperimental dan dapat berubah. Artikel ini membahas bagian-bagian penting secara umum. Penerapan saat ini juga tidak sepenuhnya sinkron dengan spesifikasi CSS Working Group.

Permasalahan

Mengapa Anda perlu melakukan ini? Kasus penggunaan yang menonjol adalah membuat tooltip atau pengalaman seperti tooltip. Dalam hal ini, Anda sering kali ingin melakukan tethering tooltip ke konten yang dirujuknya. Sering kali diperlukan cara untuk melakukan tethering elemen ke elemen lainnya. Anda juga berharap bahwa interaksi dengan halaman tidak akan memutus tethering tersebut—misalnya, jika pengguna men-scroll atau mengubah ukuran UI.

Bagian lain dari masalah ini adalah jika Anda ingin memastikan elemen yang di-tethering tetap terlihat—misalnya, jika Anda membuka tooltip dan menjadi terpotong oleh batas area pandang. Ini mungkin bukan pengalaman yang baik bagi pengguna. Anda ingin tooltip beradaptasi.

Solusi saat ini

Saat ini, ada beberapa cara berbeda yang dapat Anda lakukan untuk mengatasi masalah ini.

Pertama adalah pendekatan "Wrap the anchor" yang terlalu dasar. Anda mengambil kedua elemen dan menggabungkannya ke dalam sebuah penampung. Selanjutnya, Anda dapat menggunakan position untuk memosisikan tooltip sesuai dengan anchor.

<div class="containing-block">
  <div class="tooltip">Anchor me!</div>
  <a class="anchor">The anchor</a>
</div>
.containing-block {
  position: relative;
}

.tooltip {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%);
}

Anda dapat memindahkan container dan semuanya akan tetap berada di tempat yang diinginkan.

Pendekatan lainnya mungkin jika Anda mengetahui posisi anchor atau Anda dapat melacaknya. Anda dapat meneruskannya ke tooltip dengan properti khusus.

<div class="tooltip">Anchor me!</div>
<a class="anchor">The anchor</a>
:root {
  --anchor-width: 120px;
  --anchor-top: 40vh;
  --anchor-left: 20vmin;
}

.anchor {
  position: absolute;
  top: var(--anchor-top);
  left: var(--anchor-left);
  width: var(--anchor-width);
}

.tooltip {
  position: absolute;
  top: calc(var(--anchor-top));
  left: calc((var(--anchor-width) * 0.5) + var(--anchor-left));
  transform: translate(-50%, calc(-100% - 10px));
}

Namun, bagaimana jika Anda tidak mengetahui posisi anchor? Anda mungkin perlu mengintervensi JavaScript. Anda dapat melakukan sesuatu seperti kode berikut, tetapi sekarang ini berarti gaya Anda mulai bocor dari CSS dan ke dalam JavaScript.

const setAnchorPosition = (anchored, anchor) => {
  const bounds = anchor.getBoundingClientRect().toJSON();
  for (const [key, value] of Object.entries(bounds)) {
    anchored.style.setProperty(`--${key}`, value);
  }
};

const update = () => {
  setAnchorPosition(
    document.querySelector('.tooltip'),
    document.querySelector('.anchor')
  );
};

window.addEventListener('resize', update);
document.addEventListener('DOMContentLoaded', update);

Hal ini mulai menimbulkan beberapa pertanyaan:

  • Kapan saya menghitung gaya?
  • Bagaimana cara menghitung gaya?
  • Seberapa sering saya menghitung gaya?

Apakah solusi itu menyelesaikan masalah? Mungkin cocok untuk kasus penggunaan Anda, tetapi ada satu masalah: solusi kami tidak beradaptasi. Tidak responsif. Bagaimana jika elemen anchor saya terpotong oleh area pandang?

Sekarang Anda perlu memutuskan apakah akan bereaksi terhadap hal ini dan bagaimana caranya. Jumlah pertanyaan dan keputusan yang perlu Anda buat mulai bertambah. Yang perlu Anda lakukan adalah menambatkan satu elemen ke elemen lainnya. Dalam kondisi ideal, solusi Anda akan menyesuaikan dan bereaksi terhadap lingkungannya.

Untuk meringankan beberapa kesulitan itu, Anda mungkin perlu menggunakan solusi JavaScript untuk membantu Anda. Hal itu akan menimbulkan biaya penambahan dependensi ke project Anda, dan hal ini dapat menimbulkan masalah performa, bergantung pada cara Anda menggunakannya. Misalnya, beberapa paket menggunakan requestAnimationFrame untuk menjaga posisinya tetap benar. Ini berarti Anda dan tim harus memahami paket dan opsi konfigurasinya. Akibatnya, pertanyaan dan keputusan Anda mungkin tidak dikurangi, tetapi diubah. Ini adalah bagian dari "alasan" untuk penempatan anchor CSS. Hal ini akan mengabstraksi Anda untuk tidak memikirkan masalah kinerja saat menghitung posisi.

Berikut ini adalah tampilan kode untuk menggunakan "floating-ui", paket yang populer untuk masalah ini:

import {computePosition, flip, offset, autoUpdate} from 'https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.2.1/+esm';

const anchor = document.querySelector('.anchor')
const tooltip = document.querySelector('.tooltip')

const updatePosition = () => {  
  computePosition(anchor, tooltip, {
    placement: 'top',
    middleware: [offset(10), flip()]
  })
    .then(({x, y}) => {
      Object.assign(tooltip.style, {
        left: `${x}px`,
        top: `${y}px`
      })
  })
};

const clean = autoUpdate(anchor, tooltip, updatePosition);

Coba posisikan ulang anchor dalam demo ini yang menggunakan kode tersebut.

"tooltip" mungkin tidak berfungsi seperti yang Anda harapkan. Bereaksi saat keluar dari area pandang pada sumbu y tetapi tidak pada sumbu x. Pelajari dokumentasi ini, dan Anda mungkin akan menemukan solusi yang sesuai untuk Anda.

Tetapi, menemukan paket yang sesuai untuk proyek Anda bisa memakan banyak waktu. Ini merupakan keputusan tambahan dan dapat membuat frustrasi jika tidak sesuai dengan yang Anda inginkan.

Menggunakan penempatan anchor

Masukkan CSS Positioning API. Idenya adalah untuk mempertahankan gaya Anda dalam CSS Anda dan mengurangi jumlah keputusan yang perlu Anda buat. Anda berharap untuk mencapai hasil yang sama, tetapi tujuannya adalah untuk membuat pengalaman developer lebih baik.

  • JavaScript tidak diperlukan.
  • Biarkan browser melakukan posisi terbaik berdasarkan panduan Anda.
  • Tidak ada lagi dependensi pihak ketiga
  • Tidak ada elemen wrapper.
  • Berfungsi dengan elemen yang ada di lapisan atas.

Mari kita buat ulang dan atasi masalah yang kita coba selesaikan di atas. Namun, gunakan analogi perahu dengan jangkar. Keduanya mewakili elemen anchor dan anchor. Air mewakili blok penampungnya.

Pertama, Anda harus memilih cara menentukan anchor. Anda dapat melakukannya dalam CSS dengan menetapkan properti anchor-name pada elemen anchor. Properti ini menerima nilai dashed-ident.

.anchor {
  anchor-name: --my-anchor;
}

Atau, Anda dapat menentukan anchor di HTML dengan atribut anchor. Nilai atribut adalah ID elemen anchor. Tindakan ini akan membuat anchor implisit.

<a id="my-anchor" class="anchor"></a>
<div anchor="my-anchor" class="boat">I’m a boat!</div>

Setelah menentukan anchor, Anda dapat menggunakan fungsi anchor. Fungsi anchor menggunakan 3 argumen:

  • Elemen anchor: anchor-name anchor yang akan digunakan—atau, Anda dapat menghilangkan nilai tersebut untuk menggunakan anchor implicit. Class ini dapat ditentukan melalui hubungan HTML, atau dengan properti anchor-default dengan nilai anchor-name.
  • Sisi anchor: Kata kunci dari posisi yang ingin Anda gunakan. Ini bisa berupa top, right, bottom, left, center, dll. Atau, Anda dapat meneruskan persentase. Misalnya, 50% akan sama dengan center.
  • Penggantian: Ini adalah nilai penggantian opsional yang menerima panjang atau persentase.

Anda menggunakan fungsi anchor sebagai nilai untuk properti inset (top, right, bottom, left, atau padanan logisnya) dari elemen anchor. Anda juga dapat menggunakan fungsi anchor di calc:

.boat {
  bottom: anchor(--my-anchor top);
  left: calc(anchor(--my-anchor center) - (var(--boat-size) * 0.5));
}

 /* alternative with anchor-default */
.boat {
  anchor-default: --my-anchor;
  bottom: anchor(top);
  left: calc(anchor(center) - (var(--boat-size) * 0.5));
}

Tidak ada properti inset center, jadi salah satu opsinya adalah menggunakan calc jika Anda mengetahui ukuran elemen anchor. Mengapa tidak menggunakan translate? Anda dapat menggunakan referensi ini:

.boat {
  anchor-default: --my-anchor;
  bottom: anchor(top);
  left: anchor(center);
  translate: -50% 0;
}

Namun, browser tidak mempertimbangkan posisi yang diubah untuk elemen anchor. Akan menjadi jelas mengapa hal ini penting saat mempertimbangkan penggantian posisi dan pemosisian otomatis.

Anda mungkin telah mengetahui penggunaan properti kustom --boat-size di atas. Namun, jika Anda ingin mendasarkan ukuran elemen anchor pada anchor, Anda juga dapat mengakses ukuran tersebut. Anda dapat menggunakan fungsi anchor-size, bukan menghitungnya sendiri. Misalnya, untuk membuat perahu kita empat kali lebar angkur:

.boat {
  width: calc(4 * anchor-size(--my-anchor width));
}

Anda juga dapat mengakses tinggi dengan anchor-size(--my-anchor height). Anda juga dapat menggunakannya untuk menyetel ukuran salah satu sumbu atau keduanya.

Bagaimana jika Anda ingin menambatkan ke elemen dengan penentuan posisi absolute? Aturannya adalah elemen tidak boleh seinduk. Dalam hal ini, Anda dapat menggabungkan anchor dengan penampung yang memiliki posisi relative. Selanjutnya, Anda dapat menambatkan ke dalamnya.

<div class="anchor-wrapper">
  <a id="my-anchor" class="anchor"></a>
</div>
<div class="boat">I’m a boat!</div>

Lihat demo ini di mana Anda dapat menarik angkur dan perahu akan mengikuti.

Melacak posisi scroll

Dalam beberapa kasus, elemen anchor mungkin berada dalam penampung scroll. Pada saat yang sama, elemen anchor Anda mungkin berada di luar penampung tersebut. Karena scrolling terjadi pada thread yang berbeda dari tata letak, Anda memerlukan cara untuk melacaknya. Properti anchor-scroll dapat melakukannya. Anda menetapkannya pada elemen anchor dan memberinya nilai anchor yang ingin dilacak.

.boat { anchor-scroll: --my-anchor; }

Coba demo ini untuk mengaktifkan dan menonaktifkan anchor-scroll dengan kotak centang di sudut.

Namun, analoginya agak datar di sini, seperti dalam dunia yang ideal, perahu dan jangkar Anda berada di dalam air. Selain itu, fitur seperti Popover API mempromosikan kemampuan untuk menutup elemen terkait. Namun, penentuan posisi anchor akan berfungsi dengan elemen yang berada di lapisan paling atas. Ini adalah salah satu manfaat utama di balik API: kemampuan untuk melakukan tethering elemen dalam flow yang berbeda.

Pertimbangkan demo ini yang memiliki penampung scroll dengan anchor yang memiliki tooltip. Elemen tooltip yang merupakan popover mungkin tidak ditempatkan bersama dengan anchor:

Namun, Anda akan melihat bagaimana pop-up melacak link anchor-nya masing-masing. Anda dapat mengubah ukuran penampung scroll tersebut dan posisinya akan diperbarui untuk Anda.

Penggantian posisi dan pemosisian otomatis

Di sinilah kekuatan penempatan anchor naik satu level. position-fallback dapat memosisikan elemen anchor berdasarkan serangkaian penggantian yang Anda berikan. Anda memandu browser dengan gaya Anda dan membiarkannya bekerja sesuai posisinya.

Kasus penggunaan umum di sini adalah tooltip yang seharusnya ditampilkan di atas atau di bawah anchor. Dan perilaku ini didasarkan pada apakah tooltip akan terpotong oleh penampungnya atau tidak. Penampung tersebut biasanya merupakan area pandang.

Jika Anda mempelajari kode demo terakhir, Anda akan melihat ada properti position-fallback yang digunakan. Jika men-scroll container, Anda mungkin melihat popover anchor tersebut melonjak. Hal ini terjadi saat anchor masing-masing mendekati batas area pandang. Pada saat itu, popover mencoba menyesuaikan agar tetap berada di area pandang.

Sebelum membuat position-fallback eksplisit, pemosisian anchor juga akan menawarkan pemosisian otomatis. Anda bisa mendapatkan balik tersebut secara gratis dengan menggunakan nilai auto di fungsi anchor dan properti inset yang berlawanan. Misalnya, jika Anda menggunakan anchor untuk bottom, setel top ke auto.

.tooltip {
  position: absolute;
  bottom: anchor(--my-anchor auto);
  top: auto;
}

Alternatif untuk pemosisian otomatis adalah menggunakan position-fallback eksplisit. Tindakan ini mengharuskan Anda menentukan kumpulan penggantian posisi. Browser akan menelusuri ini hingga menemukan yang dapat digunakan dan kemudian menerapkan pemosisian tersebut. Jika tidak dapat menemukan URL yang berfungsi, URL akan disetel ke default pertama yang ditentukan.

position-fallback yang mencoba menampilkan tooltip di atas lalu di bawah dapat terlihat seperti ini:

@position-fallback --top-to-bottom {
  @try {
    bottom: anchor(top);
    left: anchor(center);
  }

  @try {
    top: anchor(bottom);
    left: anchor(center);
  }
}

Menerapkannya ke tooltip akan terlihat seperti ini:

.tooltip {
  anchor-default: --my-anchor;
  position-fallback: --top-to-bottom;
}

Penggunaan anchor-default berarti Anda dapat menggunakan kembali position-fallback untuk elemen lain. Anda juga dapat menggunakan properti kustom cakupan untuk menetapkan anchor-default.

Pertimbangkan demo ini menggunakan perahu lagi. Ada position-fallback yang ditetapkan. Saat Anda mengubah posisi angkur, perahu akan menyesuaikan agar tetap berada di dalam kontainer. Coba ubah nilai padding juga yang akan menyesuaikan padding isi. Perhatikan cara browser mengoreksi pemosisian. Posisi diubah dengan mengubah perataan petak penampung.

Kali ini position-fallback lebih panjang jika mencoba posisinya searah jarum jam.

.boat {
  anchor-default: --my-anchor;
  position-fallback: --compass;
}

@position-fallback --compass {
  @try {
    bottom: anchor(top);
    right: anchor(left);
  }

  @try {
    bottom: anchor(top);
    left: anchor(right);
  }

  @try {
    top: anchor(bottom);
    right: anchor(left);
  }

  @try {
    top: anchor(bottom);
    left: anchor(right);
  }
}


Contoh

Setelah Anda mengetahui fitur utama untuk pemosisian anchor, mari kita lihat beberapa contoh menarik di luar tooltip. Contoh-contoh ini bertujuan untuk mengalirkan ide Anda terkait cara-cara yang dapat dilakukan untuk menggunakan pemosisian anchor. Cara terbaik untuk mengembangkan spesifikasi lebih lanjut adalah dengan masukan dari pengguna sungguhan seperti Anda.

Menu konteks

Mari kita mulai dengan menu konteks menggunakan Popover API. Idenya adalah mengklik tombol dengan tanda V akan membuka menu konteks. Menu tersebut akan memiliki menunya sendiri untuk diperluas.

Markup bukanlah bagian penting di sini. Namun, Anda memiliki tiga tombol yang masing-masing menggunakan popovertarget. Kemudian Anda memiliki tiga elemen yang menggunakan atribut popover. Dengan demikian, Anda dapat membuka menu konteks tanpa JavaScript. Token akan tampak seperti berikut:

<button popovertarget="context">
  Toggle Menu
</button>        
<div popover="auto" id="context">
  <ul>
    <li><button>Save to your Liked Songs</button></li>
    <li>
      <button popovertarget="playlist">
        Add to Playlist
      </button>
    </li>
    <li>
      <button popovertarget="share">
        Share
      </button>
    </li>
  </ul>
</div>
<div popover="auto" id="share">...</div>
<div popover="auto" id="playlist">...</div>

Sekarang, Anda dapat menentukan position-fallback dan membagikannya di antara menu konteks. Kita pastikan juga tidak menetapkan gaya inset untuk popover.

[popovertarget="share"] {
  anchor-name: --share;
}

[popovertarget="playlist"] {
  anchor-name: --playlist;
}

[popovertarget="context"] {
  anchor-name: --context;
}

#share {
  anchor-default: --share;
  position-fallback: --aligned;
}

#playlist {
  anchor-default: --playlist;
  position-fallback: --aligned;
}

#context {
  anchor-default: --context;
  position-fallback: --flip;
}

@position-fallback --aligned {
  @try {
    top: anchor(top);
    left: anchor(right);
  }

  @try {
    top: anchor(bottom);
    left: anchor(right);
  }

  @try {
    top: anchor(top);
    right: anchor(left);
  }

  @try {
    bottom: anchor(bottom);
    left: anchor(right);
  }

  @try {
    right: anchor(left);
    bottom: anchor(bottom);
  }
}

@position-fallback --flip {
  @try {
    bottom: anchor(top);
    left: anchor(left);
  }

  @try {
    right: anchor(right);
    bottom: anchor(top);
  }

  @try {
    top: anchor(bottom);
    left: anchor(left);
  }

  @try {
    top: anchor(bottom);
    right: anchor(right);
  }
}

Ini memberi Anda UI menu konteks bertingkat adaptif. Coba ubah posisi konten dengan memilih. Opsi yang Anda pilih akan memperbarui perataan petak. Hal ini juga memengaruhi cara penempatan anchor posisi popover.

Fokus dan ikuti

Demo ini menggabungkan primitif CSS dengan menghadirkan :has(). Idenya adalah untuk mentransisikan indikator visual untuk input yang memiliki fokus.

Lakukan hal ini dengan menyetel anchor baru saat runtime. Untuk demo ini, properti kustom cakupan akan diperbarui pada fokus input.

#email {
    anchor-name: --email;
  }
  #name {
    anchor-name: --name;
  }
  #password {
    anchor-name: --password;
  }
:root:has(#email:focus) {
    --active-anchor: --email;
  }
  :root:has(#name:focus) {
    --active-anchor: --name;
  }
  :root:has(#password:focus) {
    --active-anchor: --password;
  }

:root {
    --active-anchor: --name;
    --active-left: anchor(var(--active-anchor) right);
    --active-top: calc(
      anchor(var(--active-anchor) top) +
        (
          (
              anchor(var(--active-anchor) bottom) -
                anchor(var(--active-anchor) top)
            ) * 0.5
        )
    );
  }
.form-indicator {
    left: var(--active-left);
    top: var(--active-top);
    transition: all 0.2s;
}

Tetapi, bagaimana Anda bisa melangkah lebih jauh? Anda dapat menggunakannya untuk beberapa bentuk overlay instruksional. Tooltip dapat berpindah di antara lokasi menarik dan memperbarui kontennya. Anda dapat melakukan crossfade konten. Animasi diskrit yang memungkinkan Anda menganimasikan display atau View Transitions dapat berfungsi di sini.

Kalk diagram batang

Hal menyenangkan lain yang dapat Anda lakukan dengan penempatan anchor adalah mengombinasikannya dengan calc. Bayangkan sebuah diagram di mana Anda memiliki beberapa popover yang menandai diagramnya.

Anda dapat melacak nilai tertinggi dan terendah menggunakan CSS min dan max. CSS untuk itu akan terlihat seperti ini:

.chart__tooltip--max {
    left: anchor(--chart right);
    bottom: max(
      anchor(--anchor-1 top),
      anchor(--anchor-2 top),
      anchor(--anchor-3 top)
    );
    translate: 0 50%;
  }

Ada beberapa JavaScript yang diperlukan untuk memperbarui nilai diagram dan beberapa CSS untuk menata gaya diagram. Namun, pemosisian anchor menangani pembaruan tata letak untuk kita.

Tuas Pengubah Ukuran

Anda tidak harus hanya menambatkan ke satu elemen. Anda dapat menggunakan banyak anchor untuk sebuah elemen. Anda mungkin telah memperhatikan hal itu dalam contoh diagram batang. Tooltip ditambatkan ke diagram, lalu ke batang yang sesuai. Jika Anda mengambil konsep itu lebih jauh, Anda bisa menggunakannya untuk mengubah ukuran elemen.

Anda dapat memperlakukan titik link seperti tuas pengubah ukuran kustom dan memanfaatkan nilai inset.

.container {
   position: absolute;
   inset:
     anchor(--handle-1 top)
     anchor(--handle-2 right)
     anchor(--handle-2 bottom)
     anchor(--handle-1 left);
 }

Dalam demo ini, GreenSock Draggable membuat pegangan Draggable. Namun, elemen <img> diubah ukurannya untuk mengisi penampung yang menyesuaikan untuk mengisi jarak di antara tuas.

Sebuah Menu Pilihan?

Yang terakhir ini sedikit menggoda untuk apa yang akan datang. Namun, Anda dapat membuat popover yang dapat difokuskan dan kini Anda memiliki penempatan anchor. Anda dapat membuat dasar elemen <select> yang dapat ditata gayanya.

<div class="select-menu">
<button popovertarget="listbox">
 Select option
 <svg>...</svg>
</button>
<div popover="auto" id="listbox">
   <option>A</option>
   <option>Styled</option>
   <option>Select</option>
</div>
</div>

anchor implisit akan mempermudah hal ini. Tapi, CSS untuk titik awal yang belum sempurna bisa terlihat seperti ini:

[popovertarget] {
 anchor-name: --select-button;
}
[popover] {
  anchor-default: --select-button;
  top: anchor(bottom);
  width: anchor-size(width);
  left: anchor(left);
}

Gabungkan fitur Popover API dengan penempatan Anchor CSS, dan Anda akan berhasil.

Akan lebih baik jika Anda mulai memperkenalkan hal-hal seperti :has(). Anda dapat memutar penanda saat dibuka:

.select-menu:has(:open) svg {
  rotate: 180deg;
}

Ke mana Anda harus mengambilnya? Apa lagi yang kita perlukan untuk membuat select berfungsi? Kita akan menyimpannya untuk artikel berikutnya. Namun jangan khawatir, akan hadir elemen pilihan yang dapat ditata gayanya. Nantikan kabar terbarunya.


Selesai!

Platform web terus berkembang. Pemosisian anchor CSS adalah bagian penting untuk meningkatkan cara Anda mengembangkan kontrol UI. Hal ini akan mengabstraksi Anda dari beberapa keputusan rumit tersebut. Tetapi juga akan memungkinkan Anda untuk melakukan hal-hal yang tidak pernah dapat Anda lakukan sebelumnya. Seperti menata gaya elemen <select>. Utarakan pendapat Anda.

Foto oleh CHUTTERSNAP di Unsplash