Thanh trượt phạm vi nhất quán với thương hiệu

Sử dụng dữ liệu đầu vào phạm vi gốc

Sử dụng <input type="range"> có nhãn thay vì tạo lại thanh trượt bằng các phần tử <div> hoặc trình xử lý con trỏ tuỳ chỉnh. Thành phần điều khiển gốc giữ lại hoạt động tương tác bằng bàn phím và con trỏ, hành vi chạm và hỗ trợ tiếp cận. Định cấu hình min, max, step và value cho trường hợp sử dụng.

<label for="range-slider">Volume</label>
<input type="range" id="range-slider" value="70"/>

Nếu giá trị yêu cầu nhãn hoặc đơn vị được định dạng, hãy hiển thị giá trị đó trong nội dung DOM thông thường và cập nhật aria-valuetext khi thay đổi. Không sử dụng ::before hoặc ::after trên các phần tử giả của thanh trượt, vì nội dung được tạo trên các thành phần điều khiển biểu mẫu không có khả năng tương tác. Nếu cần có chú giải công cụ theo sau thanh trượt, hãy neo chú giải công cụ đó vào thanh trượt bằng tính năng định vị neo CSS; hãy xem phần Chú giải công cụ nhận biết vị trí để biết chú giải công cụ luôn giữ đúng hướng khi lật.

Chọn phương pháp tạo kiểu

Để tuỳ chỉnh màu sắc đơn giản, hãy sử dụng Biểu mẫu nhất quán với thương hiệu và giữ lại chế độ hiển thị gốc của trình duyệt:

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

accent-color không phải là một API tạo kiểu thanh trượt hoàn chỉnh. Hiệu ứng của thuộc tính này đối với thanh trượt, rãnh và màu nền phụ thuộc vào tác nhân người dùng. Sử dụng các phần tử giả bên dưới khi bạn phải kiểm soát rãnh, nút điều khiển, kích thước, hình dạng hoặc màu nền.

Tạo kiểu cho rãnh và nút trượt

Đặt lại kiểu mặc định của hệ điều hành bằng appearance: none, margin: 0 và border: 0. Để chuẩn hoá việc hiển thị trên nhiều trình duyệt, hãy khai báo các phần tử giả WebKit và Firefox trong các khối quy tắc riêng biệt, đồng thời căn giữa thanh trượt WebKit bằng margin-block-start: calc((var(--slider-track-size) - var(--slider-thumb-size)) / 2) (Firefox tự động căn giữa thanh trượt). Giữ các giá trị dành cho người tiêu dùng dưới dạng thuộc tính tuỳ chỉnh trên dữ liệu đầu vào để giao diện không nhắm trực tiếp đến các phần tử giả.

input[type="range"] {
  --slider-track-size: 0.5rem;
  --slider-thumb-size: 1.25rem;
  --slider-track-radius: 999px;
  --slider-thumb-radius: 50%;
  --slider-track-color: #d9d9d9;
  --slider-fill-color: #06c;
  --slider-thumb-color: var(--slider-fill-color);
  --slider-direction: to right;

  /* reverse gradient direction for right-to-left layouts */
  &:dir(rtl) {
    --slider-direction: to left;
  }

  /* Read min and max from attributes; --control-value is synced by the script below. */
  --attr-min: attr(min type(<number>), 0);
  --attr-max: attr(max type(<number>), 100);
  --control-value: attr(value type(<number>), 50);
  --slider-progress: calc(
    progress(var(--control-value), var(--attr-min), var(--attr-max)) * 100%
  );

  appearance: none;
  inline-size: 100%;
  block-size: 2.5rem; /* Preserve a generous interaction area. */
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  cursor: pointer;
}

/* WebKit/Blink. */
input[type="range"]::-webkit-slider-runnable-track {
  block-size: var(--slider-track-size);
  border: 0;
  border-radius: var(--slider-track-radius);
  background: linear-gradient(
    var(--slider-direction),
    var(--slider-fill-color) var(--slider-progress),
    var(--slider-track-color) var(--slider-progress)
  );
}

input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  inline-size: var(--slider-thumb-size);
  block-size: var(--slider-thumb-size);
  margin-block-start: calc(
    (var(--slider-track-size) - var(--slider-thumb-size)) / 2
  );
  border: 0;
  border-radius: var(--slider-thumb-radius);
  background: var(--slider-thumb-color);
}

/* Firefox. */
input[type="range"]::-moz-range-track {
  block-size: var(--slider-track-size);
  border: 0;
  border-radius: var(--slider-track-radius);
  background: var(--slider-track-color);
}

input[type="range"]::-moz-range-progress {
  block-size: var(--slider-track-size);
  border-radius: var(--slider-track-radius);
  background: var(--slider-fill-color);
}

input[type="range"]::-moz-range-thumb {
  inline-size: var(--slider-thumb-size);
  block-size: var(--slider-thumb-size);
  border: 0;
  border-radius: var(--slider-thumb-radius);
  background: var(--slider-thumb-color);
}

Luôn cung cấp chỉ báo tiêu điểm bàn phím hiển thị sau khi xoá giao diện gốc. Không chỉ sử dụng màu sắc:

input[type="range"]:focus-visible {
  outline: 2px solid var(--slider-fill-color);
  outline-offset: 4px;
}

Tạo kiểu cho tuyến đường đang hoạt động

Khi phần được lấp đầy của dải được tạo kiểu khác với dải chưa được lấp đầy, Firefox sẽ hiển thị phần đó thông qua ::-moz-range-progress. WebKit/Blink không cung cấp phần tử giả tương đương, vì vậy, hãy đồng bộ hoá giá trị đầu vào trực tiếp với --control-value trên các sự kiện input:

const slider = document.querySelector('#range-slider');

function syncSliderValue() {
  slider.style.setProperty('--control-value', slider.value);
}

syncSliderValue();
slider.addEventListener('input', syncSliderValue);

Các điểm hạn chế

  • Đừng kết hợp bộ chọn giả của WebKit và Firefox thành một danh sách được phân tách bằng dấu phẩy (ví dụ: ::-webkit-slider-thumb, ::-moz-range-thumb); trình duyệt sẽ bỏ toàn bộ quy tắc nếu gặp phải phần tử giả không nhận dạng được của nhà cung cấp.

Bản dự phòng

Dự phòng và hỗ trợ trình duyệt cho màu nhấn

Thuộc tính accent-color là cải tiến tăng dần. Những trình duyệt không hỗ trợ thuộc tính này sẽ bỏ qua thuộc tính này và sử dụng màu giao diện người dùng mặc định.

Phương án dự phòng và hỗ trợ trình duyệt cho progress() và attr()

Khi progress() không được hỗ trợ, hãy tính --slider-progress bằng calc():

@supports not (opacity: progress(1, 0, 100)) {
  input[type="range"] {
    --slider-progress: calc(
      (var(--control-value) - var(--attr-min)) /
      (var(--attr-max) - var(--attr-min)) * 100%
    );
  }
}

Khi attr() đã nhập không được hỗ trợ, hãy đồng bộ hoá --attr-min và --attr-max từ các thuộc tính min và max của dữ liệu đầu vào trong JavaScript:

function syncSliderValue() {
  slider.style.setProperty('--attr-min', slider.min || 0);
  slider.style.setProperty('--attr-max', slider.max || 100);
  slider.style.setProperty('--control-value', slider.value);
}