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 gồm thanh trượt và hộp đánh dấu.

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

Bản minh hoạ

Nếu bạn thích xem video hoặc muốn có bả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 xây dựng, đây là hướng dẫn ngắn hơn về YouTube:

Tổng quan

Tôi đã chia nhỏ 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. Nhập hộp đánh dấu tuỳ chỉnh
  5. Những điểm cần lưu ý về khả năng hỗ trợ tiếp cận
  6. JavaScript

Bố cục

Đây là bản minh hoạ Thử thách GUI đầu tiên tất cả Lưới CSS! Dưới đây là từng lưới được làm nổi bật bằng Công cụ của Chrome cho nhà phát triển cho lưới:

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

Chỉ vì 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 cách" vì bố cục này chỉ sử dụng lưới để thêm khoảng trống giữa các khối.

Sau đây là 5 bố cục sử dụng chiến lược này:

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

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

Khoảng trống được lấp đầy
.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);
    }
  }
}

Xuống dòng tự nhiên

Bố cục phức tạp nhất là bố cục macro, hệ thống bố cục logic giữa <main><form>.

Căn giữa nội dung

Hộp linh hoạt và lưới đều cung cấp khả năng cho align-items hoặc align-content và khi xử lý các phần tử gói, việc căn chỉnh bố cục content sẽ phân bổ không gian giữa các phần tử con theo 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 video trên về cách "nội dung" vẫn được căn giữa, mặc dù việc xuống dòng đã xảy ra.

Lặp lại tự động điều chỉnh kích thước tối đa

<form> sử dụng bố cục lưới thích ứng cho từng phần. Bố cục này chuyển từ một sang 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ó giá trị row-gap (--space-xl) khác với giá trị column-gap (--space-xxl) để đặt thao tác chạm tuỳ chỉnh đó vào bố cục thích ứng. Khi ngăn xếp cột, chúng ta muốn có một khoảng cách lớn, nhưng không lớn như đang ở trên một 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 blog có bố cục tuyệt vời về vấn đề này, gọi là RAM.

Có 3 phần 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 sẽ chỉ định align-items: flex-start.
  • Có kiểu max-width: 89vw.

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

Trong bài đăng này, max-width: 89vw rất hữu ích. 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 có bao nhiêu lần lặp lại mà nó có thể vừa với không gian. Mặc dù rõ ràng là không gian là "chiều rộng đầy đủ", nhưng theo thông số kỹ thuật của 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" cho bố cục của tôi. Tôi và một vài người dùng Chrome khác đang tìm hiểu lý do khiến một giá trị hợp lý hơn, chẳng hạn như 100vw là chưa đủ và liệu đây có thực sự là lỗi hay không.

Giãn cách

Phần lớn nội dung hài hoà của bố cục này đến từ bảng khoảng cách giới hạn, 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;
}

Cách 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ụ, tập hợp kiểu 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);
    }
  }
}

Một lưới với 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ẽ được trải rộng bằng cách tăng khoảng đệm. Dịch vụ so sánh giá (CSS) năm 2021 có vẻ ổn định!

Bạn có nhớ bố cục trước đó, "chỉ dành cho khoảng cách" không? Sau đây là phiên bản hoàn chỉnh hơn về giao diện của thành phần này 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 nổi bật mà vẫn thể hiện được tính biểu đạt mà vẫn tối giản. Tôi làm 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 giao diện và màu văn bản bằng số thay vì các tên như surface-darksurface-darker vì trong một truy vấn nội dung nghe nhìn, tôi sẽ lật chúng và màu sáng cũng như tối sẽ không có ý nghĩa.

Tôi lật chúng trong một truy vấn phương tiện tùy chọ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 có thể xem nhanh thông tin tổng quan và chiến lược tổng thể trước khi chúng ta đi sâu vào chi tiết về cú pháp màu. Tuy nhiên, vì tôi đi trước một chút, nên tôi lùi lại một chút.

LCH?

Không đi sâu vào lĩnh vực lý thuyết màu sắc, LCH là một cú pháp hướng tới con người, phục vụ cho cách chúng ta cảm nhận 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 lợi thế khác biệt vì con người có thể viết mã dễ dàng hơn và những người khác sẽ phù hợp với các điều chỉnh này.

Ảnh chụp màn hình trang web pod.link/csspodcast, trong đó hiển thị tập Màu 2: Nhận biết
Tìm hiểu về màu sắc cảm nhận (và nhiều thông tin 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ị mà tôi đang lật để tạo ra giao diện sáng và tối. Hãy xem 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%, 0 sắc độ và 0 sắc độ: màu xám rất tối không màu. Sau đó, trong truy vấn nội dung nghe nhìn cho chế độ sáng, độ sáng sẽ được chuyển thành 90% bằng --surface1: lch(90 0 0);. Và đó là nội dung chính của chiến lược. Bắt đầu bằng cách chỉ cần 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 gì có thể duy trì khả năng hỗ trợ tiếp cận.

Điểm thưởng với lch() ở đây là sự nhẹ nhàng dành cho con người và chúng ta có thể thấy thoải mái về sự thay đổi % đối với nó, nghĩa là % khác biệt về mặt nhận thức và nhất quán. Ví dụ: hsl() không đáng tin cậy.

Bạn có thể tìm hiểu nhiều thông tin khác về hệ 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 các màu này. Hãy để tôi nhắc lại: Chúng ta không thể sử dụng 1/3 số màu có trong hầu hết các màn hình hiện đại. Và đây không chỉ là màu sắc bất kỳ, mà còn là những màu 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 đã ngừng hoạt động do phần cứng giám sát phát triển nhanh hơn so với thông số kỹ thuật CSS và cách triển khai trình duyệt.

Lea Verou

Các tuỳ chọn kiểm soát biểu mẫu thích ứng bằ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 giao diện này.

Nội dung ở trên minh hoạ hiệu quả của thuộc tính từ bảng điều khiển Kiểu của DevTools. Bản minh hoạ sử dụng thẻ HTML, theo tôi thường là ở vị trí tốt hơn:

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

Hãy tìm hiểu toàn bộ về vấn đề này trong bài viết color-scheme này của Thomas Steiner. Bạn sẽ nhận được rất nhiều thứ ngoài hộp đánh dấu màu tối!

Dịch vụ so sánh giá (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ề tính năng này tại đây trên GitHub. Tôi đã đưa nó vào kiểu của mình cho thành phần này. Khi trình duyệt hỗ trợ tính năng này, các hộp đánh dấu của tôi sẽ có chủ đề nhiều hơn với các hộp bật lên màu hồng và tím.

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

Ảnh chụp màn hình từ Chromium trên Linux với các hộp đánh dấu màu hồng

Nổi bật màu với độ dốc và tiêu điểm cố định trong

Màu sắc sẽ xuất hiện nhiều nhất khi được sử dụng một cách hạn chế 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 nhiều màu sắc trên giao diện người dùng.

Video trên có nhiều lớp phản hồi và tương tác trên giao diện người dùng, giúp thể hiện cá tính cho hoạt động tương tác bằng cách:

  • Làm nổi bật bối cảnh.
  • Cung cấp thông tin phản hồi về "mức độ đầy đủ" của giá trị trong phạm vi cho giao diện người dùng.
  • Cung cấp phản hồi về 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.

Để đưa ra ý kiến phản hồi khi một phần tử đang được tương tác, CSS đang sử dụng lớp giả :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ó tâm điểm-trong:

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

Phạm vi tùy chỉnh

Với phần tử nhập 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 phạm vi
  2. Theo dõi
  3. Thích

Kiểu phần tử phạm vi

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 số dòng đầu tiên của CSS là các 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 các kiểu đó sẽ giúp ích. Các kiểu còn lại chủ yếu là kiểu đặt lại, để cung cấp một nền tảng nhất quán cho việc xây dựng các phần phức tạp 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 thực hiện việc này với hiệu ứng chuyển màu dừng cứng ở trên cùng. Độ dốc trong suốt theo tỷ lệ phần trăm tô màu nền, và sau đó sử dụng màu của vùng hiển thị chưa được lấp đầy. Phía sau bề mặt chưa được lấp đầy đó là một màu đầy đủ chiều rộng, đang chờ độ trong suốt hiện ra.

Theo dõi kiểu tô màu nền

Thiết kế của tôi yêu cầu JavaScript để duy trì kiểu tô màu nền. Có các chiến lược chỉ dành 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 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 giúp nâng cấp hình ảnh đẹp mắt. Thanh trượt hoạt động tốt mà không cần JavaScript, không bắt buộc phải có đề xuất --track-fill, đơn giản là sẽ không có kiểu điền nếu không có. Nếu có JavaScript, hãy điền thuộc tính tuỳ chỉnh trong khi vẫn 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à bài đăng hay về CSS-Tricks của Ana Tudor, trong đó minh hoạ giải pháp chỉ dành cho CSS để điền theo dõi. 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 là để tạo ra một vòng tròn đẹp mắt. Một lần nữa, bạn sẽ thấy độ dốc của nền cố định ở đó, giúp hợp nhất các màu động của hình thu nhỏ, bản nhạc và các phần tử SVG được liên kết. Tôi đã tách riêng các kiểu cho hoạt động tương tác để giúp tách biệt kỹ thuật box-shadow được sử dụng cho phần làm nổi bật khi di chuột lên:

@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à để dễ quản lý và tạo hiệu ứng nổi bật bằng hình ảnh động để người dùng phản hồi. Bằng cách sử dụng hiệu ứng bóng đổ, tôi có thể tránh kích hoạt bố cục bằng hiệu ứng này. Tôi thực hiện việc này bằng cách tạo bóng không bị mờ và khớp với hình tròn của phần tử ngón tay cái. Sau đó, tôi thay đổi và chuyển đổi kích thước trải rộng của nó khi di chuột.

Nếu chỉ có hiệu ứng đánh dấu dễ thực hiện 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- sau để đạ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ử nhập 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. Phần tử hộp đánh dấu
  2. Nhãn được liên kết
  3. Hiệu ứng nổi bật

Phần tử 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 tôi sẽ giới thiệu sau này để tạo kiểu cho vùng nổi bật. Nếu không, đó chủ yếu là những nội dung theo phong cách nhẹ nhàng của tôi. Tôi thích con trỏ là con trỏ, tôi thích độ lệch đường viền, các 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 phải cung cấp nhãn cho các hộp đánh dấu vì 2 lý do. Đầu tiên là thể hiện giá trị hộp đánh dấu dùng cho mục đích gì, để trả lời "bật hoặc 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 các hộp đánh dấu thông qua các 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 hộp đánh dấu theo mã nhận dạng: <label for="text-notifications">. Trên hộp đánh dấu, hãy tăng gấp đôi cả tên và mã nhận dạng để đảm bảo hệ thống tìm thấy thông tin này trên 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 cách tương tác với biểu mẫu của bạn.

Đánh dấu hộp đánh dấu

Tôi muốn giữ cho các giao diện nhất quán và phần tử thanh trượt có một hình thu nhỏ được đánh dấu đẹ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/giảm tỷ lệ bóng đổ. Tuy nhiên, hiệu ứng đó không có tác dụng ở đây vì các hộp đánh dấu của chúng ta và phải là hình vuông.

Tôi đã có được hiệu ứng hình ảnh tương tự nhờ một phần tử giả và một lượng CSS phức tạp như vậy:

@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 một phần tử giả dạng vòng tròn khá đơn giản, nhưng việc đặt phần tử này phía sau phần tử được đính kèm lại khó hơn. Sau đây là trước và sau khi tôi khắc phục:

Đó chắc chắn là một tương tác nhỏ, nhưng điều quan trọng đối với tôi là giữ sự nhất quán về hình ảnh. Kỹ thuật điều chỉnh tỷ lệ ảnh động cũng giống như cách chúng tôi sử dụng ở những nơi khác. Chúng tôi đặt thuộc tính tuỳ chỉnh thành một giá trị mới và cho phép CSS chuyển đổi thuộc tính này dựa trên các lựa chọn ưu tiên về 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ả lập này đã nhấn vào không gian đó bằng cách đặt chính nó trở lại trong không gian z một chút.

Hỗ trợ tiếp cận

Video trên YouTube minh hoạ hiệu quả các hoạt động tương tác giữa 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 ra một số chi tiết ở đây.

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

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

Mỗi phần trong số 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ố phần tử kết nối khả năng tương tác và một số phần tử giúp định hình cây hỗ trợ tiếp cận mà trình đọc màn hình di chuyển.

Thuộc tính HTML

Chúng ta có thể ẩn các thành phần 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. Hãy 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. Lý do là chúng tôi đã ẩn biểu tượng có thể là đ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ẽ cần dừng lại, lắng nghe và di chuyển qua hình ảnh mà họ có thể không nhìn thấy được.

SVG là một loạt các phép toán, hãy thêm phần tử <title> cho tiêu đề di chuột miễn phí và một nhận xét mà con người có thể đọc được về những gì 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 đã đề cập đến cách quản lý màu nền đường đi qua JavaScript, vì vậy, hãy xem JavaScript liên quan đến <form> ngay bây giờ:

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

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

Mỗi khi biểu mẫu được tương tác và thay đổi, 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 cho máy chủ.

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

Kết luận

Giờ bạn đã biết tôi làm việc đó như thế nào, bạn sẽ làm thế nào?! Đ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 với các ô trong khung yêu thích của họ? 🙂

Hãy đa dạng hoá phương pháp tiếp cận của chúng ta và tìm hiểu tất cả các cách xây dựng trên web. Tạo một bản minh hoạ, đường liên kết tweet me 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 dưới đây!

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

  • @tomayac bằng kiểu liên quan đến khu vực di chuột cho các 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 thành phần: demosource.