Xây dựng thành phần Cài đặt

Tổng quan cơ bản về cách tạo thành phần cài đặt của thanh trượt và hộp đánh dấu.

Trong bài đăng này, tôi muốn chia sẻ suy nghĩ về việc xây dựng một thành phần Cài đặt cho web có khả năng thích ứng, hỗ trợ nhiều phương thức nhập trên thiết bị và hoạt động trên nhiều trình duyệt. Hãy dùng thử bản minh hoạ.

Bản minh hoạ

Nếu bạn thích xem video hơn hoặc muốn xem trước giao diện người dùng/trải nghiệm người dùng của những gì chúng tôi đang tạo, dưới đây là hướng dẫn từng bước ngắn hơn trên YouTube:

Tổng quan

Tôi đã chia các khía cạnh của thành phần này thành các phần sau:

  1. Bố cục
  2. Màu
  3. Dữ liệu đầu vào của dải ô tuỳ chỉnh
  4. Dữ liệu đầu vào tuỳ chỉnh dạng hộp đánh dấu
  5. Những điểm cần cân nhắc về khả năng hỗ trợ tiếp cận
  6. JavaScript

Bố cục

Đây là bản minh hoạ đầu tiên của Thử thách GUI sử dụng toàn bộ CSS Grid! Dưới đây là mỗi lưới được làm nổi bật bằng Công cụ của Chrome cho lưới:

Các đường viền đầy màu sắc và lớp phủ khoảng cách giúp hiển thị tất cả các hộp tạo nên bố cục cài đặt

Chỉ để tạo khoảng trống

Bố cục phổ biến nhất:

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

Tôi gọi bố cục này là "chỉ dành cho khoảng trống" vì bố cục này chỉ sử dụng lưới để thêm khoảng trống giữa các khối.

Có 5 bố cục sử dụng chiến lược này, cụ thể như sau:

Bố cục lưới dọc được làm nổi bật bằng đường viền và lấp đầy khoảng trống

Phần tử fieldset chứa từng nhóm đầu vào (.fieldset-item) sử dụng gap: 1px để tạo đường viền tóc giữa các phần tử. Không có giải pháp đường viền phức tạp!

Lấp đầy khoảng trống
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
Thủ thuật đường viền
.grid {
  display: grid;

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

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

Tự nhiên xuống dòng

Cuối cùng, bố cục phức tạp nhất là bố cục vĩ mô, hệ thống bố cục logic giữa <main><form>.

Căn giữa nội dung gói

Hộp linh hoạt và lưới đều cung cấp khả năng align-items hoặc align-content. Ngoài ra, khi xử lý các phần tử gói, việc căn chỉnh bố cục content sẽ phân phối không gian giữa các phần tử con dưới dạng một nhóm.

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

Phần tử chính đang sử dụng viết tắt căn chỉnh place-content: center để các phần tử con được căn giữa theo chiều dọc và chiều ngang trong cả bố cục một và hai cột.

Hãy xem trong video trên cách "nội dung" được căn giữa, mặc dù đã tự xuống dòng.

Lặp lại tính năng tự động điều chỉnh kích thước theo tỷ lệ minmax

<form> sử dụng bố cục lưới thích ứng cho mỗi phần. Bố cục này chuyển đổi từ một đến hai cột dựa trên không gian có sẵn.

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

Lưới này có một giá trị cho row-gap (--space-xl) khác với column-gap (--space-xxl) để đặt điểm chạm tuỳ chỉnh đó trên bố cục thích ứng. Khi xếp chồng các cột, chúng ta muốn có một khoảng trống lớn, nhưng không lớn như khi xem trên màn hình rộng.

Thuộc tính grid-template-columns sử dụng 3 hàm CSS: repeat(), minmax()min(). Una Kravets có một bài đăng thú vị trên blog về bố cục này về chủ đề này, với tên gọi là RAM.

Có 3 bổ sung đặc biệt trong bố cục của chúng ta, nếu bạn so sánh với bố cục của Una:

  • Chúng ta truyền thêm một hàm min().
  • Chúng ta chỉ định align-items: flex-start.
  • Có một kiểu max-width: 89vw.

Evan Minto đã mô tả rõ ràng về hàm min() bổ sung trên blog của họ trong bài đăng Lưới CSS phản hồi nội tại với minmax() và min(). Bạn nên đọc qua. Việc điều chỉnh căn chỉnh flex-start là để xoá hiệu ứng kéo giãn mặc định, để các phần tử con của bố cục này không cần có chiều cao bằng nhau mà có thể có chiều cao tự nhiên, nội tại. Video trên YouTube có thông tin phân tích nhanh về việc bổ sung căn chỉnh này.

max-width: 89vw đáng để phân tích một chút trong bài đăng này. Hãy để tôi cho bạn thấy bố cục có và không áp dụng kiểu:

Chuyện gì đang xảy ra? Khi được chỉ định, max-width sẽ cung cấp ngữ cảnh, kích thước rõ ràng hoặc kích thước xác định cho thuật toán bố cục auto-fit để biết số lần lặp lại có thể vừa với không gian. Mặc dù rõ ràng không gian là "chiều rộng đầy đủ", nhưng theo thông số kỹ thuật lưới CSS, bạn phải cung cấp kích thước xác định hoặc kích thước tối đa. Tôi đã cung cấp kích thước tối đa.

Vậy tại sao lại là 89vw? Vì "nó hoạt động" với bố cục của tôi. Tôi và một số người khác trong nhóm Chrome đang điều tra lý do tại sao một giá trị hợp lý hơn, chẳng hạn như 100vw, là không đủ và liệu đây có phải là lỗi hay không.

Giãn cách

Phần lớn sự hài hoà của bố cục này đến từ một bảng màu giới hạn về khoảng cách, chính xác là 7.

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

Việc sử dụng các flow này thực sự hiệu quả với lưới, CSS @nestcú pháp cấp 5 của @media. Dưới đây là ví dụ về tập hợp bố cục <main> đầy đủ.

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

Lưới có nội dung được căn giữa, có khoảng đệm vừa phải theo mặc định (như trên thiết bị di động). Tuy nhiên, khi có thêm không gian khung nhìn, khung nhìn sẽ mở rộng bằng cách tăng khoảng đệm. CSS 2021 trông khá ổn!

Bạn còn nhớ bố cục trước đó, "chỉ dành cho khoảng trống" không? Dưới đây là phiên bản hoàn chỉnh hơn về giao diện của các lớp đó trong thành phần này:

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

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

Màu

Việc sử dụng màu sắc có kiểm soát đã giúp thiết kế này trở nên nổi bật, biểu cảm nhưng vẫn tối giản. Tôi thực hiện như sau:

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

Tôi đặt tên cho màu sắc của bề mặt và văn bản bằng các con số thay vì tên như surface-darksurface-darker vì trong truy vấn nội dung đa phương tiện, tôi sẽ lật các màu này và màu sáng và tối sẽ không có ý nghĩa.

Tôi lật các giá trị này trong truy vấn nội dung nghe nhìn ưu tiên như sau:

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

Điều quan trọng là bạn phải xem nhanh bức tranh tổng thể và chiến lược trước khi đi sâu vào thông tin chi tiết về cú pháp màu. Nhưng vì tôi đã đi hơi xa, nên hãy để tôi quay lại một chút.

LCH?

Nếu không đi sâu vào lý thuyết màu sắc, LCH là một cú pháp hướng đến con người, phục vụ cách chúng ta nhận biết màu sắc, chứ không phải cách chúng ta đo lường màu sắc bằng toán học (như 255). Điều này mang lại một lợi thế rõ ràng vì con người có thể viết mã dễ dàng hơn và những người khác cũng sẽ điều chỉnh được theo.

Ảnh chụp màn hình trang web pod.link/csspodcast, trong đó có tập Color 2: Perception (Màu sắc 2: Nhận thức)
Hãy tìm hiểu về màu sắc cảm nhận (và nhiều nội dung khác!) trên Podcast CSS

Hôm nay, trong bản minh hoạ này, hãy tập trung vào cú pháp và các giá trị tôi đang chuyển sang để tạo chế độ sáng và tối. Hãy xem xét 1 nền tảng và 1 màu văn bản:

: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) chuyển thành độ sáng 10%, sắc độ 0 và sắc thái 0: màu xám không màu rất tối. Sau đó, trong truy vấn nội dung nghe nhìn cho chế độ sáng, độ sáng sẽ được chuyển sang 90% bằng --surface1: lch(90 0 0);. Đó là cốt lõi của chiến lược này. Bắt đầu bằng cách thay đổi độ sáng giữa 2 giao diện, duy trì tỷ lệ tương phản mà thiết kế yêu cầu hoặc những yếu tố có thể duy trì khả năng hỗ trợ tiếp cận.

Ưu điểm của lch() ở đây là độ sáng được định hướng theo con người và chúng ta có thể cảm thấy hài lòng về việc thay đổi %, rằng độ sáng sẽ khác biệt một cách rõ ràng và nhất quán. Ví dụ: hsl() không đáng tin cậy.

Bạn có thể tìm hiểu thêm về không gian màu và lch() nếu quan tâm. Sắp có rồi!

CSS hiện không thể truy cập vào những màu này. Tôi xin lặp lại: Chúng ta không có quyền truy cập vào một phần ba màu sắc trong hầu hết màn hình hiện đại. Và đây không chỉ là những màu sắc bất kỳ, mà là những màu sắc sống động nhất mà màn hình có thể hiển thị. Các trang web của chúng tôi bị quá tải vì phần cứng màn hình phát triển nhanh hơn so với thông số kỹ thuật CSS và việc triển khai trình duyệt.

Lea Verou

Chế độ điều khiển biểu mẫu thích ứng sử dụng bảng phối màu

Nhiều trình duyệt cung cấp các chế độ điều khiển giao diện tối, hiện là Safari và Chromium, nhưng bạn phải chỉ định trong CSS hoặc HTML rằng thiết kế của bạn sử dụng các chế độ này.

Phần trên minh hoạ hiệu ứng của thuộc tính này trong bảng điều khiển Styles (Kiểu) của DevTools. Bản minh hoạ sử dụng thẻ HTML. Theo tôi, thẻ này thường là vị trí tốt hơn:

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

Tìm hiểu tất cả thông tin về tính năng này trong bài viết color-scheme của Thomas Steiner. Còn nhiều điều khác để bạn khai thác ngoài tính năng nhập hộp đánh dấu màu tối!

CSS accent-color

Đã có hoạt động gần đây xung quanh accent-color trên các phần tử biểu mẫu, là một kiểu CSS duy nhất có thể thay đổi màu sắc thái màu được sử dụng trong phần tử đầu vào của trình duyệt. Đọc thêm về vấn đề này tại đây trên GitHub. Tôi đã đưa thuộc tính này vào các kiểu của thành phần này. Khi trình duyệt hỗ trợ, hộp đánh dấu của tôi sẽ phù hợp hơn với chủ đề bằng màu hồng và tím.

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

Ảnh chụp màn hình hộp đánh dấu màu hồng trong Chromium trên Linux

Ảnh nổi bật màu với hiệu ứng chuyển màu cố định và tiêu điểm bên trong

Màu sắc nổi bật nhất khi được sử dụng một cách tiết kiệm và một trong những cách tôi muốn đạt được điều đó là thông qua các hoạt động tương tác trên giao diện người dùng đầy màu sắc.

Có nhiều lớp phản hồi và tương tác trên giao diện người dùng trong video trên, giúp tạo nên đặc điểm tương tác bằng cách:

  • Nêu bật ngữ cảnh.
  • Cung cấp phản hồi trên giao diện người dùng về "mức độ đầy" của giá trị trong phạm vi.
  • Cung cấp phản hồi trên giao diện người dùng cho biết một trường đang chấp nhận dữ liệu đầu vào.

Để cung cấp phản hồi khi một phần tử đang được tương tác, CSS đang sử dụng lớp giả lập :focus-within để thay đổi giao diện của nhiều phần tử. Hãy cùng phân tích .fieldset-item, điều này rất thú vị:

.fieldset-item {
  ...

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

    & svg {
      fill: white;
    }

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

Khi một trong các phần tử con của phần tử này có tiêu điểm bên trong:

  1. Nền .fieldset-item được gán màu bề mặt có độ tương phản cao hơn.
  2. svg lồng nhau được tô màu trắng để có độ tương phản cao hơn.
  3. <picture> clip-path lồng nhau mở rộng thành một vòng tròn đầy đủ và nền được tô bằng màu chuyển đổi cố định sáng.

Phạm vi tùy chỉnh

Với phần tử đầu vào HTML sau đây, tôi sẽ cho bạn thấy cách tôi tuỳ chỉnh giao diện của phần tử đó:

<input type="range">

Có 3 phần trong phần tử này mà chúng ta cần tuỳ chỉnh:

  1. Phần tử / vùng chứa dải ô
  2. Theo dõi
  3. Thumb

Kiểu phần tử dải ô

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

Một vài dòng đầu tiên của CSS là phần tuỳ chỉnh của kiểu và tôi hy vọng rằng việc gắn nhãn rõ ràng cho chúng sẽ hữu ích. Các kiểu còn lại chủ yếu là kiểu đặt lại để cung cấp nền tảng nhất quán cho việc xây dựng các phần khó khăn của thành phần.

Kiểu theo dõi

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

Bí quyết để làm điều này là "để lộ" màu nền rực rỡ. Bạn có thể thực hiện việc này với dải chuyển màu ở trên cùng. Độ dốc trong suốt đến tỷ lệ phần trăm lấp đầy và sau đó sử dụng màu bề mặt của kênh chưa được lấp đầy. Phía sau bề mặt chưa được lấp đầy đó là một màu có chiều rộng đầy đủ, đang chờ độ trong suốt để hiển thị.

Kiểu tô màu theo dõi

Thiết kế của tôi yêu cầu JavaScript để duy trì kiểu tô. Chỉ có các chiến lược cho CSS nhưng các chiến lược này yêu cầu phần tử thumb phải có cùng chiều cao với bản nhạc và tôi không thể tìm thấy sự hài hoà trong những giới hạn đó.

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

Tôi nghĩ điều này tạo ra sự nâng cấp trực quan đẹp mắt. Thanh trượt hoạt động tốt mà không cần JavaScript, thuộc tính --track-fill không bắt buộc, chỉ là thanh trượt sẽ không có kiểu tô nếu không có. Nếu có JavaScript, hãy điền thuộc tính tuỳ chỉnh đồng thời quan sát mọi thay đổi của người dùng, đồng bộ hoá thuộc tính tuỳ chỉnh với giá trị.

Đây là một bài đăng rất hay về CSS-Tricks của Ana Tudor, trong đó minh hoạ một giải pháp chỉ dành cho CSS để điền kênh. Tôi cũng thấy phần tử range này rất truyền cảm hứng.

Kiểu hình thu nhỏ

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

Phần lớn các kiểu này đều để tạo một vòng tròn đẹp mắt. Một lần nữa, bạn sẽ thấy hiệu ứng chuyển màu nền cố định ở đó giúp hợp nhất màu động của các hình thu nhỏ, bản nhạc và các phần tử SVG được liên kết. Tôi đã tách các kiểu tương tác để giúp tách biệt kỹ thuật box-shadow đang được sử dụng cho hiệu ứng làm nổi bật khi di chuột:

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

Mục tiêu là tạo một điểm nhấn hình ảnh động dễ quản lý cho ý kiến phản hồi của người dùng. Bằng cách sử dụng hiệu ứng bóng hộp, tôi có thể tránh kích hoạt bố cục. Tôi thực hiện việc này bằng cách tạo một bóng không bị mờ và khớp với hình tròn của phần tử con trỏ. Sau đó, tôi thay đổi và chuyển đổi kích thước lan rộng của nó khi di chuột.

Nếu hiệu ứng làm nổi bật dễ dàng như vậy trên các hộp đánh dấu…

Bộ chọn trên nhiều trình duyệt

Tôi nhận thấy mình cần các bộ chọn -webkit--moz- này để đạt được tính nhất quán trên nhiều trình duyệt:

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

Hộp đánh dấu tuỳ chỉnh

Với phần tử đầu vào HTML sau đây, tôi sẽ cho bạn thấy cách tôi tuỳ chỉnh giao diện của phần tử đó:

<input type="checkbox">

Có 3 phần trong phần tử này mà chúng ta cần tuỳ chỉnh:

  1. Thành phần hộp đánh dấu
  2. Nhãn được liên kết
  3. Hiệu ứng làm nổi bật

Thành phần hộp đánh dấu

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

Kiểu transform-styleposition chuẩn bị cho phần tử giả mà chúng ta sẽ giới thiệu sau để tạo kiểu cho phần nổi bật. Nếu không, đó chủ yếu là nội dung mang phong cách nhỏ tuổi của tôi. Tôi muốn con trỏ là con trỏ, tôi thích các khoảng bù đường viền, hộp đánh dấu mặc định quá nhỏ và nếu accent-color được hỗ trợ, hãy đưa các hộp đánh dấu này vào bảng phối màu thương hiệu.

Nhãn hộp đánh dấu

Bạn cần cung cấp nhãn cho hộp đánh dấu vì 2 lý do. Mục đầu tiên là để đại diện cho mục đích sử dụng giá trị hộp đánh dấu, nhằm trả lời câu hỏi "bật hay tắt để làm gì?" Thứ hai là về trải nghiệm người dùng, người dùng web đã quen với việc tương tác với hộp đánh dấu thông qua nhãn liên kết.

input
<input
  type="checkbox"
  id="text-notifications"
  name="text-notifications"
>
nhãn
<label for="text-notifications">
  <h3>Text Messages</h3>
  <small>Get notified about all text messages sent to your device</small>
</label>

Trên nhãn của bạn, hãy đặt thuộc tính for trỏ đến một hộp đánh dấu theo mã nhận dạng: <label for="text-notifications">. Trên hộp đánh dấu, hãy nhân đôi cả tên và mã nhận dạng để đảm bảo hộp đánh dấu đó được tìm thấy bằng nhiều công cụ và công nghệ, chẳng hạn như chuột hoặc trình đọc màn hình: <input type="checkbox" id="text-notifications" name="text-notifications">. :hover, :active và nhiều tính năng khác được cung cấp miễn phí khi kết nối, giúp tăng số cách tương tác với biểu mẫu.

Làm nổi bật hộp đánh dấu

Tôi muốn giữ cho giao diện của mình nhất quán và phần tử thanh trượt có một hình thu nhỏ nổi bật đẹp mắt mà tôi muốn sử dụng với hộp đánh dấu. Hình thu nhỏ có thể sử dụng box-shadow và thuộc tính spread để tăng và giảm bóng đổ. Tuy nhiên, hiệu ứng đó không hoạt động ở đây vì hộp đánh dấu của chúng ta và phải là hình vuông.

Tôi có thể đạt được hiệu ứng hình ảnh tương tự bằng một phần tử giả và một lượng CSS khó khăn:

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

Việc tạo phần tử giả lập hình tròn là công việc đơn giản, nhưng đặt phần tử đó ở phía sau phần tử được đính kèm thì khó hơn. Dưới đây là hình ảnh trước và sau khi tôi khắc phục vấn đề:

Đây chắc chắn là một tương tác vi mô, nhưng điều quan trọng đối với tôi là phải duy trì tính nhất quán về hình ảnh. Kỹ thuật điều chỉnh tỷ lệ ảnh động giống như chúng ta đã sử dụng ở các nơi khác. Chúng ta đặt một thuộc tính tuỳ chỉnh thành một giá trị mới và để CSS chuyển đổi thuộc tính đó dựa trên các tuỳ chọn chuyển động. Tính năng chính ở đây là translateZ(-1px). Phần tử mẹ tạo một không gian 3D và phần tử con giả này đã khai thác vào đó bằng cách đặt lại một chút vào không gian z.

Hỗ trợ tiếp cận

Video trên YouTube minh hoạ rõ ràng các hoạt động tương tác bằng chuột, bàn phím và trình đọc màn hình cho thành phần cài đặt này. Tôi sẽ nêu một số chi tiết ở đây.

Lựa chọn phần tử HTML

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

Mỗi tệp này chứa các gợi ý và mẹo cho công cụ duyệt web của người dùng. Một số phần tử cung cấp gợi ý tương tác, một số kết nối khả năng tương tác và một số giúp định hình cây hỗ trợ tiếp cận mà trình đọc màn hình điều hướng.

Thuộc tính HTML

Chúng ta có thể ẩn các phần tử mà trình đọc màn hình không cần đến, trong trường hợp này là biểu tượng bên cạnh thanh trượt:

<picture aria-hidden="true">

Video ở trên minh hoạ quy trình của trình đọc màn hình trên Mac OS. Lưu ý cách tiêu điểm nhập di chuyển thẳng từ thanh trượt này sang thanh trượt tiếp theo. Điều này là do chúng ta đã ẩn biểu tượng có thể là một điểm dừng trên đường đến thanh trượt tiếp theo. Nếu không có thuộc tính này, người dùng sẽ phải dừng lại, nghe và di chuyển qua hình ảnh mà họ không thể nhìn thấy.

SVG là một tập hợp các phép toán, hãy thêm một phần tử <title> cho tiêu đề di chuột tự do và một nhận xét mà con người có thể đọc được về nội dung toán học đang tạo:

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

Ngoài ra, chúng tôi đã sử dụng HTML được đánh dấu rõ ràng đủ để các bài kiểm thử biểu mẫu thực sự hiệu quả trên chuột, bàn phím, tay điều khiển trò chơi điện tử và trình đọc màn hình.

JavaScript

Tôi đã trình bày cách quản lý màu nền theo dõi từ JavaScript, vì vậy, bây giờ hãy xem JavaScript có liên quan đến <form>:

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

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

Mỗi khi người dùng tương tác và thay đổi biểu mẫu, bảng điều khiển sẽ ghi lại biểu mẫu dưới dạng một đối tượng vào bảng để dễ dàng xem xét trước khi gửi đến máy chủ.

Ảnh chụp màn hình kết quả console.table(), trong đó dữ liệu biểu mẫu hiển thị trong một bảng

Kết luận

Giờ thì bạn đã biết cách tôi làm, còn bạn thì sao?! Điều này tạo ra một số cấu trúc thành phần thú vị! Ai sẽ tạo phiên bản đầu tiên có các khe trong khung yêu thích của họ? 🙂

Hãy đa dạng hoá các phương pháp và tìm hiểu tất cả các cách xây dựng trên web. Hãy tạo bản minh hoạ, gửi đường liên kết đến bản minh hoạ đó cho tôi trên Twitter và tôi sẽ thêm bản minh hoạ đó vào phần Bản phối lại của cộng đồng bên dưới!

Bản phối lại của cộng đồng

  • @tomayac với phong cách của họ liên quan đến vùng di chuột cho nhãn hộp đánh dấu! Phiên bản này không có khoảng cách di chuột giữa các phần tử: demosource.