API การเลือกที่ปรับแต่งได้มีวิธีที่อิงตาม CSS และประกาศได้ในการสร้างภาพเคลื่อนไหวให้กับองค์ประกอบ <select> และเครื่องมือเลือกแบบเลื่อนลง เมื่อรวม appearance: base-select เข้ากับเทคนิคแอนิเมชัน CSS สมัยใหม่ เช่น @starting-style และลักษณะการเปลี่ยนผ่าน allow-discrete คุณจะสร้างการเปลี่ยนผ่าน UI ระดับพรีเมียมที่ลื่นไหลสำหรับองค์ประกอบเลเยอร์บนสุดได้โดยไม่ต้องพึ่งพาไลบรารี JavaScript ขนาดใหญ่
ก่อนหน้านี้ การสร้างภาพเคลื่อนไหวของเมนูแบบเลื่อนลงที่เลือกแบบเนทีฟเป็นไปไม่ได้เนื่องจาก UI ของเมนูแสดงผลนอกข้อจำกัดของวิวพอร์ตที่เข้าถึงได้ เมื่อใช้ appearance: base-select เครื่องมือเลือกจะจัดสไตล์และทำให้เคลื่อนไหวได้เหมือนกับองค์ประกอบอื่นๆ ของหน้า
วิธีการนำไปใช้งาน
วิธีใช้เครื่องมือเลือกแบบเคลื่อนไหว
- เลือกใช้การปรับแต่ง: ใช้
appearance: base-selectกับทั้งองค์ประกอบ<select>และองค์ประกอบจำลอง::picker(select) - เปิดใช้การเปลี่ยนขนาดอัตโนมัติ (ไม่บังคับ): กำหนด
interpolate-size: allow-keywords(โดยปกติจะอยู่ใน:root) เพื่อให้เบราว์เซอร์เปลี่ยนระหว่างค่าเมตริกที่ไม่ต่อเนื่อง เช่นheight: autoและheight: 0 - ทำให้คอนเทนเนอร์ชั้นบนสุดเคลื่อนไหว: ใช้รูปแบบการเข้า/ออกมาตรฐานกับ
::picker(select)เพื่อให้แน่ใจว่าการเปลี่ยนความทึบแสงทำงานเมื่อย้ายระหว่างdisplay: noneและdisplay: blockคุณต้องใช้transition-behavior: allow-discrete(มักเขียนในบรรทัดเป็นtransition: display 0.3s allow-discrete) - เชื่อมโยงกับสถานะเปิดด้วย
@starting-style: ใช้@starting-styleเพื่อกำหนดสไตล์พื้นฐานที่เบราว์เซอร์ควรคำนวณก่อนที่การเปลี่ยนภาพจะเริ่มต้น เช่น หากต้องการให้เฟดเข้า ให้ตั้งค่าความทึบแสงเป็น0ภายในบล็อก@starting-style - หมุนไอคอน: ใช้ตัวเลือกโฟกัสหรือตัวเลือกที่ใช้งานอยู่ขององค์ประกอบจำลอง เช่น
:open::picker-iconเพื่อใช้การเปลี่ยน (เช่น การหมุนหรือการแปล) กับตัวบ่งชี้ลูกศร
โค้ดตัวอย่าง: การปรับขนาดและการเฟดแบบ Smooth Select
ตัวอย่างต่อไปนี้แสดงการเลือกที่กำหนดเองซึ่งจัดรูปแบบด้วยภาพเคลื่อนไหวของหน้ามาตรฐานสำหรับคอนเทนเนอร์เครื่องมือเลือก
<!-- Always use a <label> linked via 'for' to the select for accessibility -->
<label for="theme-select">Visual Theme</label>
<select id="theme-select" class="animated-select" name="theme">
<!-- The <button> inside <select> becomes the visible trigger when appearance: base-select is used -->
<button>
<!-- <selectedcontent> automatically displays the content of the chosen <option> -->
<selectedcontent></selectedcontent>
</button>
<option value="system">
<!-- MANDATORY: Decorative inline SVGs MUST set aria-hidden="true" to prevent redundant screen reader announcement -->
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
<line x1="8" y1="21" x2="16" y2="21"></line>
<line x1="12" y1="17" x2="12" y2="21"></line>
</svg>
System Default
</option>
<option value="light">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
Light UI
</option>
</select>
/* Opt-in to customizable select */
.animated-select,
.animated-select::picker(select) {
appearance: base-select;
}
/* Enable auto-keyword transitions (usually set globally at :root) */
:root {
interpolate-size: allow-keywords;
}
/* Style the visible trigger and icon rotation */
.animated-select {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0.875rem 1rem;
font-size: 1rem;
border-radius: 8px;
cursor: pointer;
transition: border-color 0.2s ease;
}
.animated-select::picker-icon {
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.animated-select:open::picker-icon {
transform: rotate(180deg);
}
/*
* The Picker Container
* Uses top-layer animations with `allow-discrete` visibility hooks
*/
.animated-select::picker(select) {
background: white;
border-radius: 12px;
box-shadow: 0 10px 25px -3px rgba(0,0,0,0.1);
padding: 0.5rem;
margin-top: 0.25rem;
width: anchor-size(width);
overflow: hidden;
/* The crucial transition setting for popover animations */
transition:
display 0.4s allow-discrete,
overlay 0.4s allow-discrete,
opacity 0.4s cubic-bezier(0.16, 1, 0.3, 1),
height 0.4s cubic-bezier(0.16, 1, 0.3, 1);
opacity: 0;
height: 0;
}
/* Open State */
.animated-select:open::picker(select) {
opacity: 1;
height: auto;
}
/* @starting-style to hook the transition on initial popover open */
@starting-style {
.animated-select:open::picker(select) {
opacity: 0;
height: 0;
}
}
/* Support for SVG inside Options and Selected Content */
.animated-select option svg,
.animated-select selectedcontent svg {
flex-shrink: 0; /* Prevent icons from shrinking */
width: 1.25rem;
height: 1.25rem;
}
/* MANDATORY: Provide multiple indicators (e.g. bold font and distinct background) for the checked state to avoid color-only state communication */
.animated-select option:checked {
font-weight: 700;
background-color: #f1f5f9;
}
/* Ensure copy-paste safety for users with motion sensitivities */
@media (prefers-reduced-motion: reduce) {
.animated-select::picker(select),
.animated-select::picker-icon {
transition: none !important;
}
}
การนำไปใช้เชิงกลยุทธ์และแนวทางปฏิบัติแนะนำ
- ควรใช้
@starting-styleเมื่อต้องการให้ภาพเคลื่อนไหวแสดงขึ้นเมื่อองค์ประกอบเปลี่ยนจากdisplay: noneเป็นมองเห็นได้ - อย่าใช้การล็อกการเลื่อนเฉพาะกิจ องค์ประกอบชั้นบนสุดที่จัดการโดย "base-select" ควรอนุญาตให้มีลักษณะการปิดฉากหลังตามธรรมชาติ
- ควรตรวจสอบค่ากำหนดการลดการเคลื่อนไหว รวมข้อจำกัดของภาพเคลื่อนไหวไว้ในคำค้นหาสื่อ
prefers-reduced-motionเสมอ เพื่อให้ผู้ที่ได้รับผลกระทบจากอาการเมารถเมาเรือเข้าถึงสภาพแวดล้อมได้ - ทดสอบลักษณะการทำงานของเลย์เอาต์ การตั้งค่า
appearance: base-selectจะนำลักษณะการทำงานของเบราว์เซอร์เริ่มต้นในการปรับขนาดการเลือกตามความกว้างของตัวเลือกที่ยาวที่สุดออก คุณอาจต้องตั้งค่าความกว้างคงที่หรือใช้ข้อจำกัด Flex/Grid เพื่อป้องกันไม่ให้เลย์เอาต์เปลี่ยนแปลง - ตรวจสอบว่า
<select>มีแอตทริบิวต์nameและ<label>ที่เชื่อมโยงกัน ซึ่งจะช่วยให้มั่นใจได้ว่าแม้จะมี UI ที่กำหนดเอง คอมโพเนนต์จะยังคงเข้าถึงได้โดยโปรแกรมอ่านหน้าจอและทำงานได้อย่างถูกต้องกับการส่งแบบฟอร์มมาตรฐาน - ใช้แอตทริบิวต์
valueมาตรฐานในตัวเลือกเพื่อให้แน่ใจว่าการส่งแบบฟอร์มทำงานเหมือนเดิมทุกประการ
กลยุทธ์สำรอง
การสนับสนุนเบราว์เซอร์และทางเลือกสำหรับ <select> ที่ปรับแต่งได้
สำหรับเบราว์เซอร์ที่ยังไม่รองรับ appearance: base-select องค์ประกอบ <select> จะลดระดับลงเป็นเมนูแบบเลื่อนลงของระบบปฏิบัติการมาตรฐานอย่างราบรื่น
- ละเว้นเนื้อหาที่ไม่ใช่ข้อความ: เบราว์เซอร์รุ่นเก่าจะตัดแท็ก HTML (เช่น
<svg>หรือ<div>) ภายในแท็ก<option>และแสดงผลเฉพาะโหนดข้อความ ตรวจสอบว่าเนื้อหาข้อความของ<option>อ่านได้และมีความหมายในตัวเอง - การจัดการโครงสร้าง HTML: โปรแกรมแยกวิเคราะห์มาตรฐานอาจละเว้นแท็ก
<button>และ<selectedcontent>ภายใน<select>หรือถือว่าแท็กดังกล่าวไม่ถูกต้อง ไม่จำเป็นต้องใช้ Polyfill ของ JavaScript ที่มีขนาดใหญ่สำหรับการเพิ่มประสิทธิภาพแบบต่อเนื่อง หากคุณมองว่าข้อความมาตรฐานเป็นทางเลือกสำรองที่อ่านได้
document.addEventListener("DOMContentLoaded", () => {
// Check if browser supports base-select value
if (!CSS.supports("appearance", "base-select")) {
// Custom select overrides are not supported natively.
}
});