การสร้างคอมโพเนนต์การตั้งค่า

ภาพรวมพื้นฐานเกี่ยวกับวิธีสร้างคอมโพเนนต์การตั้งค่าของแถบเลื่อนและช่องทำเครื่องหมาย

ในโพสต์นี้ ฉันต้องการแชร์แนวคิดเกี่ยวกับการสร้างคอมโพเนนต์การตั้งค่าสำหรับเว็บที่ปรับเปลี่ยนตามอุปกรณ์ รองรับอินพุตอุปกรณ์หลายรายการ และทำงานในเบราว์เซอร์ต่างๆ ได้ ลองใช้เดโม

สาธิต

หากคุณชอบดูวิดีโอหรือต้องการดูตัวอย่าง UI/UX ของสิ่งที่เรากำลังสร้าง โปรดดูคำแนะนำสั้นๆ บน YouTube

ภาพรวม

เราได้แบ่งแง่มุมต่างๆ ขององค์ประกอบนี้ออกเป็นส่วนๆ ดังนี้

  1. เลย์เอาต์
  2. สี
  3. อินพุตช่วงที่กำหนดเอง
  4. อินพุตช่องทำเครื่องหมายที่กำหนดเอง
  5. ข้อควรพิจารณาเกี่ยวกับการช่วยเหลือพิเศษ
  6. JavaScript

เลย์เอาต์

นี่คือการสาธิต GUI Challenge ครั้งแรกที่เป็น CSS Grid ทั้งหมด ตารางกริดแต่ละรายการที่ไฮไลต์ด้วยเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome สำหรับตารางกริดมีดังนี้

เส้นขอบสีสันสดใสและการวางซ้อนระยะห่างของช่องว่างที่ช่วยแสดงช่องทั้งหมดที่ประกอบกันเป็นเลย์เอาต์การตั้งค่า

Just for gap

เลย์เอาต์ที่พบบ่อยที่สุดมีดังนี้

foo {
  display: grid;
  gap: var(--something);
}

ฉันเรียกเลย์เอาต์นี้ว่า "just for gap" เพราะใช้ตารางกริดเพื่อเพิ่มช่องว่างระหว่างบล็อกเท่านั้น

เลย์เอาต์ 5 แบบใช้กลยุทธ์นี้ โดยเลย์เอาต์ทั้งหมดจะแสดงดังนี้

เลย์เอาต์ตารางกริดแนวตั้งที่ไฮไลต์ด้วยเส้นขอบและเติมช่องว่าง

องค์ประกอบ fieldset ซึ่งมีกลุ่มอินพุตแต่ละกลุ่ม (.fieldset-item) ใช้ gap: 1px เพื่อสร้างเส้นขอบบางๆ ระหว่างองค์ประกอบ ไม่ต้องแก้ปัญหาเรื่องชายแดนที่ซับซ้อน

ช่องว่างที่เติมเต็มแล้ว
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
กลเม็ดการใช้เส้นขอบ
.grid {
  display: grid;

  & > .fieldset-item {
    background: var(--bg-surface-2);

    &:not(:last-child) {
      border-bottom: 1px solid var(--bg-surface-1);
    }
  }
}

การตัดบรรทัดตารางกริดแบบเป็นธรรมชาติ

เลย์เอาต์ที่ซับซ้อนที่สุดคือเลย์เอาต์มาโคร ซึ่งเป็นระบบเลย์เอาต์เชิงตรรกะระหว่าง <main> ถึง <form>

การจัดเนื้อหาที่ตัดขึ้นบรรทัดใหม่ให้อยู่กึ่งกลาง

ทั้ง Flexbox และ Grid มีความสามารถในการalign-itemsหรือalign-content และเมื่อจัดการกับองค์ประกอบที่ตัดขึ้นบรรทัดใหม่ การจัดแนวเลย์เอาต์ content จะกระจายพื้นที่ว่างระหว่างองค์ประกอบย่อยเป็นกลุ่ม

main {
  display: grid;
  gap: var(--space-xl);
  place-content: center;
}

องค์ประกอบหลักใช้place-content: center ชวเลขการจัดแนวเพื่อให้องค์ประกอบย่อยอยู่ตรงกลางในแนวตั้งและแนวนอนทั้งในเลย์เอาต์แบบ 1 และ 2 คอลัมน์

ดูวิดีโอด้านบนเพื่อดูว่า "เนื้อหา" ยังคงอยู่ตรงกลางแม้ว่าจะมีการตัดบรรทัดก็ตาม

ทำซ้ำ minmax ของการปรับพอดีอัตโนมัติ

<form> ใช้เลย์เอาต์ตารางกริดแบบปรับเปลี่ยนได้สำหรับแต่ละส่วน เลย์เอาต์นี้จะเปลี่ยนจาก 1 คอลัมน์เป็น 2 คอลัมน์ตามพื้นที่ว่างที่มีอยู่

form {
  display: grid;
  gap: var(--space-xl) var(--space-xxl);
  grid-template-columns: repeat(auto-fit, minmax(min(10ch, 100%), 35ch));
  align-items: flex-start;
  max-width: 89vw;
}

ตารางกริดนี้มีค่าสำหรับ row-gap (--space-xl) แตกต่างจาก column-gap (--space-xxl) เพื่อเพิ่มความเฉพาะตัวให้กับเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ เมื่อคอลัมน์ซ้อนกัน เราต้องการช่องว่างขนาดใหญ่ แต่ไม่ใหญ่เท่ากับตอนที่อยู่บนหน้าจอกว้าง

พร็อพเพอร์ตี้ grid-template-columns ใช้ฟังก์ชัน CSS 3 ฟังก์ชัน ได้แก่ repeat(), minmax() และ min() Una Kravets มีบล็อกโพสต์เกี่ยวกับเลย์เอาต์ที่ยอดเยี่ยมในเรื่องนี้ โดยเรียกว่า RAM

เลย์เอาต์ของเรามีส่วนเพิ่มเติมพิเศษ 3 ส่วนเมื่อเทียบกับของ Una

  • เราส่งฟังก์ชัน min() เพิ่มเติม
  • เราจะระบุ align-items: flex-start
  • มีสไตล์ max-width: 89vw

Evan Minto ได้อธิบายฟังก์ชัน min() เพิ่มเติมไว้อย่างดีในบล็อกของเขาในโพสต์ Intrinsically Responsive CSS Grid with minmax() and min() เราขอแนะนำให้อ่านโพสต์ดังกล่าว การแก้ไขการจัดแนว flex-start คือการนำเอฟเฟกต์การยืดเริ่มต้นออก เพื่อให้องค์ประกอบย่อยของเลย์เอาต์นี้ไม่จำเป็นต้องมีความสูงเท่ากัน แต่สามารถมีความสูงตามธรรมชาติและตามจริงได้ วิดีโอ YouTube มีรายละเอียดสั้นๆ เกี่ยวกับการเพิ่มการจัดแนวนี้

max-width: 89vw เป็นเรื่องที่ควรค่าแก่การอธิบายสั้นๆ ในโพสต์นี้ เราจะแสดงเลย์เอาต์ที่มีและไม่มีการใช้สไตล์ให้ดู

บอกว่าคุณกำลังคิดอะไรอยู่ เมื่อระบุ max-width จะเป็นการให้บริบท การกำหนดขนาดที่ชัดเจน หรือการกำหนดขนาดที่แน่นอนสำหรับอัลกอริทึมเลย์เอาต์ auto-fit เพื่อให้ทราบจำนวนการทำซ้ำที่สามารถใส่ลงในพื้นที่ได้ แม้ว่าพื้นที่ดังกล่าวจะดูเหมือน "เต็มความกว้าง" แต่ตามข้อกำหนดของตารางกริด CSS คุณต้องระบุขนาดที่แน่นอนหรือขนาดสูงสุด ฉันได้ระบุขนาดสูงสุดแล้ว

แล้วทำไมต้องเป็น 89vw เพราะ "มันใช้ได้" กับเลย์เอาต์ของฉัน เราและทีม Chrome กำลังตรวจสอบว่าเหตุใดค่าที่สมเหตุสมผลกว่า เช่น 100vw จึงไม่เพียงพอ และปัญหานี้เป็นข้อบกพร่องจริงหรือไม่

การเว้นวรรค

ความกลมกลืนส่วนใหญ่ของเลย์เอาต์นี้มาจากระยะห่างที่จำกัด ซึ่งก็คือ 7 ระยะห่าง

:root {
  --space-xxs: .25rem;
  --space-xs:  .5rem;
  --space-sm:  1rem;
  --space-md:  1.5rem;
  --space-lg:  2rem;
  --space-xl:  3rem;
  --space-xxl: 6rem;
}

การใช้งานเหล่านี้เข้ากันได้ดีกับ Grid, CSS @nest และไวยากรณ์ระดับ 5 ของ @media ตัวอย่างเช่น ชุดสไตล์เลย์เอาต์ <main> ที่สมบูรณ์

main {
  display: grid;
  gap: var(--space-xl);
  place-content: center;
  padding: var(--space-sm);

  @media (width >= 540px) {
    & {
      padding: var(--space-lg);
    }
  }

  @media (width >= 800px) {
    & {
      padding: var(--space-xl);
    }
  }
}

ตารางกริดที่มีเนื้อหาอยู่ตรงกลาง โดยมีการเว้นระยะห่างจากขอบปานกลางโดยค่าเริ่มต้น (เช่น บนอุปกรณ์เคลื่อนที่) แต่เมื่อมีพื้นที่วิวพอร์ตมากขึ้น ก็จะกระจายออกโดยเพิ่มระยะห่างจากขอบ CSS ปี 2021 ดูดีมาก

จำเลย์เอาต์ก่อนหน้านี้ที่ "ใช้เพื่อเว้นช่องว่างเท่านั้น" ได้ไหม ต่อไปนี้เป็นเวอร์ชันที่สมบูรณ์ยิ่งขึ้นของลักษณะที่ปรากฏในคอมโพเนนต์นี้

header {
  display: grid;
  gap: var(--space-xxs);
}

section {
  display: grid;
  gap: var(--space-md);
}

สี

การใช้สีอย่างควบคุมได้ช่วยให้การออกแบบนี้โดดเด่นในด้านการแสดงออกแต่ก็ยังคงความเรียบง่าย ฉันทำแบบนี้

:root {
  --surface1: lch(10 0 0);
  --surface2: lch(15 0 0);
  --surface3: lch(20 0 0);
  --surface4: lch(25 0 0);

  --text1: lch(95 0 0);
  --text2: lch(75 0 0);
}

ฉันตั้งชื่อสีพื้นผิวและสีข้อความเป็นตัวเลขแทนชื่ออย่าง surface-dark และ surface-darker เพราะในการค้นหาสื่อ ฉันจะสลับสี และสีอ่อนและสีเข้มจะไม่มีความหมาย

ฉันจะสลับค่าในคิวรีสื่อค่ากำหนดดังนี้

:root {
  ...

  @media (prefers-color-scheme: light) {
    & {
      --surface1: lch(90 0 0);
      --surface2: lch(100 0 0);
      --surface3: lch(98 0 0);
      --surface4: lch(85 0 0);

      --text1: lch(20 0 0);
      --text2: lch(40 0 0);
    }
  }
}

สิ่งสำคัญคือต้องดูภาพรวมและกลยุทธ์โดยรวมอย่างรวดเร็วก่อนที่เราจะเจาะลึกรายละเอียดไวยากรณ์สี แต่ก่อนจะไปไกลกว่านี้ เราขอย้อนกลับไปสักนิด

LCH?

โดยไม่ต้องลงลึกถึงทฤษฎีสีมากนัก LCH เป็นไวยากรณ์ที่เน้นมนุษย์เป็นหลัก ซึ่งตอบสนองต่อวิธีที่เรารับรู้สี ไม่ใช่วิธีที่เราวัดสีด้วยคณิตศาสตร์ (เช่น 255) ซึ่งทำให้มีข้อได้เปรียบที่ชัดเจน เนื่องจากมนุษย์สามารถเขียนได้ง่ายกว่าและมนุษย์คนอื่นๆ จะเข้าใจการปรับเปลี่ยนเหล่านี้

ภาพหน้าจอของหน้าเว็บ pod.link/csspodcast โดยมีตอน Color 2: Perception เปิดอยู่
ดูข้อมูลเกี่ยวกับสีที่รับรู้ได้ (และอื่นๆ) ใน CSS Podcast

สำหรับวันนี้ ในการสาธิตนี้ เราจะเน้นที่ไวยากรณ์และค่าที่ฉันกำลังสลับเพื่อสร้างโหมดสว่างและมืด มาดูพื้นผิว 1 รายการและสีข้อความ 1 สีกัน

:root {
  --surface1: lch(10 0 0);
  --text1:    lch(95 0 0);

  @media (prefers-color-scheme: light) {
    & {
      --surface1: lch(90 0 0);
      --text1:    lch(40 0 0);
    }
  }
}

--surface1: lch(10 0 0) แปลงเป็นความสว่าง 10%, โครมา 0 และสี 0: สีเทาเข้มมากที่ไม่มีสี จากนั้นในคิวรีสื่อสำหรับโหมดสว่าง ความสว่าง จะเปลี่ยนเป็น 90% โดยมี --surface1: lch(90 0 0); และนั่นคือสาระสำคัญของกลยุทธ์นี้ เริ่มด้วยการเปลี่ยนความสว่างระหว่าง 2 ธีม โดยคงอัตราส่วนคอนทราสต์ที่การออกแบบต้องการหรือสิ่งที่สามารถรักษาการช่วยเหลือพิเศษไว้ได้

ข้อดีของ lch() ในที่นี้คือความสว่างเป็นสิ่งที่มนุษย์รับรู้ได้ และเราจะรู้สึก ดีกับการเปลี่ยนแปลง % ของความสว่าง ซึ่งจะแตกต่างจากเดิม % อย่างสม่ำเสมอ และรับรู้ได้ hsl() เช่น ไม่น่าเชื่อถือเท่า

ยังมีข้อมูลเพิ่มเติมให้เรียนรู้เกี่ยวกับขอบเขตสีและ lch() หากคุณสนใจ มาแล้ว!

ตอนนี้ CSS ไม่สามารถเข้าถึงสีเหล่านี้ได้เลย ขอย้ำอีกครั้งว่าเราไม่สามารถเข้าถึง 1 ใน 3 ของสีในจอภาพสมัยใหม่ส่วนใหญ่ได้ และสีเหล่านี้ไม่ใช่แค่สีธรรมดา แต่เป็นสีที่สดใสที่สุดที่หน้าจอสามารถแสดงได้ เว็บไซต์ของเรามีสีซีดเนื่องจากฮาร์ดแวร์ของจอภาพพัฒนาเร็วกว่าข้อกำหนด CSS และการใช้งานเบราว์เซอร์

Lea Verou

การควบคุมแบบฟอร์มที่ปรับเปลี่ยนได้ด้วยรูปแบบสี

เบราว์เซอร์หลายตัวมีตัวควบคุมธีมมืด ซึ่งปัจจุบันคือ Safari และ Chromium แต่คุณต้องระบุใน CSS หรือ HTML ว่าการออกแบบของคุณใช้ธีมมืด

ตัวอย่างด้านบนแสดงผลลัพธ์ของพร็อพเพอร์ตี้จากแผงสไตล์ของเครื่องมือสำหรับนักพัฒนาเว็บ การสาธิตใช้แท็ก HTML ซึ่งในความเห็นของฉันมักจะเป็นตำแหน่งที่ดีกว่า

<meta name="color-scheme" content="dark light">

ดูข้อมูลทั้งหมดได้ในcolor-schemeบทความนี้โดย Thomas Steiner ยังมีอะไรให้คุณได้มากกว่า การป้อนข้อมูลในช่องทำเครื่องหมายสีเข้ม

CSS accent-color

เมื่อเร็วๆ นี้มีกิจกรรมเกี่ยวกับ accent-color ในองค์ประกอบของแบบฟอร์ม ซึ่งเป็นรูปแบบ CSS เดียวที่สามารถเปลี่ยนสีที่ใช้ในองค์ประกอบอินพุตของเบราว์เซอร์ได้ อ่านข้อมูลเพิ่มเติมได้ที่นี่ใน GitHub ฉันได้รวมไว้ในสไตล์ของฉัน สำหรับคอมโพเนนต์นี้แล้ว เนื่องจากเบราว์เซอร์รองรับ ช่องทำเครื่องหมายของฉันจึงจะเข้ากับธีมมากขึ้นด้วยป๊อปสีชมพูและสีม่วง

input[type="checkbox"] {
  accent-color: var(--brand);
}

ภาพหน้าจอจาก Chromium ใน Linux ของช่องทำเครื่องหมายสีชมพู

ป๊อปสีที่มีการไล่ระดับสีแบบคงที่และโฟกัสภายใน

สีจะโดดเด่นที่สุดเมื่อใช้ในปริมาณที่พอเหมาะ และวิธีหนึ่งที่ฉันชอบใช้เพื่อให้ได้ผลลัพธ์ดังกล่าวคือการโต้ตอบกับ UI ที่มีสีสัน

มีฟีดแบ็กและการโต้ตอบของ UI หลายชั้นในวิดีโอด้านบน ซึ่งช่วยสร้างบุคลิกให้กับการโต้ตอบโดยทำดังนี้

  • ไฮไลต์บริบท
  • แสดงความคิดเห็นเกี่ยวกับ UI ของ "ความเต็ม" ของค่าในช่วง
  • การแสดงความคิดเห็นเกี่ยวกับ UI ว่าช่องยอมรับอินพุต

CSS จะใช้คลาสจำลอง :focus-within เพื่อแสดงความคิดเห็นเมื่อมีการโต้ตอบกับองค์ประกอบหนึ่งๆ เพื่อเปลี่ยนลักษณะที่ปรากฏขององค์ประกอบต่างๆ มาดูรายละเอียดของ .fieldset-item กัน ซึ่งน่าสนใจมาก

.fieldset-item {
  ...

  &:focus-within {
    background: var(--surface2);

    & svg {
      fill: white;
    }

    & picture {
      clip-path: circle(50%);
      background: var(--brand-bg-gradient) fixed;
    }
  }
}

เมื่อองค์ประกอบย่อยรายการใดรายการหนึ่งขององค์ประกอบนี้มี focus-within:

  1. พื้นหลัง .fieldset-item จะได้รับการกำหนดสีพื้นผิวที่มีคอนทราสต์สูงกว่า
  2. svg ที่ฝังอยู่จะเติมสีขาวเพื่อให้มีคอนทราสต์สูงขึ้น
  3. <picture> clip-path ที่ซ้อนกันจะขยายเป็นวงกลมเต็มวงและพื้นหลังจะเต็มไปด้วยการไล่ระดับสีแบบคงที่ที่สว่าง

ช่วงที่กำหนดเอง

จากองค์ประกอบอินพุต HTML ต่อไปนี้ เราจะแสดงวิธีปรับแต่งลักษณะที่ปรากฏ

<input type="range">

องค์ประกอบนี้มี 3 ส่วนที่เราต้องปรับแต่ง ได้แก่

  1. องค์ประกอบ/คอนเทนเนอร์ช่วง
  2. ติดตาม
  3. Thumb

รูปแบบองค์ประกอบช่วง

input[type="range"] {
  /* style setting variables */
  --track-height: .5ex;
  --track-fill: 0%;
  --thumb-size: 3ex;
  --thumb-offset: -1.25ex;
  --thumb-highlight-size: 0px;

  appearance: none;         /* clear styles, make way for mine */
  display: block;
  inline-size: 100%;        /* fill container */
  margin: 1ex 0;            /* ensure thumb isn't colliding with sibling content */
  background: transparent;  /* bg is in the track */
  outline-offset: 5px;      /* focus styles have space */
}

CSS 2-3 บรรทัดแรกเป็นส่วนที่กำหนดเองของรูปแบบ และเราหวังว่าการติดป้ายกำกับอย่างชัดเจนจะช่วยได้ รูปแบบที่เหลือส่วนใหญ่เป็นรูปแบบการรีเซ็ต เพื่อให้มีรากฐานที่สอดคล้องกันสำหรับการสร้างส่วนที่ยุ่งยากของคอมโพเนนต์

ติดตามสไตล์

input[type="range"]::-webkit-slider-runnable-track {
  appearance: none; /* clear styles, make way for mine */
  block-size: var(--track-height);
  border-radius: 5ex;
  background:
    /* hard stop gradient:
        - half transparent (where colorful fill we be)
        - half dark track fill
        - 1st background image is on top
    */
    linear-gradient(
      to right,
      transparent var(--track-fill),
      var(--surface1) 0%
    ),
    /* colorful fill effect, behind track surface fill */
    var(--brand-bg-gradient) fixed;
}

เคล็ดลับของวิธีนี้คือการ "เปิดเผย" สีเติมที่สดใส ซึ่งทำได้โดยใช้การไล่ระดับสีแบบ Hard Stop ที่ด้านบน การไล่ระดับสีจะโปร่งใสจนถึงเปอร์เซ็นต์การเติม และหลังจากนั้นจะใช้สีพื้นผิวแทร็กที่ไม่ได้เติม ด้านหลังพื้นผิวที่ไม่มีสีนั้นคือสีแบบเต็มความกว้างที่รอให้ความโปร่งใสเผยออกมา

รูปแบบการเติมพื้นแทร็ก

การออกแบบของฉันต้องใช้ JavaScript เพื่อรักษารูปแบบการเติม มีกลยุทธ์ที่ใช้ CSS เท่านั้น แต่ต้องใช้องค์ประกอบ Thumb ที่มีความสูงเท่ากับแทร็ก และฉันไม่สามารถค้นหาความสอดคล้องกันภายในขีดจำกัดเหล่านั้นได้

/* grab sliders on page */
const sliders = document.querySelectorAll('input[type="range"]')

/* take a slider element, return a percentage string for use in CSS */
const rangeToPercent = slider => {
  const max = slider.getAttribute('max') || 10;
  const percent = slider.value / max * 100;

  return `${parseInt(percent)}%`;
};

/* on page load, set the fill amount */
sliders.forEach(slider => {
  slider.style.setProperty('--track-fill', rangeToPercent(slider));

  /* when a slider changes, update the fill prop */
  slider.addEventListener('input', e => {
    e.target.style.setProperty('--track-fill', rangeToPercent(e.target));
  })
})

เราคิดว่าการอัปเกรดนี้จะช่วยให้ภาพดูดีขึ้น แถบเลื่อนทำงานได้ดีโดยไม่ต้องใช้ JavaScript, ไม่จำเป็นต้องใช้พร็อพเพอร์ตี้ --track-fill และจะไม่มีสไตล์การเติมหากไม่มีพร็อพเพอร์ตี้ดังกล่าว หาก JavaScript พร้อมใช้งาน ให้ป้อนข้อมูลคุณสมบัติที่กำหนดเองพร้อมกับสังเกตการเปลี่ยนแปลงของผู้ใช้ โดยซิงค์คุณสมบัติที่กำหนดเองกับค่า

โพสต์ที่ยอดเยี่ยมใน CSS-Tricks โดย Ana Tudor แสดงให้เห็นถึงโซลูชัน CSS เท่านั้นสำหรับการเติมแทร็ก ฉันยังพบว่าองค์ประกอบ range นี้สร้างแรงบันดาลใจได้ดีมาก

รูปแบบ Thumb

input[type="range"]::-webkit-slider-thumb {
  appearance: none; /* clear styles, make way for mine */
  cursor: ew-resize; /* cursor style to support drag direction */
  border: 3px solid var(--surface3);
  block-size: var(--thumb-size);
  inline-size: var(--thumb-size);
  margin-top: var(--thumb-offset);
  border-radius: 50%;
  background: var(--brand-bg-gradient) fixed;
}

สไตล์ส่วนใหญ่เหล่านี้มีไว้เพื่อสร้างวงกลมที่สวยงาม คุณจะเห็นการไล่ระดับสีพื้นหลังคงที่ซึ่งรวมสีแบบไดนามิกของปุ่มเลื่อน แทร็ก และองค์ประกอบ SVG ที่เกี่ยวข้องเข้าด้วยกัน ฉันแยกสไตล์สำหรับการโต้ตอบเพื่อช่วยแยกเทคนิค box-shadow ที่ใช้สำหรับไฮไลต์เมื่อวางเมาส์เหนือองค์ประกอบ

@custom-media --motionOK (prefers-reduced-motion: no-preference);

::-webkit-slider-thumb {
  …

  /* shadow spread is initally 0 */
  box-shadow: 0 0 0 var(--thumb-highlight-size) var(--thumb-highlight-color);

  /* if motion is OK, transition the box-shadow change */
  @media (--motionOK) {
    & {
      transition: box-shadow .1s ease;
    }
  }

  /* on hover/active state of parent, increase size prop */
  @nest input[type="range"]:is(:hover,:active) & {
    --thumb-highlight-size: 10px;
  }
}

เป้าหมายคือการสร้างไฮไลต์ภาพเคลื่อนไหวที่จัดการได้ง่ายสำหรับความคิดเห็นของผู้ใช้ การใช้เงาของกล่องช่วยให้ฉันหลีกเลี่ยงการทริกเกอร์เลย์เอาต์ด้วยเอฟเฟกต์ได้ ฉันทำเช่นนี้โดยสร้างเงาที่ไม่เบลอและตรงกับรูปทรงกลมขององค์ประกอบ Thumb จากนั้นฉันจะเปลี่ยนและเปลี่ยนขนาดการกระจายเมื่อวางเมาส์เหนือ

ถ้าเอฟเฟกต์ไฮไลต์ทำได้ง่ายๆ ในช่องทำเครื่องหมายก็คงดี…

ตัวเลือกข้ามเบราว์เซอร์

ฉันพบว่าฉันต้องใช้ตัวเลือก -webkit- และ -moz- เหล่านี้เพื่อให้ได้ความสอดคล้องกันในเบราว์เซอร์ต่างๆ:

input[type="range"] {
  &::-webkit-slider-runnable-track {}
  &::-moz-range-track {}
  &::-webkit-slider-thumb {}
  &::-moz-range-thumb {}
}

ช่องทำเครื่องหมายที่กำหนดเอง

จากองค์ประกอบอินพุต HTML ต่อไปนี้ เราจะแสดงวิธีปรับแต่งลักษณะที่ปรากฏ

<input type="checkbox">

องค์ประกอบนี้มี 3 ส่วนที่เราต้องปรับแต่ง ได้แก่

  1. องค์ประกอบช่องทำเครื่องหมาย
  2. ป้ายกำกับที่เชื่อมโยง
  3. เอฟเฟกต์ไฮไลต์

องค์ประกอบช่องทำเครื่องหมาย

input[type="checkbox"] {
  inline-size: var(--space-sm);   /* increase width */
  block-size: var(--space-sm);    /* increase height */
  outline-offset: 5px;            /* focus style enhancement */
  accent-color: var(--brand);     /* tint the input */
  position: relative;             /* prepare for an absolute pseudo element */
  transform-style: preserve-3d;   /* create a 3d z-space stacking context */
  margin: 0;
  cursor: pointer;
}

สไตล์ transform-style และ position จะเตรียมพร้อมสำหรับองค์ประกอบเทียมที่เราจะแนะนำในภายหลังเพื่อจัดสไตล์ไฮไลต์ นอกเหนือจากนั้น ส่วนใหญ่จะเป็นเรื่องสไตล์เล็กๆ น้อยๆ ที่เป็นความคิดเห็นของฉันเอง ฉันชอบให้เคอร์เซอร์เป็นตัวชี้ ชอบการออฟเซ็ตเส้นขอบ ช่องทำเครื่องหมายเริ่มต้นมีขนาดเล็กเกินไป และหากรองรับ accent-color ให้นำช่องทำเครื่องหมายเหล่านี้มาไว้ในรูปแบบสีของแบรนด์

ป้ายกำกับช่องทำเครื่องหมาย

การระบุป้ายกำกับสำหรับช่องทำเครื่องหมายมีความสำคัญด้วยเหตุผล 2 ประการ ส่วนแรกคือการแสดงให้เห็นว่าค่าของช่องทำเครื่องหมายใช้ทำอะไร เพื่อตอบคำถามว่า "เปิดหรือปิดสำหรับอะไร" ประการที่ 2 คือเพื่อ UX ผู้ใช้เว็บคุ้นเคยกับการโต้ตอบกับช่องทำเครื่องหมายผ่านป้ายกำกับที่เกี่ยวข้อง

อินพุต
<input
  type="checkbox"
  id="text-notifications"
  name="text-notifications"
>
ป้ายกำกับ
<label for="text-notifications">
  <h3>Text Messages</h3>
  <small>Get notified about all text messages sent to your device</small>
</label>

ในป้ายกำกับ ให้ใส่แอตทริบิวต์ for ที่ชี้ไปยังช่องทำเครื่องหมายตามรหัส: <label for="text-notifications"> ในช่องทำเครื่องหมาย ให้ใส่ทั้งชื่อและรหัสซ้ำเพื่อให้แน่ใจว่าเครื่องมือและเทคโนโลยีต่างๆ เช่น เมาส์หรือโปรแกรมอ่านหน้าจอ จะค้นหาช่องทำเครื่องหมายนั้นได้ <input type="checkbox" id="text-notifications" name="text-notifications"> :hover, :active และอื่นๆ จะมาพร้อมกับการเชื่อมต่อโดยไม่มีค่าใช้จ่าย ซึ่งจะเพิ่มวิธีที่ผู้ใช้โต้ตอบกับแบบฟอร์มของคุณได้

ไฮไลต์ช่องทำเครื่องหมาย

ฉันต้องการให้อินเทอร์เฟซสอดคล้องกัน และองค์ประกอบแถบเลื่อนมีไฮไลต์ภาพขนาดย่อที่สวยงามซึ่งฉันต้องการใช้กับช่องทำเครื่องหมาย ภาพขนาดย่อสามารถใช้ box-shadow และพร็อพเพอร์ตี้ spread เพื่อปรับขนาดเงาขึ้นและลงได้ อย่างไรก็ตาม เอฟเฟกต์นั้นใช้ไม่ได้ที่นี่เนื่องจากช่องทำเครื่องหมายของเราเป็นสี่เหลี่ยมจัตุรัสและควรเป็นเช่นนั้น

ฉันสร้างเอฟเฟกต์ภาพแบบเดียวกันได้ด้วยองค์ประกอบจำลองและ CSS ที่ซับซ้อนจำนวนมาก

@custom-media --motionOK (prefers-reduced-motion: no-preference);

input[type="checkbox"]::before {
  --thumb-scale: .01;                        /* initial scale of highlight */
  --thumb-highlight-size: var(--space-xl);

  content: "";
  inline-size: var(--thumb-highlight-size);
  block-size: var(--thumb-highlight-size);
  clip-path: circle(50%);                     /* circle shape */
  position: absolute;                         /* this is why position relative on parent */
  top: 50%;                                   /* pop and plop technique (https://web.dev/centering-in-css#5-pop-and-plop) */
  left: 50%;
  background: var(--thumb-highlight-color);
  transform-origin: center center;            /* goal is a centered scaling circle */
  transform:                                  /* order here matters!! */
    translateX(-50%)                          /* counter balances left: 50% */
    translateY(-50%)                          /* counter balances top: 50% */
    translateZ(-1px)                          /* PUTS IT BEHIND THE CHECKBOX */
    scale(var(--thumb-scale))                 /* value we toggle for animation */
  ;
  will-change: transform;

  @media (--motionOK) {                       /* transition only if motion is OK */
    & {
      transition: transform .2s ease;
    }
  }
}

/* on hover, set scale custom property to "in" state */
input[type="checkbox"]:hover::before {
  --thumb-scale: 1;
}

การสร้างองค์ประกอบเทียมวงกลมเป็นงานที่ตรงไปตรงมา แต่การวางไว้ด้านหลังองค์ประกอบที่แนบมานั้นทำได้ยากกว่า นี่คือภาพก่อนและหลังที่ฉันแก้ไข

แม้จะเป็นการโต้ตอบเล็กๆ น้อยๆ แต่ก็สำคัญสำหรับฉันในการรักษาความสอดคล้องของภาพ เทคนิคการปรับขนาดภาพเคลื่อนไหวเป็นเทคนิคเดียวกับที่เราใช้ในที่อื่นๆ เราตั้งค่าคุณสมบัติที่กำหนดเองเป็นค่าใหม่และให้ CSS เปลี่ยนแปลงค่าตามค่ากำหนดการเคลื่อนไหว ฟีเจอร์หลักในที่นี้คือ translateZ(-1px) องค์ประกอบหลักสร้างพื้นที่ 3 มิติ และองค์ประกอบย่อยเทียมนี้ก็เข้าไปอยู่ในพื้นที่นั้นโดยวางตัวเองไว้ด้านหลังเล็กน้อยในพื้นที่แกน Z

การช่วยเหลือพิเศษ

วิดีโอ YouTube แสดงการสาธิตการโต้ตอบของเมาส์ แป้นพิมพ์ และโปรแกรมอ่านหน้าจอสำหรับคอมโพเนนต์การตั้งค่านี้ได้อย่างดีเยี่ยม เราจะอธิบายรายละเอียดบางส่วนที่นี่

ตัวเลือกองค์ประกอบ HTML

<form>
<header>
<fieldset>
<picture>
<label>
<input>

ซึ่งแต่ละรายการจะมีคำแนะนำและเคล็ดลับสำหรับเครื่องมือการท่องเว็บของผู้ใช้ องค์ประกอบบางอย่างให้คำแนะนำในการโต้ตอบ บางองค์ประกอบเชื่อมต่อการโต้ตอบ และบางองค์ประกอบช่วยกำหนดรูปร่างของแผนผังการช่วยเหลือพิเศษที่โปรแกรมอ่านหน้าจอใช้ไปยังส่วนต่างๆ

แอตทริบิวต์ HTML

เราสามารถซ่อนองค์ประกอบที่โปรแกรมอ่านหน้าจอไม่จำเป็นต้องใช้ได้ ซึ่งในกรณีนี้คือไอคอนข้างแถบเลื่อน

<picture aria-hidden="true">

วิดีโอด้านบนแสดงขั้นตอนการใช้โปรแกรมอ่านหน้าจอใน Mac OS สังเกตว่าโฟกัสอินพุตย้ายจากแถบเลื่อนหนึ่งไปยังแถบเลื่อนถัดไปโดยตรงอย่างไร เนื่องจากเราได้ซ่อนไอคอนที่อาจเป็นจุดหยุดพักระหว่างทางไปยังแถบเลื่อนถัดไป หากไม่มีแอตทริบิวต์นี้ ผู้ใช้จะต้องหยุด ฟัง และข้ามรูปภาพที่อาจมองไม่เห็น

SVG เป็นชุดคณิตศาสตร์ ลองเพิ่มองค์ประกอบ <title> สำหรับชื่อที่แสดงเมื่อวางเมาส์เหนือองค์ประกอบและแสดงความคิดเห็นที่มนุษย์อ่านได้เกี่ยวกับสิ่งที่คณิตศาสตร์สร้างขึ้น

<svg viewBox="0 0 24 24">
  <title>A note icon</title>
  <path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
</svg>

นอกจากนี้ เรายังใช้ HTML ที่ทำเครื่องหมายไว้อย่างชัดเจนมากพอ เพื่อให้แบบฟอร์มทำงานได้ดีกับเมาส์ คีย์บอร์ด ตัวควบคุมวิดีโอเกม และโปรแกรมอ่านหน้าจอ

JavaScript

เราได้พูดถึงวิธีจัดการสีเติมของแทร็กจาก JavaScript ไปแล้ว ลองมาดู JavaScript ที่เกี่ยวข้องกับ <form> กัน

const form = document.querySelector('form');

form.addEventListener('input', event => {
  const formData = Object.fromEntries(new FormData(form));
  console.table(formData);
})

ทุกครั้งที่มีการโต้ตอบและเปลี่ยนแปลงแบบฟอร์ม คอนโซลจะบันทึกแบบฟอร์มเป็นออบเจ็กต์ลงในตารางเพื่อให้ตรวจสอบได้ง่ายก่อนส่งไปยังเซิร์ฟเวอร์

ภาพหน้าจอของผลลัพธ์ console.table() ซึ่งข้อมูลแบบฟอร์มจะแสดงในตาราง

บทสรุป

ตอนนี้คุณก็รู้แล้วว่าเราทำยังไง แล้วคุณล่ะจะทำยังไง ซึ่งทำให้สถาปัตยกรรมคอมโพเนนต์ มีความสนุกสนาน ใครจะเป็นคนสร้างเวอร์ชันแรกที่มีช่องในเฟรมเวิร์กที่ตนเองชื่นชอบ 🙂

มาลองใช้วิธีการที่หลากหลายและเรียนรู้วิธีทั้งหมดในการสร้างบนเว็บกัน สร้างเดโม ทวีตลิงก์มาหาฉัน แล้วฉันจะเพิ่มลงในส่วนรีมิกซ์ของชุมชนด้านล่าง

รีมิกซ์ของชุมชน

  • @tomayac สำหรับสไตล์ของพื้นที่วางเมาส์เหนือป้ายกำกับช่องทำเครื่องหมาย เวอร์ชันนี้ไม่มีช่องว่างระหว่างองค์ประกอบเมื่อวางเมาส์เหนือองค์ประกอบ: เดโมและแหล่งที่มา