:checked ⚡️ HTML и CSS с примерами кода

Псевдокласс :checked находит любые элементы radio (<input type="radio">), checkbox (<input type="checkbox">) или option (<option> внутри <select>), которые выбраны или включены.

Пользователь может изменить это состояние, нажав на элемент, или выбрав другое значение, в этом случае :checked повторно не применится к элементу, а сохранится.

Псевдоклассы
  • :active
  • :any-link
  • :blank
  • :checked
  • :current()
  • :default
  • :defined
  • :dir()
  • :disabled
  • :empty
  • :enabled
  • :first
  • :first-child
  • :first-of-type
  • :focus
  • :focus-visible
  • :focus-within
  • :fullscreen
  • :future
  • :has()
  • :host
  • :host()
  • :host-context()
  • :hover
  • :indeterminate
  • :in-range
  • :invalid
  • :is()
  • :lang()
  • :last-child
  • :last-of-type
  • :left
  • :link
  • :local-link
  • :not()
  • :nth-child()
  • :nth-col()
  • :nth-last-child()
  • :nth-last-col()
  • :nth-last-of-type()
  • :nth-of-type()
  • :only-child
  • :only-of-type
  • :optional
  • :out-of-range
  • :past
  • :placeholder-shown
  • :read-only
  • :read-write
  • :required
  • :right
  • :root
  • :scope
  • :target
  • :target-within
  • :user-invalid
  • :valid
  • :visited
  • :where()

Синтаксис

/* Matches any checked/selected radio, checkbox, or option */
:checked {
  margin-left: 25px;
  border: 1px solid blue;
}

Спецификации

  • HTML Living Standard
  • HTML 5
  • Selectors Level 4
  • CSS Basic User Interface Module Level 3
  • Selectors Level 3

Описание и примеры

Пример 1

HTMLCSS

<div>
  <input type="radio" name="my-input" />
  <label for="yes">Yes</label>
  <input type="radio" name="my-input" />
  <label for="yes">No</label>
</div>
<div>
  <input type="checkbox" name="my-checkbox" />
  <label for="opt-in">Check me!</label>
</div>
<select name="my-select">
  <option value="opt1">Apples</option>
  <option value="opt2">Grapes</option>
  <option value="opt3">Pears</option>
</select>
div,
select {
  margin: 8px;
}
/* Labels for checked inputs */
input:checked + label {
  color: red;
}
/* Radio element, when checked */
input[type='radio']:checked {
  box-shadow: 0 0 0 3px orange;
}
/* Checkbox element, when checked */
input[type='checkbox']:checked {
  box-shadow: 0 0 0 3px hotpink;
}
/* Option elements, when selected */
option:checked {
  box-shadow: 0 0 0 3px lime;
  color: red;
}

Пример 2: переключение элементов со скрытым флажком

В этом примере используется псевдо-класс :checked, чтобы пользователь мог переключать контент на основе состояния флажка, без использования JavaScript.

HTMLCSS

<input type="checkbox" />
<table>
  <thead>
    <tr>
      <th>Column #1</th>
      <th>Column #2</th>
      <th>Column #3</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>[more text]</td>
      <td>[more text]</td>
      <td>[more text]</td>
    </tr>
    <tr>
      <td>[cell text]</td>
      <td>[cell text]</td>
      <td>[cell text]</td>
    </tr>
    <tr>
      <td>[cell text]</td>
      <td>[cell text]</td>
      <td>[cell text]</td>
    </tr>
    <tr>
      <td>[more text]</td>
      <td>[more text]</td>
      <td>[more text]</td>
    </tr>
    <tr>
      <td>[more text]</td>
      <td>[more text]</td>
      <td>[more text]</td>
    </tr>
  </tbody>
</table>
<label for="expand-toggle">Toggle hidden rows</label>
/* Hide the toggle checkbox */
#expand-toggle {
  display: none;
}
/* Hide expandable content by default */
.
expandable { visibility: collapse; background: #ddd; } /* Style the button */ #expand-btn { display: inline-block; margin-top: 12px; padding: 5px 11px; background-color: #ff7; border: 1px solid; border-radius: 3px; } /* Show hidden content when the checkbox is checked */ #expand-toggle:checked ~ * .expandable { visibility: visible; } /* Style the button when the checkbox is checked */ #expand-toggle:checked ~ #expand-btn { background-color: #ccc; }

Еще раз о визуализации input типа checkbox и radio. Для тех, кто забыл как / Хабр

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

Недавно у меня была короткая работа по разработке ТЗ на модернизацию давно существующего проекта. И, в частности дело касалось стилизации пресловутых <input type=»checkbox»>. Выяснилось, что исполнитель, программист «на все руки» даже не понял, что я ему на словах объяснял как это сделать. Пришлось делать примеры и, как результат, появился этот текст.

Напомню, что сейчас checkbox и radiobox разные сайты изображают по-разному. Бывает, что не отмеченный input сразу и не разглядишь – такой он «дизайнерский красивый», а у последних версий Chrome выбранные checkbox стали гнусного цвета циан.

Итак, ситуация

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

Маркетологи и рекламщики тоже его используют. Что эта система делает – неважно, на чем написано – неважно.

А важно, что на сайте этого продукта есть много страниц с формами, на которых много input checkbox и radio.

Жалобы сотрудников

Директор: На большом экране плохо видно и незаметны «крыжики».
Главбух: На моем компе «крыжики» выглядят так, у сотрудниц иначе, дома тоже не так, а на планшете совсем иначе.
Маркетолог: А можно, так что бы некоторые не выбранные позиции были красными, а другие выбранные были зелеными?
И т. д., и т.п.

Итак, задача


  1. Минимальными затратами и минимальными изменениями исправить внешний вид checkbox и radiobox.
  2. Сделать стилизацию checkbox и radiobox для разных юзеров. Важно: это закрытый сайт, там «всё свои», «красоты» не нужны, а нужна эффективность восприятия.

Что нельзя

1. Серверную часть трогать нельзя.
2. Файлы javascript трогать нельзя, свой javascript вставлять нельзя.
3. Файлы css трогать нельзя.

А что можно

1. Править html шаблоны.
2. Создать файл стилей для всех юзеров.
4. Создать файл стилей для конкретного юзера или группы юзеров.

А что сделали можно сразу посмотреть на codepen.io, но лучше почитать дальше.

Предварительное изучение показало

1. Почти все <input type=»checkbox»> имеют поле name, а которые не имеют, то у них есть id.
2. Все <input type=»radio»> имеют поле name, некоторые имеют id.
3. Соответственно, в css к checkbox можно обращаться как по id, так и по name. К radio – или по id, или по номеру потомка у родителя.

Фрагменты исходного кода:

/* вариант 1 */
<tag><input type="checkbox"> Некий текст</tag>
/* вариант 2 */
<tag><input type="checkbox"> Некий текст<br>
<input type="checkbox"> Некий текст</tag>
/* вариант 3 */
...<label><input type="checkbox"> Некий текст</label>...
/* вариант 4 */
<td><input type="checkbox"></td>
<td><label for="idxxx">Некий текст</label></td>

Так исправим код:

/* вариант 1 */
<tag><label><input type="checkbox"><s></s><span>Некий текст</span></label></tag>
/* вариант 2 */
<tag><label><input type="checkbox"><s></s><span>Некий текст</span></label><br>.
..</tag> /* вариант 3 */ ...<label><input type="checkbox"><s></s><span>Некий текст</span></label>... /* вариант 4 */ <td><label><input type="checkbox"><s></s></label></td> <td><label for="idxxx">Некий текст</label></td>

Всё тоже самое и для <input type=»radio»>, класс у LABEL тот же.

Что конкретно сделали?


  1. Каждый input (корме варианта 3) обернули тэгом LABEL с нашим классом. Варианту 3 просто добавили класс.
  2. Сразу после input вставили пустой тэг S. Так как сам input будет не видим, то это тэг будет визуализировать это input.
  3. Сопроводительный текст обернули тэгом SPAN (кроме варианта 4). Этот тэг понадобиться, когда будем решать вопрос выравнивания визуального input относительно этого текста.
  4. Варианту 4 добавили еще класс, что бы не осуществлять это выравнивание, раз сопроводительный текст стоит в другой ячейки таблицы. Строго говоря, надо было бы сделать на оборот – вариантам 1-3 добавить класс, отвечающий за выравнивание. Но, вариантов 1-3 гораздо больше, чем 4-го и что бы не раздувать html сделано так.

Риторические вопросы и риторические же ответы

1. Зачем тэг S? Ну, не нравится S – можно использовать любой другой строчный элемент. Просто он должен быть, его можно стилизовать в зависимости от состояния предшествующего input.

2. Почему тэги S и SPAN без классов? Ну, зачем раздувать html? Тем более, что не очевидно, что одна из конструкций ниже будет «работать» медленнее другой.

  .new-input > S { }
  .new-input > .new-input-S {}

3. Как вы догадались, мне не нравятся идеи БЭМ, тем более идея «раздувать» html файл обилием упоминаний разных классов. В реальном проекте мы использовали только два класса – mni и mnio. :-))

Некоторые предварительные рассуждения и настройки css касательно box-sizing:border-box, нормализации LABEL, селекторов «A + B», «A ~ B» и «[attr]», псевдоклассов :checked, :disabled и ::before. Кто не уверен, что знает или хочет освежить знания смотрит под катом.

Предварительные рассуждения

1. Напомню, что в «старом» css (box-sizing:content-box) свойства width и height задают только ширину и высоту содержимого, а padding и border добавляются к этим значениям. box-sizing:border-box меняет схему так, что padding и border включаются в width и height.

2. Проверка показала, что в нашем случае используется старая модель, а менять «настройки» страниц запрещено. Не «наши» LABEL это простые строчные элементы, в них только текст. Поэтому стилизуем ВСЕ LABEL.

LABEL {
    box-sizing:border-box; cursor:pointer; user-select:none;
}
LABEL *,
LABEL *::before,
LABEL *::after {
    box-sizing:inherit;
}

Т.е., ставим box-sizing:border-box для тэга LABEL, всем его потомкам. Заодно ставим курсор и запрещаем выделение текст (что бы не мешало клику).

3. Комбинация селекторов «A + B» означает, что стили будут применяться только к селектору B, если он следует сразу ПОСЛЕ селектора A, т. е. только для первого B. С другой стороны, «A ~ B» означает, что ко всем селекторам B после селектора A, т.е. для первого и последующих.
Естественно, всё в пределах одного «родителя».

Как это будем использовать?

<label><input type="checkbox"><s></s><span>Некий текст</span></label>
<label><input type="radio"><s></s><span>Некий текст</span></label>
/* 1 */
.new-input > INPUT + S {}
.new-input > INPUT ~ SPAN {}
/* 2 */
.new-input > INPUT:not(:checked) + S {}
.new-input > INPUT:not(:checked) ~ SPAN {}
/* 3 */
.new-input > INPUT:checked + S {}
.new-input > INPUT:checked ~ SPAN {}
/* 4 */
.new-input > INPUT:disabled + S {}
.new-input > INPUT:disabled ~ SPAN {}
/* 5 */
.new-input > INPUT[type="radio"] + S {}

Первая группа – общие стили для тэгов S и SPAN.
Вторая группа – стили только когда INPUT НЕ выбран.
Третья – стили только когда INPUT выбран.
Четвертая – когда INPUT заблокирован.

И, наконец, пятая группа – общие стили для тэга S ТОЛЬКО, если он стоит после input radio.
Таким образом, можно изменять стили тэгов S и SPAN в зависимости от состояния input.

4. Поскольку у нас тэг S будет изображать из себя input, то самому input поставим display:none, его не будет видно, а тэг LABEL будет его переключать, а тэг S будет соответственно меняться. Почему не используем html свойство hidden у input? Потому, что на некоторых браузерах hidden у input «работает» не совсем верно, плюс не будем перегружать html файл.


Итак, начинаем визуализацию input

Пример N 1. Самый простой – используем алфавитные символы
html код тот же, а css будет такой:

/* s1 */
.new-input > INPUT + S::before {
  content: "c";
}
/* s2 */
.new-input > INPUT:checked + S::before {
  content: "V";
}
/* s3 */
. new-input > INPUT[type="radio"] + S::before {
  content: "r";
}
/* s4 */
.new-input > INPUT[type="radio"]:checked + S::before {
  content: "X";
}
/* s5 */
.new-input > INPUT:disabled + S::before {
  opacity: 0.5;
}
/* s6 */
.new-input > S {
  text-decoration: none;
  margin-left: 3px;
  margin-right: 6px;
}
/* s7 */
.new-input > S::before {
  display: inline-block;
  width: 1.25em;
  text-align: center;
  color: #fafafa;
  background-color: #37474f;
}
/* s8 */
.new-input > INPUT[type="radio"] + S::before {
  border-radius: 50%;
}

Тэг S буде визуализировать input. Но мы «разделим» его по функционалу: сам тэг S будет отвечать за размещение в LABEL и выравнивание относительно следующего SPAN.

А псевдоэлемент S::before разместится внутри тэга S и будет изображать из себя input.

Строка s1 определяет, какой символ будет помещен в S::before когда input не выбран. В принципе надо было бы написать «. new-input > INPUT:not(:checked) + S::before», но некоторые браузеры (например, IE), подобную конструкцию могут и не исполнить.
Строка s2 определяет символ, когда input выбран.
Строки s3 и s4 делают то же для input radio.
Строка s5 описывает, что будет если input заблокирован – в данном случае тэг S будет наполовину прозрачным.
Строка s6 определяет выравнивание, в данном случае дает отбивку слева и справа (только в этом примере). Плюс, убирает штатное перечеркивание.
Строка s7 делает квадратик, s8 превращает его в кружок для input radio.

Пример N 1 можно посмотреть на codepen.io. Там представлены нативные input и новые. Первые можно убрать.

Чуток подробнее про display: inline-block, font-size, line-height

Конченая высота строки текста определяется на основе заданных font-size, line-height. При единичном line-height – высота будет по font-size, при числовом line-height – высота будет по их произведению или, при указании единиц измерения для line-height – высоту определит максимальное значение. В примере указан line-height:1.25, поэтому и у S::before указано width:1.25em.

Для S::before указано display: inline-block – в этом случае S::before «внутри» себя будет блоком (можно указать ширину, высоту, рамки и пр.), а «снаружи» он останется строчным элементом. В дальнейшем об этом будет подробнее.

Вопрос:

Может можно использовать специальные символы? Типа вот этих:
□ ■ ▢ ▣ ○ ● ◎◉
Задать им нужный размер и всё. Нет?

Ответ:

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

Пример N 2. «Рисуем» элементы input средствами css

html код тот же, а css будет такой:

/* s1 */
.new-input > S::before {
  content: "";
  display: inline-block;
  width: 0.75em;
  height: 0. 75em;
  border: 1px solid currentColor;
  padding: 2px;
  background-clip: content-box;
  border-radius: 20%;
}
/* s2 */
.new-input > INPUT[type="radio"] + S::before {
  border-radius: 50%;
}
/* s3 */
.new-input > INPUT:checked + S::before {
  background-color: currentColor;
}
/* s4 */
.new-input > INPUT:disabled + S::before {
  opacity: 0.5;
}
/* s5 */
.new-input > S {
  text-decoration: none;
  margin-left: 3px;
  margin-right: 6px;
}

Строка s1 определяет S::before для визуализации input. Это будет inline-block, ширина и высота которого установлена в 0.75em, что примерно равно высоте прописной буквы и зависит от font-size родителя. Задана тонкая рамка текущим цветом, внутренняя отбивка, небольшое скругление углов. И – самое важное! – установлено свойство background-clip:content-box. Это очень интересное свойство – если будет установлен background-color, то он закрасит только контентную часть и не затронет отбивку (padding). Что нам и надо.

Строка s2 для input типа radio делает S::before круглым.
Строка s3 для отмеченного input устанавливает для S::before background-color текущим цветом. Т.е., «рисует» внутри квадратик или кружок.
Строка s4 отрабатывает блокировку input, строка s5 дает отбивки слева и справа.

Преимущества этого метода

  1. Всё очень просто. Работает на всех браузерах. Даже у IE10 (в эмуляции у 11-го).
  2. Можно раскрашивать по своему усмотрению.
  3. Раз S::before это inline-block, то он сидит на попе базовой линии ровно и никуда с нее не слезает. Если он по высоте будет больше текста, то просто увеличит высоту строки и останется на базовой линии.
  4. Раз визуализация input находится внутри тэга S, то его можно легко позиционировать и выравнивать.
  5. Размеры S::before в em дают возможность задавать его размер относительно размера текста подписи. Можно, к примеру, поставить предельные значения высоты и ширины.

Недостатки этого метода

В основном в использовании размеров в em. Дело в том, что может возникнуть ситуация когда ширина и высота при расчете (из em в px) будет иметь дробное значение. На обычных компьютерах с обычным экраном округление может произойти не корректно. Например, размеры 12.8px на 12.8px у той же Мозилы могут стать как 13px на 12px. Тогда надо ставить фиксированные размеры. Хотя на современных мониторах и видеокартах, ноутбуках, на планшетах и смартфонах этого не происходит из-за того, что точка (пиксель) браузера состоит из нескольких пикселей экрана.

Пример N 2 можно посмотреть на codepen.io. Там представлены нативные input и новые. Первые можно убрать.
Итак, первую задачу – визуализацию input – выполнили. Переходим к избранной «раскраске».

Раскрашиваем input

html для примера:

<label><input name="chb1" type="checkbox" .. .><s></s><span>Некий текст</span></label>
<label><input type="radio" ...><s></s><span>Некий текст</span></label>

К input типа checkbox будем обращаться по name, к radio по id.

Всё красим в синий

/* только input */
.new-input > INPUT[name="chb1"] + S,
.new-input > INPUT#rb1 + S {
  color: #0091ea;
}
/* только text */
.new-input > INPUT[name="chb1"] ~ SPAN,
.new-input > INPUT#rb1 ~ SPAN {
  color: #0091ea;
}
/* или всё */
.new-input > INPUT[name="chb1"] ~ *,
.new-input > INPUT#rb1 ~ * {
  color: #0091ea;
}

Помним о специфичности в css, эти стили будут более специфичны, чем базовые и сработают обязательно. Чем они отличаются от описанных выше? Тем, что применяются только к избранным input – к тем, что имеет указанное значение name и id.

Тут всё хорошо кроме того, что не выбранные input будут не очень хорошо глядеться – тонкая синяя рамка мало заметна.

Красим в зеленый, когда input выбран

/* только input */
.new-input > INPUT[name="chb1"]:checked + S,
.new-input > INPUT#rb1:checked + S {
  color: #00c853;
}
/* только text */
.new-input > INPUT[name="chb1"]:checked ~ SPAN,
.new-input > INPUT#rb1:checked ~ SPAN {
  color: #00c853;
}
/* или всё */
.new-input > INPUT[name="chb1"]:checked ~ *,
.new-input > INPUT#rb1:checked ~ * {
  color: #00c853;
}

Первый вариант, на мой взгляд, не очень хорош – зеленым будут и рамка, и внутренний квадратик/кружок. Можно раскрасить только его.

/* только input и только внутри */
.new-input > INPUT[name="chb1"]:checked + S::before,
.new-input > INPUT#rb1:checked + S::before {
  background-color: #00c853;
}

Красим в красный, когда input НЕ выбран

/* только input */
.new-input > INPUT[name="chb1"]:not(:checked) + S,
.new-input > INPUT#rb1:not(:checked) + S {
  color: #d50000;
}
/* только text */
. new-input > INPUT[name="chb1"]:not(:checked) ~ SPAN,
.new-input > INPUT#rb1:not(:checked) ~ SPAN {
  color: #d50000;
}
/* или всё */
.new-input > INPUT[name="chb1"]:not(:checked) ~ *,
.new-input > INPUT#rb1:not(:checked) ~ * {
  color: #d50000;
}

Логика понятна? Можно и дальше делать более сложные конструкции.

Например, при не выбранном input текст должен быть красным и жирным, а при выбранном внутренний элемент input и текст должен быть зеленым. Элементарно!

/* текст, когда нет выбора */
.new-input > INPUT[name="chb1"]:not(:checked) ~ SPAN,
.new-input > INPUT#rb1:not(:checked) ~ SPAN {
  color: #d50000;
  font-weight: bold;
}
/* внутренний элемент input, когда выбран */ 
.new-input > INPUT[name="chb1"]:checked + S::before,
.new-input > INPUT#rb1:checked + S::before {
  background-color: #00c853;
}
/* текст, когда выбран */ 
.new-input > INPUT[name="chb1"]:checked ~ SPAN,
.new-input > INPUT#rb1:checked ~ SPAN {
  color: #00c853;
}

А, к примеру, надо обработать целую группу input (10-15 штук). Что бы не писать кучу строк можно найти их общего родителя (.parent_element) и сократить условие.

.parent_element > .new-input > INPUT:not(:checked) ~ SPAN {
  color: #d50000;
  font-weight: bold;
}
.parent_element > .new-input > INPUT:checked + S::before {
  background-color: #00c853;
}
.parent_element > .new-input > INPUT:checked ~ SPAN {
  color: #00c853;
}

Всё можно посмотреть в финальном примере на codepen.io

Вот, вроде как, и всё. Осталось только «почесать родимые пятна» перфекциониста – проблемы выравнивания.

Выравнивание визуального input и сопроводительного текста

Для начала напомню общеизвестные вещи на тему размещения текста, форматирования и прочего. Всё под катом.

Общеизвестные вещи

Буду стараться не применят специальные термины, ибо в дизайне, верстке и css они иногда отличаются. Всё простыми словами.

1. Свойство font-size не определяет размер букв, а только размер знакоместа. Есть базовая линия (baseline), по которой расположены «нормальные» буквы. У «ненормальных» – g ц – нижние элементы «свисают» ниже её. Есть линия капители (cap height) – это верхняя граница «нормальной» прописной (заглавной) буквы. У «ненормальных» – Ё Й – верхние элементы «вылезают» выше её. Иными словами, размер прописной буквы это расстояние от базовой линии до капители, а знакоместо это чуть больше сверху и снизу. Обычно в «нормальных» шрифтах высота капители это 75% от высоты знакоместо. К примеру, font-size:16px, а размер буквы Н у шрифта Arial будет 12px. Но, бывают «специалисты» у шрифтов которых всё не так.

2. Свойство line-height определяет высоту строки. Если его вычисленное значение больше, чем указано в font-size, то браузер разместит текст так, что бы нормальная прописная буква была по середине высоты строки. Есть нюансы, но тут они не важны.

3. Соответственно, в нашем случае тэги S и SPAN должны иметь одинаковые значения font-size и line-height желательно заданные где-то выше у родителей. В нашем случае в примерах font-size:16px и line-height:1.25. Поэтому в примере N1 у S::before ширина указана 1.25em, а высота у него определяется автоматически. А в примере N2 (и финальный пример) – у S::before ширина и высота 0.75em, что бы был по высоте с прописную букву. Задав другое значение font-size ничего менять не надо. Естественно, эту величину надо подогнать под конкретный шрифт.

4. Если перед текстом стоит какая-то квадратная или круглая «штучка», то любой дизайнер скажет, что она должна быть по высоте с прописную букву. А отбивка между ними должна быть в определенных процентах от размера шрифта. Если высота меньше высоты буквы, то она должна быть визуально значительно меньше, но не меньше 50%. Если больше, то тоже визуально значительно больше, но не больше 150%. А вот чуть-чуть, на пару пикселей больше/меньше – это ужас-ужас! Ну, и расположена эта штучка должна быть на базовой линии или по середине без всяких там чуть-чуть.

Зачем я это упомянул? А затем, что перфекционисту глаза режет, когда input криво стоит рядом с текстом — или прилипает, или далеко, или чуть меньше, или чуть больше. Мы так делать не должны!

Что будет, если сопроводительный текст в SPAN будет выведен в две или три строки? Очевидно, что он «залезет» под input. Это не красиво, надо исправить.

Один древний метод такой: тэгу S делаем float:left, а тэгу SPAN display:block и overflow:hidden.

Получится колонка текста. Подразумевается, что у кого-то из них будет соответствующий margin, что даст отбивку между ними. Ну, ещё добавляется геморрой с прекращением float после SPAN. Мы пойдем современным путем – применим flexbox. Он тут совершенно к месту.

.new-input {
  display: flex;
  flex-direction: row;
  align-items: start;
}
.new-input > S {
  margin-right: 4px;
  flex: 0 0 auto;
}
.new-input > SPAN {
  flex: 0 1 auto;
}

В этом случае тэг LABEL (который .new-input) будет flex, S и SPAN будут блоками, разместятся вверху LABEL. Текст в SPAN в случае необходимости будет в несколько строк. Вот из-за этого визуальный input описали в S::before. Независимо от высоты SPAN S::before будет расположен на одной базовой линии с первой строкой SPAN. Как вариант можно было указать align-items:center – тогда при однострочном SPAN визуальный input был бы вверху, а при двух строках – посередине, а при трех – у второй строки. В финальном примере можно переключать расположение input.

Вот и всё

Надеюсь, было интересно и кому-нибудь полезно. Прошу, не сильно меня ругать – это мой первый опыт на Хабр.

Пример N 1 – просто демонстрация взаимодействия изменения input и соседнего элемента.

Пример N 2 – визуализация input средствами css, как основа решения.

Финальный пример – всё описанное вместе.

Про конкретную реализацию

Там были обширные формы, где блоки полей возможные для правки конкретным пользователям выделялись слабым фоном, а остальным input имели свойство disabled и служили только для информации. Поэтому стиль «. new-input > INPUT:disabled + S::before» не применяли.

UPD

В ответ на комментарии Пример N 3.
Там работает поддержка клавиш и фокуса для скрытого input.

:checked — CSS: каскадные таблицы стилей «checkbox»>

) или option ( in a
<дел>