Создание компонента настроек

Базовый обзор того, как создать компонент настроек ползунков и флажков.

В этом посте я хочу поделиться мыслями о создании веб-компонента «Настройки», который реагирует на запросы, поддерживает ввод данных с нескольких устройств и работает в разных браузерах. Попробуйте демо .

Демо

Если вы предпочитаете видео или хотите просмотреть UI/UX того, что мы создаем, вот более короткое пошаговое руководство на YouTube:

Обзор

Я разбил аспекты этого компонента на следующие разделы:

  1. Макеты
  2. Цвет
  3. Пользовательский ввод диапазона
  4. Пользовательский ввод флажка
  5. Соображения доступности
  6. JavaScript

Макеты

Это первая демонстрация GUI Challenge, полностью состоящая из CSS Grid ! Вот каждая сетка, выделенная с помощью Chrome DevTools forgrid :

Красочные контуры и наложения промежутков, которые помогают показать все поля, составляющие макет настроек.

Просто ради пробела

Самая распространенная планировка:

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

Я называю этот макет «только для промежутков», потому что он использует сетку только для добавления промежутков между блоками.

Эту стратегию используют пять макетов, вот все из них:

Вертикальные макеты сетки выделены контурами и заполнены пробелами.

Элемент 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 Alignment , чтобы дочерние элементы располагались по центру по вертикали и горизонтали как в макете с одним, так и с двумя столбцами.

Посмотрите в приведенном выше видео, как «контент» остается по центру, даже если произошел перенос.

Повторить автоподбор минмакса

<form> использует адаптивную сетку для каждого раздела. Этот макет переключается с одного на два столбца в зависимости от доступного пространства.

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 использует 3 функции CSS: repeat() , minmax() и min() . У Уны Кравец есть отличный пост в блоге о макете , который она называет RAM .

В нашем макете есть 3 особых дополнения, если сравнивать его с Уной:

  • Мы передаем дополнительную функцию min() .
  • Мы указываем align-items: flex-start .
  • Есть стиль max-width: 89vw .

Дополнительная функция min() хорошо описана Эваном Минто в их блоге в статье «Внутренне адаптивная CSS-сетка с minmax() и 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 — это синтаксис, ориентированный на человека, который учитывает то, как мы воспринимаем цвет, а не то, как мы измеряем цвет с помощью математических вычислений (например, 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); . И в этом суть стратегии. Начните с простого изменения яркости между двумя темами, поддержания коэффициента контрастности, которого требует дизайн, или того, что может обеспечить доступность.

Бонус lch() здесь в том, что легкость ориентирована на человека, и мы можем чувствовать себя хорошо, если ее % изменить, что она будет восприниматься и последовательно % отличаться. hsl() например, не так надежен .

Если вам интересно, можно узнать больше о цветовых пространствах и lch() . Оно приближается!

CSS сейчас вообще не имеет доступа к этим цветам . Позвольте мне повторить: у нас нет доступа к трети цветов большинства современных мониторов. И это не просто любые цвета, а самые яркие цвета, которые только может отобразить экран . Наши веб-сайты размыты, потому что аппаратное обеспечение мониторов развивалось быстрее, чем спецификации CSS и реализации браузеров.

Леа Веру

Адаптивные элементы управления формой с цветовой схемой

Многие браузеры поставляют элементы управления темной темой, в настоящее время это Safari и Chromium, но вам необходимо указать в CSS или HTML, что они используются в вашем дизайне.

Вышеупомянутое демонстрирует эффект свойства на панели «Стили» DevTools. В демо-версии используется тег HTML, который, на мой взгляд, в целом является лучшим местом:

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

Узнайте все об этом из статьи Томаса Штайнера color-scheme . Вы можете получить гораздо больше, чем просто ввод темных флажков!

accent-color CSS

В последнее время наблюдалась активность по поводу accent-color в элементах формы, представляющего собой единый стиль CSS, который может изменять цвет оттенка, используемый во входном элементе браузера. Подробнее об этом читайте здесь, на GitHub . Я включил его в свои стили для этого компонента. Поскольку браузеры поддерживают это, мои флажки будут более тематическими с розовыми и фиолетовыми цветными пятнами.

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

Скриншот из Chromium в Linux с розовыми флажками

Яркие цвета с фиксированными градиентами и фокусом внутри

Цвет выделяется больше всего, когда он используется экономно, и один из способов добиться этого — с помощью красочного взаимодействия с пользовательским интерфейсом.

В приведенном выше видео есть много уровней обратной связи и взаимодействия пользовательского интерфейса, которые помогают придать индивидуальности взаимодействию за счет:

  • Выделение контекста.
  • Предоставление обратной связи пользовательского интерфейса о том, насколько полно значение находится в диапазоне.
  • Предоставление обратной связи пользовательского интерфейса о том, что поле принимает ввод.

Чтобы обеспечить обратную связь при взаимодействии с элементом, 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. Вложенный clip-path <picture> расширяется до полного круга, а фон заполняется ярким фиксированным градиентом.

Пользовательский диапазон

Учитывая следующий элемент ввода HTML, я покажу вам, как я настроил его внешний вид:

<input type="range">

У этого элемента есть три части, которые нам нужно настроить:

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

Первые несколько строк 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 , который демонстрирует решение только для 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">

У этого элемента есть три части, которые нам нужно настроить:

  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 , перенесите эти флажки в цветовую схему бренда.

Ярлыки флажков

Метки для флажков важно предоставлять по двум причинам. Первый — указать, для чего используется значение флажка, чтобы ответить «включено или выключено для чего?» Во-вторых, что касается 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) . Родитель создал трехмерное пространство, и этот дочерний псевдоэлемент подключился к нему, немного поместив себя обратно в 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(), где данные формы показаны в таблице.

Заключение

Теперь, когда вы знаете, как я это сделал, как бы вы поступили?! Это создает забавную компонентную архитектуру! Кто собирается сделать 1-ю версию со слотами на любимом фреймворке? 🙂

Давайте разнообразим наши подходы и изучим все способы разработки в Интернете. Создайте демо, пришлите мне ссылку в Твиттере , и я добавлю ее в раздел ремиксов сообщества ниже!

Ремиксы сообщества

  • @tomayac с их стилем области наведения меток флажков! В этой версии нет пробела при наведении курсора между элементами: demo и source .
,

Базовый обзор того, как создать компонент настроек ползунков и флажков.

В этом посте я хочу поделиться мыслями о создании веб-компонента «Настройки», который реагирует на запросы, поддерживает ввод данных с нескольких устройств и работает в разных браузерах. Попробуйте демо .

Демо

Если вы предпочитаете видео или хотите просмотреть UI/UX того, что мы создаем, вот более короткое пошаговое руководство на YouTube:

Обзор

Я разбил аспекты этого компонента на следующие разделы:

  1. Макеты
  2. Цвет
  3. Пользовательский ввод диапазона
  4. Пользовательский ввод флажка
  5. Соображения доступности
  6. JavaScript

Макеты

Это первая демонстрация GUI Challenge, полностью состоящая из CSS Grid ! Вот каждая сетка, выделенная с помощью Chrome DevTools forgrid :

Красочные контуры и наложения промежутков, которые помогают показать все поля, составляющие макет настроек.

Просто ради пробела

Самая распространенная планировка:

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

Я называю этот макет «только для промежутков», потому что он использует сетку только для добавления промежутков между блоками.

Эту стратегию используют пять макетов, вот все из них:

Вертикальные макеты сетки выделены контурами и заполнены пробелами.

Элемент 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 Alignment , чтобы дочерние элементы располагались по центру по вертикали и горизонтали как в макете с одним, так и с двумя столбцами.

Посмотрите в приведенном выше видео, как «контент» остается по центру, даже если произошел перенос.

Повторить автоподбор минмакса

<form> использует адаптивную сетку для каждого раздела. Этот макет переключается с одного на два столбца в зависимости от доступного пространства.

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 использует 3 функции CSS: repeat() , minmax() и min() . У Уны Кравец есть отличный пост в блоге о макете , который она называет RAM .

В нашем макете есть 3 особых дополнения, если сравнивать его с Уной:

  • Мы передаем дополнительную функцию min() .
  • Мы указываем align-items: flex-start .
  • Есть стиль max-width: 89vw .

Дополнительная функция min() хорошо описана Эваном Минто в их блоге в статье «Внутренне адаптивная CSS-сетка с minmax() и 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 — это синтаксис, ориентированный на человека, который учитывает то, как мы воспринимаем цвет, а не то, как мы измеряем цвет с помощью математических вычислений (например, 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); . И в этом суть стратегии. Начните с простого изменения яркости между двумя темами, поддержания коэффициента контрастности, которого требует дизайн, или того, что может обеспечить доступность.

Бонус lch() здесь в том, что легкость ориентирована на человека, и мы можем чувствовать себя хорошо, если ее % изменить, что она будет восприниматься и последовательно % отличаться. hsl() например, не так надежен .

Если вам интересно, можно узнать больше о цветовых пространствах и lch() . Оно приближается!

CSS сейчас вообще не имеет доступа к этим цветам . Позвольте мне повторить: у нас нет доступа к трети цветов большинства современных мониторов. И это не просто любые цвета, а самые яркие цвета, которые только может отобразить экран . Наши веб-сайты размыты, потому что аппаратное обеспечение мониторов развивалось быстрее, чем спецификации CSS и реализации браузеров.

Леа Веру

Адаптивные элементы управления формой с цветовой схемой

Многие браузеры поставляют элементы управления темной темой, в настоящее время это Safari и Chromium, но вам необходимо указать в CSS или HTML, что они используются в вашем дизайне.

Вышеупомянутое демонстрирует эффект свойства на панели «Стили» DevTools. В демо-версии используется тег HTML, который, на мой взгляд, в целом является лучшим местом:

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

Узнайте все об этом в статье Томаса Штайнера color-scheme . Вы можете получить гораздо больше, чем просто ввод темных флажков!

accent-color CSS

В последнее время наблюдалась активность по поводу accent-color в элементах формы, представляющего собой единый стиль CSS, который может изменять цвет оттенка, используемый во входном элементе браузера. Подробнее об этом читайте здесь, на GitHub . Я включил его в свои стили для этого компонента. Поскольку браузеры поддерживают это, мои флажки будут более тематическими с розовыми и фиолетовыми цветными пятнами.

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

Скриншот из Chromium в Linux с розовыми флажками

Яркие цвета с фиксированными градиентами и фокусом внутри

Цвет выделяется больше всего, когда он используется экономно, и один из способов добиться этого — с помощью красочного взаимодействия с пользовательским интерфейсом.

В приведенном выше видео есть много уровней обратной связи и взаимодействия пользовательского интерфейса, которые помогают придать индивидуальности взаимодействию за счет:

  • Выделение контекста.
  • Предоставление обратной связи пользовательского интерфейса о том, насколько полно значение находится в диапазоне.
  • Предоставление обратной связи пользовательского интерфейса о том, что поле принимает ввод.

Чтобы обеспечить обратную связь при взаимодействии с элементом, 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. Вложенный clip-path <picture> расширяется до полного круга, а фон заполняется ярким фиксированным градиентом.

Пользовательский диапазон

Учитывая следующий элемент ввода HTML, я покажу вам, как я настроил его внешний вид:

<input type="range">

У этого элемента есть три части, которые нам нужно настроить:

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

Первые несколько строк 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 , в котором демонстрируется решение, использующее только 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">

У этого элемента есть три части, которые нам нужно настроить:

  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 , перенесите эти флажки в цветовую схему бренда.

Ярлыки флажков

Метки для флажков важно предоставлять по двум причинам. Первый — указать, для чего используется значение флажка, чтобы ответить «включено или выключено для чего?» Во-вторых, что касается 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) . Родитель создал трехмерное пространство, и этот дочерний псевдоэлемент подключился к нему, немного поместив себя обратно в 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);
})

Каждый раз, когда форма взаимодействует и изменяется и изменяется, консоль регистрирует форму в качестве объекта в таблицу для легкого просмотра перед отправкой на сервер.

Снимок экрана консоли.table (), где данные формы показаны в таблице

Заключение

Теперь, когда вы знаете, как я это сделал, как бы вы?! Это делает для какой -то забавной архитектуры компонентов! Кто сделает 1 -ю версию с слотами в своих любимых рамках? 🙂

Давайте диверсифицируем наши подходы и узнаем все способы построения в Интернете. Создайте демонстрацию, напишите мне ссылки, и я добавлю ее в раздел ремиксов сообщества ниже!

Общественные ремиксы

  • @tomayac со своим стилем относительно области падения для меткеров флажок! Эта версия не имеет разрыва на колебании между элементами: демонстрация и источник .
,

Основополагающий обзор того, как создать компонент настройки ползунков и флажок.

В этом посте я хочу поделиться размышлением о создании компонента настроек для Интернета, который является адаптивным, поддерживает несколько входов устройств и работает в браузерах. Попробуйте демо .

Демо

Если вы предпочитаете видео или хотите, чтобы интерфейс пользовательского интерфейса/UX о том, что мы строим, вот более короткое прохождение на YouTube:

Обзор

Я разбил аспекты этого компонента на следующие разделы:

  1. Макеты
  2. Цвет
  3. Пользовательский вход диапазона
  4. Пользовательский флажок ввод
  5. Соображения доступности
  6. JavaScript

Макеты

Это первая демонстрация Gui Challenge, которая будет All CSS Grid ! Вот каждая сетка, выделенная Chrome Devtools для сетки :

Красочные очертания и накладывание расстояния между зазорами, которые помогают показывать все коробки, которые составляют макет настройки

Просто для разрыва

Наиболее распространенный макет:

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

Я называю этот макет «только для разрыва», потому что он использует только сетку, чтобы добавить пробелы между блоками.

Пять макетов используют эту стратегию, вот все они отображаются:

Вертикальные сетки, выделенные контурами и заполненными пробелами

Элемент 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 выравнивание сокращена , чтобы дети были центрированы по вертикали и горизонтально как в одном, так и в двух макетах столбцов.

Посмотрите в приведенном выше видео, как «контент» остается центрированным, хотя произошла упаковка.

Повторите автоматический Minmax

<form> использует адаптивную сетку для каждого раздела. Этот макет переключается от одного на два столбца на основе доступного пространства.

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 (--пространство-XL), чем column-gap (--Space-xxl), чтобы поместить это индивидуальное прикосновение к отзывчивому макету. Когда столбцы складываются, мы хотим большой разрыв, но не такой большой, как если бы мы на широком экране.

Свойство grid-template-columns использует 3 функции CSS: repeat() , minmax() и min() . У Una Kravets есть отличный пост в блоге макета об этом, называя его Ram .

В нашем макете есть 3 специальных дополнения, если вы сравниваете его с UNA:

  • Мы передаем функцию дополнительного min() .
  • Мы указываем align-items: flex-start .
  • Там есть стиль max-width: 89vw .

Функция дополнительного min() хорошо описана Эваном Минто в их блоге в посте, по сути, отзывчивой сетке CSS с MinMax () и Min () . Я рекомендую дать это чтение. Коррекция выравнивания flex-start заключается в том, чтобы удалить эффект растяжения по умолчанию, чтобы дети этого макета не должны иметь равных высот, они могут иметь естественную, внутреннюю высоту. Видео на YouTube быстро разбивает это дополнение.

max-width: 89vw стоит небольшой разбивки в этом посте. Позвольте мне показать вам макет со стилем и без применения:

Что происходит? Когда указана max-width , он предоставляет контекст, явные размеры или определенные размеры для алгоритма макета auto-fit чтобы узнать, сколько повторений он может вписаться в пространство. Хотя кажется очевидным, что пространство является «полной шириной», согласно спецификации сетки CSS, необходимо предоставить определенный размер или максимальный размер. Я предоставил максимальный размер.

Итак, почему 89vw ? Потому что «это сработало» для моего макета. Я и пара других хромированных людей исследуют, почему более разумная ценность, например, 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);
    }
  }
}

Сетка с центрированным контентом, умеренно мягкая по умолчанию (например, на мобильном телефоне). Но по мере того, как все больше места для просмотра становится доступным, он распространяется за счет увеличения прокладки. 2021 CSS выглядит довольно хорошо!

Помните более ранний макет «только для разрыва»? Вот более полная версия того, как они выглядят в этом компоненте:

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 Chroma и 0 Hue: очень темный бесцветный серый. Затем, в медиа -запросе для режима света, легкость переворачивается до 90% с --surface1: lch(90 0 0); . И это суть стратегии. Начните с того, что просто изменяет легкость между двумя темами, поддержав соотношение контрастности, которые требует дизайна или что может сохранить доступность.

Бонус с lch() здесь состоит в том, что легкость ориентирована на человека, и мы можем чувствовать себя хорошо в отношении того, что % изменится, что это будет восприниматься и последовательно, что % отличается. hsl() Например, не так надежно .

Есть еще кое -что, чтобы узнать о цветных пространствах и lch() если вам интересно. Оно приближается!

CSS прямо сейчас не может получить доступ к этим цветам вообще . Позвольте мне повторить: у нас нет доступа к одной трети цветов в большинстве современных мониторов. И это не только любые цвета, но и самые яркие цвета, которые может отображать экран . Наши веб -сайты вымыты, потому что оборудование для мониторинга развивалось быстрее, чем спецификации CSS и реализации браузеров.

Леа Веру

Адаптивные элементы управления формой с цветовой схемой

Многие браузеры отправляют темные элементы управления темами, в настоящее время Safari и Chromium, но вы должны указать в CSS или HTML, что ваш дизайн использует их.

Вышеуказанное демонстрирует влияние свойства с панели стилей Devtools. Демо использует тег HTML, который, по моему мнению, обычно является лучшим местоположением:

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

Узнайте все об этом в этой статье color-scheme Томаса Штайнера . Есть гораздо больше, чем входные флажки, чем входные флажки!

CSS accent-color

Недавние активности в области accent-color на элементах формы, будучи одним стилем CSS, который может изменить цвет оттенок, используемый в входном элементе браузеров. Узнайте больше об этом здесь, на GitHub . Я включил его в свои стили для этого компонента. Поскольку браузеры поддерживают его, мои флажки будут больше на теме с розовым и фиолетовым цветом.

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

Скриншот от Chromium on Linux из розовых флажок

Цвет всплывает с фиксированными градиентами и фокусировкой

Цвет появляется больше всего, когда он используется экономно, и одним из способов достижения этого является то, что мне это нравится через красочные взаимодействия пользовательского интерфейса.

В приведенном выше видео есть много слоев обратной связи и взаимодействия пользовательского интерфейса, которые помогают дать личность взаимодействию:

  • Выделение контекста.
  • Предоставление обратной связи пользовательского интерфейса «насколько полно» значение в диапазоне.
  • Предоставление обратной связи пользовательского интерфейса, что поле принимает вход.

Чтобы обеспечить обратную связь, когда элемент взаимодействует, CSS использует класс :focus-within Pseudo, чтобы изменить внешний вид различных элементов, давайте .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 */
}

Первые несколько строк 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 используется для выделения Hover:

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

Целью была простая в управлении и анимированной визуальной мощности для отзывов пользователей. Используя тень коробки, я могу избежать запуска макета с эффектом. Я делаю это, создавая тень, которая не размыта и соответствует круговой форме элемента большого пальца. Затем я меняю и переходите, это размер распределения на пахни.

Если бы только эффект выделения был таким простым на флажках ...

Селекторы браузера Cross

Я обнаружил, что мне нужны эти -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 причинам. Во -первых, это представлять, для чего используется значение флажки, чтобы ответить "включен или выключен для чего?" Во -вторых, для 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 , который указывает на флажок по ID: <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) . Родитель создал трехмерное пространство, и этот псевдоэлементный ребенок врезался в него, немного поместившись назад в 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> javaScript:

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

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

Каждый раз, когда форма взаимодействует и изменяется и изменяется, консоль регистрирует форму в качестве объекта в таблицу для легкого просмотра перед отправкой на сервер.

Снимок экрана консоли.table (), где данные формы показаны в таблице

Заключение

Теперь, когда вы знаете, как я это сделал, как бы вы?! Это делает для какой -то забавной архитектуры компонентов! Кто сделает 1 -ю версию с слотами в своих любимых рамках? 🙂

Давайте диверсифицируем наши подходы и узнаем все способы построения в Интернете. Создайте демонстрацию, напишите мне ссылки, и я добавлю ее в раздел ремиксов сообщества ниже!

Общественные ремиксы

  • @tomayac со своим стилем относительно области падения для меткеров флажок! Эта версия не имеет разрыва на колебании между элементами: демонстрация и источник .
,

Основополагающий обзор того, как создать компонент настройки ползунков и флажок.

В этом посте я хочу поделиться размышлением о создании компонента настроек для Интернета, который является адаптивным, поддерживает несколько входов устройств и работает в браузерах. Попробуйте демо .

Демо

Если вы предпочитаете видео или хотите, чтобы интерфейс пользовательского интерфейса/UX о том, что мы строим, вот более короткое прохождение на YouTube:

Обзор

Я разбил аспекты этого компонента на следующие разделы:

  1. Макеты
  2. Цвет
  3. Пользовательский вход диапазона
  4. Пользовательский флажок ввод
  5. Соображения доступности
  6. JavaScript

Макеты

Это первая демонстрация Gui Challenge, которая будет All CSS Grid ! Вот каждая сетка, выделенная Chrome Devtools для сетки :

Красочные очертания и накладывание расстояния между зазорами, которые помогают показывать все коробки, которые составляют макет настройки

Просто для разрыва

Наиболее распространенный макет:

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

Я называю этот макет «только для разрыва», потому что он использует только сетку, чтобы добавить пробелы между блоками.

Пять макетов используют эту стратегию, вот все они отображаются:

Вертикальные сетки, выделенные контурами и заполненными пробелами

Элемент 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 выравнивание сокращена , чтобы дети были центрированы по вертикали и горизонтально как в одном, так и в двух макетах столбцов.

Посмотрите в приведенном выше видео, как «контент» остается центрированным, хотя произошла упаковка.

Повторите автоматический Minmax

<form> использует адаптивную сетку для каждого раздела. Этот макет переключается от одного на два столбца на основе доступного пространства.

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 (--пространство-XL), чем column-gap (--Space-xxl), чтобы поместить это индивидуальное прикосновение к отзывчивому макету. Когда столбцы складываются, мы хотим большой разрыв, но не такой большой, как если бы мы на широком экране.

Свойство grid-template-columns использует 3 функции CSS: repeat() , minmax() и min() . У Una Kravets есть отличный пост в блоге макета об этом, называя его Ram .

В нашем макете есть 3 специальных дополнения, если вы сравниваете его с UNA:

  • Мы передаем функцию дополнительного min() .
  • Мы указываем align-items: flex-start .
  • Там есть стиль max-width: 89vw .

Функция дополнительного min() хорошо описана Эваном Минто в их блоге в посте, по сути, отзывчивой сетке CSS с MinMax () и Min () . Я рекомендую дать это чтение. Коррекция выравнивания flex-start заключается в том, чтобы удалить эффект растяжения по умолчанию, чтобы дети этого макета не должны иметь равных высот, они могут иметь естественную, внутреннюю высоту. Видео на YouTube быстро разбивает это дополнение.

max-width: 89vw стоит небольшой разбивки в этом посте. Позвольте мне показать вам макет со стилем и без применения:

Что происходит? Когда указана max-width , он предоставляет контекст, явные размеры или определенные размеры для алгоритма макета auto-fit чтобы узнать, сколько повторений он может вписаться в пространство. Хотя кажется очевидным, что пространство является «полной шириной», согласно спецификации сетки CSS, необходимо предоставить определенный размер или максимальный размер. Я предоставил максимальный размер.

Итак, почему 89vw ? Потому что «это сработало» для моего макета. Я и пара других хромированных людей исследуют, почему более разумная ценность, например, 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);
    }
  }
}

Сетка с центрированным контентом, умеренно мягкая по умолчанию (например, на мобильном телефоне). Но по мере того, как все больше места для просмотра становится доступным, он распространяется за счет увеличения прокладки. 2021 CSS выглядит довольно хорошо!

Помните более ранний макет «только для разрыва»? Вот более полная версия того, как они выглядят в этом компоненте:

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 Chroma и 0 Hue: очень темный бесцветный серый. Затем, в медиа -запросе для режима света, легкость переворачивается до 90% с --surface1: lch(90 0 0); . И это суть стратегии. Начните с того, что просто изменяет легкость между двумя темами, поддержав соотношение контрастности, которые требует дизайна или что может сохранить доступность.

Бонус с lch() здесь состоит в том, что легкость ориентирована на человека, и мы можем чувствовать себя хорошо в отношении того, что % изменится, что это будет восприниматься и последовательно, что % отличается. hsl() Например, не так надежно .

Есть еще кое -что, чтобы узнать о цветных пространствах и lch() если вам интересно. Оно приближается!

CSS прямо сейчас не может получить доступ к этим цветам вообще . Позвольте мне повторить: у нас нет доступа к одной трети цветов в большинстве современных мониторов. И это не только любые цвета, но и самые яркие цвета, которые может отображать экран . Наши веб -сайты вымыты, потому что оборудование для мониторинга развивалось быстрее, чем спецификации CSS и реализации браузеров.

Леа Веру

Адаптивные элементы управления формой с цветовой схемой

Многие браузеры отправляют темные элементы управления темами, в настоящее время Safari и Chromium, но вы должны указать в CSS или HTML, что ваш дизайн использует их.

Вышеуказанное демонстрирует влияние свойства с панели стилей Devtools. Демо использует тег HTML, который, по моему мнению, обычно является лучшим местоположением:

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

Узнайте все об этом в этой статье color-scheme Томаса Штайнера . Есть гораздо больше, чем входные флажки, чем входные флажки!

CSS accent-color

Недавние активности в области accent-color на элементах формы, будучи одним стилем CSS, который может изменить цвет оттенок, используемый в входном элементе браузеров. Узнайте больше об этом здесь, на GitHub . Я включил его в свои стили для этого компонента. Поскольку браузеры поддерживают его, мои флажки будут больше на теме с розовым и фиолетовым цветом.

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

Скриншот от Chromium on Linux из розовых флажок

Цвет всплывает с фиксированными градиентами и фокусировкой

Цвет появляется больше всего, когда он используется экономно, и одним из способов достижения этого является то, что мне это нравится через красочные взаимодействия пользовательского интерфейса.

В приведенном выше видео есть много слоев обратной связи и взаимодействия пользовательского интерфейса, которые помогают дать личность взаимодействию:

  • Выделение контекста.
  • Предоставление обратной связи пользовательского интерфейса «насколько полно» значение в диапазоне.
  • Предоставление обратной связи пользовательского интерфейса, что поле принимает вход.

Чтобы обеспечить обратную связь, когда элемент взаимодействует, CSS использует класс :focus-within Pseudo, чтобы изменить внешний вид различных элементов, давайте .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 */
}

Первые несколько строк 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 используется для выделения Hover:

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

Целью была простая в управлении и анимированной визуальной мощности для отзывов пользователей. Используя тень коробки, я могу избежать запуска макета с эффектом. Я делаю это, создавая тень, которая не размыта и соответствует круговой форме элемента большого пальца. Затем я меняю и переходите, это размер распределения на пахни.

Если бы только эффект выделения был таким простым на флажках ...

Селекторы браузера Cross

Я обнаружил, что мне нужны эти -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 причинам. Во -первых, это представлять, для чего используется значение флажки, чтобы ответить "включен или выключен для чего?" Во -вторых, для 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 , который указывает на флажок по ID: <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) . Родитель создал трехмерное пространство, и этот псевдоэлементный ребенок врезался в него, немного поместившись назад в z-пространство.

Доступность

Видео на YouTube отлично демонстрирует взаимодействие мыши, клавиатуры и экрана для этого компонента настройки. Я вызову здесь некоторые детали.

Выбор элементов HTML

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

Каждый из этих подсказок и подсказки для инструмента просмотра пользователя. Некоторые элементы предоставляют подсказки взаимодействия, некоторые соединяют интерактивность, а некоторые помогают сформировать дерево доступности, которое движется экраном.

HTML-атрибуты

Мы можем скрыть элементы, которые не нужны для читателей экрана, в этом случае значок рядом со слайдером:

<picture aria-hidden="true">

Вышеприведенное видео демонстрирует поток для чтения экрана на Mac OS. Обратите внимание, как фокус ввода перемещается прямо от одного ползунка к другому. Это потому, что мы спрятали значок, который, возможно, был остановкой на пути к следующему слайдеру. Without this attribute, a user would need to stop, listen and move past the picture which they may not be able to see.

The SVG is a bunch of math, let's add a <title> element for a free mouse hover title and a human readable comment about what the math is creating:

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

Other than that, we've used enough clearly marked HTML, that the form tests really well across mouse, keyboard, video game controllers and screenreaders.

JavaScript

I've already covered how the track fill color was being managed from JavaScript, so let's look at the <form> related JavaScript now:

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

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

Everytime the form is interacted with and changed, the console logs the form as an object into a table for easy review before submitting to a server.

A screenshot of the console.table() results, where the form data is shown in a table

Заключение

Now that you know how I did it, how would you?! This makes for some fun component architecture! Who's going to make the 1st version with slots in their favorite framework? 🙂

Let's diversify our approaches and learn all the ways to build on the web. Create a demo, tweet me links, and I'll add it to the Community remixes section below!

Community remixes

  • @tomayac with their style regarding the hover area for the checkbox labels! This version has no hover gap between elements: demo and source .