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

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

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

การสาธิต

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

ภาพรวม

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

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

เลย์เอาต์

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

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

สำหรับช่องว่าง

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

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

ฉันเรียกเลย์เอาต์นี้ว่า "เฉพาะสำหรับช่องว่าง" เพราะจะใช้เพียงตารางกริดเพื่อเพิ่มช่องว่างระหว่างบล็อกต่างๆ

เลย์เอาต์ 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 และตารางกริดต่างก็ช่วยให้ align-items หรือ align-content ทำงานได้ และเมื่อจัดการองค์ประกอบการรวม content การปรับแนวเลย์เอาต์จะกระจายพื้นที่ว่างให้กับกลุ่มย่อยเป็นกลุ่ม

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

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

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

เพิ่มต่ำสุดของการปรับให้พอดีอัตโนมัติซ้ำ

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

max-width: 89vw มีรายละเอียดเล็กน้อยในโพสต์นี้ ขอแสดงเลย์เอาต์ที่มีและไม่มีการใช้สไตล์

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

ทำไมต้องเป็น 89vw เนื่องจาก "ใช้งานได้แล้ว" สำหรับเลย์เอาต์ของฉัน ฉันและเจ้าหน้าที่ Chrome อีก 2-3 คนกำลังตรวจสอบว่าค่าที่สมเหตุสมผลมากกว่า เช่น 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;
}

ใช้โฟลว์เหล่านี้ได้ดีกับตารางกริด, 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 ที่มีฟีเจอร์สี 2: ตอนของการรับรู้ดึงขึ้นมา
ดูข้อมูลเกี่ยวกับสีที่รับรู้ (และอื่นๆ อีกมากมาย) ในพอดแคสต์ CSS

สำหรับวันนี้ ในการสาธิตนี้ เรามาเน้นที่ไวยากรณ์และค่าที่ผมกำลังพลิกเพื่อเปลี่ยนเป็นแสงและมืด ลองดูที่ 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;
    }
  }
}

เมื่อรายการย่อยอย่างใดอย่างหนึ่งขององค์ประกอบนี้มีโฟกัสจากภายใน:

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

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

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

<input type="range">

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

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

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

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 */
}

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

รูปแบบแทร็ก

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;
}

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

ติดตามรูปแบบการเติมข้อมูล

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

/* 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 นี้สร้างแรงบันดาลใจได้อย่างมาก

รูปแบบนิ้วหัวแม่มือ

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;
  }
}

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

หากเอฟเฟกต์การไฮไลต์อย่างเดียวง่ายเกินไปต่อช่องทำเครื่องหมาย...

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

ฉันพบว่าฉันต้องใช้ตัวเลือก -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"> ในช่องทำเครื่องหมาย ให้เพิ่มทั้งชื่อและรหัสเข้าไป 2 เท่าเพื่อให้แน่ใจว่าคุณจะเจอเครื่องมือและเทคโนโลยีต่างๆ เช่น เมาส์หรือโปรแกรมอ่านหน้าจอ ดังนี้ <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-space

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

วิดีโอ 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() ที่ข้อมูลแบบฟอร์มจะแสดงในตาราง

บทสรุป

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

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

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

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