Главная->Уроки по css->Что можно сделать с помощью CSS?
Что можно сделать с помощью CSS?
В последнее время я заинтересовался в том, как сильно можно углубиться в использование решений, основанных только на CSS, чтобы по максимуму избавиться от JavaScript при проектировании интерфейсов. Прошу запомнить: некоторые из этих демонстрационных примеров реализованы только для того, чтобы показать, каким мощным стал CSS. Я еще расскажу, почему некоторые из этих демок стоило бы перенести на JavaScript. И еще: чтобы не усложнять, я предоставлю только необходимые фрагменты HTML и CSS. Не стоит ожидать, что при копировании материала из статьи возымеет такой же эффект, как и в нижеприведенных демках. Но если вы хотите посмотреть полный код примера - можно перейти на сайт CodePen, также в конце статьи я предоставлю ссылку на файлы с исходным кодом. Пример простой формы Ниже представлен вариант оповещения пользователя при заполнении формы. Демо на CodePen Тут представлены три текстовых поля, в качестве меток используются заполнители. Конечно, это не очень хорошо сказывается на пользовательском взаимодействии, но я попадал во многие ситуации, когда главный дизайнер запрещал использование видимых меток. Бывает. Мое решение этой надуманной проблемы состоит в том, чтобы преобразовать метки в “всплывающие напоминания”, когда пользователь переводит фокус на поле ввода: 1 <div class="form-row"> 2 <input type="email" class="input-text" id="name2" placeholder="Your Email" /> 3 <label class="label-helper" for="name2">Your Email</label> 4 </div> Как видно в этом участке HTML нет никакой магии. Единственная непривычная вещь заключается в том, что элемент <label> размещается за полем ввода. А вот этот кусок CSS заставит все это работать: 01 .form-row { 02 position: relative; 03 } 04 05 .input-text { 06 background-color: #fff; 07 border: 1px solid #ccc; 08 margin-bottom: 8px; 09 padding: 8px 4px; 10 position: relative; 11 width: 100%; 12 z-index: 3; 13 } 14 15 .label-helper { 16 bottom: 0; 17 left: 0; 18 opacity: 0; 19 position: absolute; 20 transition: .2s bottom, .2s opacity; 21 z-index: 1; 22 } Первый блок CSS - это стили по умолчанию для полей ввода. Стиль .form-row удерживает абсолютно спозиционированный элемент .label-helper рядом с соответствующим полем ввода. Элемент .input-text спозиционирован относительно, а z-index к нему применен потому, что мы не хотим, чтобы метка перекрывала поле ввода при переходе. А вот и CSS, который обрабатывает показ метки при смене фокуса: 01 .input-text:focus + .label-helper, 02 .input-text:invalid + .label-helper { 03 bottom: 95%; /* magic number, boo */ 04 font-family: arial; 05 font-size: 14px; 06 line-height: 1; 07 opacity: 1; 08 padding: 4px; 09 } 10 11 .input-text:invalid { 12 border-left: 10px solid #f00; 13 } 14 15 .input-text:invalid + .label-helper:after { 16 color: #f00; 17 content: "X"; 18 font-family: arial; 19 font-size: 14px; 20 line-height: 1; 21 padding-left: 12px; 22 } Важным моментом является использование псевдо-класса :focus в комбинации с селектором соседних элементов + (селекторы соседних элементов играют важную роль в наших демонстрационных примерах). Поэтому нам и надо размещать метки после полей ввода, так как иначе не будет работать селектор соседних элементов. Проще говоря, когда элемент попадает в фокус, соседний элемент (метка) становится видимым, и изменяет свое положение так, чтобы оказаться над полем ввода, и предоставляет пользователю подсказку, так как заполнитель поля более не отображается. В качестве небольшого дополнения я подсвечиваю поле ввода email-а, если в нем ошибка. Так как поля ввода не поддерживают псевдо-элементы, ошибка ‘x’ применяется к метке. Несколько более сложных вариантов использования Приведенный выше пример обретает популярность. Как я знаю, я не первый и не последний, кто использовал эффект для отображения подсказки. Переходы для одностраничных сайтов Codrops опубликовал пример полноэкранных макетов с эффектами перехода в апреле 2013. Мне всегда казалось, что это очень круто, и я хотел попробовать воссоздать этот эффект, или же что-то похожее на него, но только без JavaScript. Немного поигравшись, я таки сделал это: Демо на CodePen Вот HTML: 01 <input type="radio" class="radio" name="pages" id="exit" checked /> 02 03 <div class="page demo2"> 04 05 <input type="radio" class="radio" name="pages" id="page_1" /> 06 <section class="section-container section-one"> 07 <label for="exit" class="check-label exit-label"> 08 X 09 </label> 10 <label for="page_1" class="page-label check-label"> 11 <span>Page X</span> 12 </label> 13 <header class="section-header"> 14 <div class="section-content"> 15 <h1> 16 Title of Section X 17 </h1> 18 </div> 19 </header> 20 <div class="section-info"> 21 <div class="section-content"> 22 <p> 23 ... 24 </p> 25 </div> 26 </div> 27 </section> 28 29 <!-- repeat this 3 more times, with IDs of 30 "page_2, page_3, and page_4" --> 31 32 </div> Возможно, вы заметили использование элементов <input type="radio"> в начале каждого раздела. В CSS мы будем основываться на них, так как нам также нужно опираться на селекторе соседних элементов (+), чтобы заставить работать эффекты перехода. Проще говоря, мы будем использовать состояние :checked переключателей, которые будут активироваться кликом по соответствующей метке, и с помощью селектора соседних элементов мы будем менять их размещение с помощью простого CSS. А вот и сам CSS: 01 .radio, 02 .exit-label { 03 display: none; 04 height: 0; 05 visibility: hidden; 06 } 07 08 .exit-label { 09 border: 1px solid #333; 10 color: #444; 11 display: inline-block; 12 padding: 4px 12px; 13 text-decoration: none; 14 } 15 16 .page-label { 17 height: 100%; 18 left: 0; 19 position: absolute; 20 text-align: center; 21 top: 0; 22 width: 100%; 23 } 24 25 .page-label span { 26 position: relative; 27 top: 50%; 28 } Вышеприведенный CSS обеспечивает стилизацию “кнопок”, а также скрывает переключатели, отключая их отображени и прямое взаимодействие. Элементы .page-label и .page-label span используются для больших кнопок-панелей, тогда как класс .exit-label стилизует кнопку “закрытия” открытой панели (опять же, метки, стилизованные под кнопки). 01 .section-container { 02 border: 0px solid; 03 height: 50%; 04 overflow: hidden; 05 position: fixed; 06 transform-origin: center center; 07 transition: 0.4s all; 08 width: 50%; 09 z-index: 1; 10 } 11 12 .section-content { 13 margin: auto; 14 max-width: 800px; 15 opacity: 0; 16 padding: 20px; 17 transition: 0.66s all; 18 visibility: hidden; 19 } 20 21 .section-one { 22 transform: translate(0%, 0%); 23 } 24 25 .section-two { 26 transform: translate(0%, 100%); 27 } 28 29 .section-three { 30 transform: translate(100%, 0%); 31 } 32 33 .section-four { 34 transform: translate(100%, 100%); 35 } Тут у нас настройка контейнеров панелей (разделов) и их содержимого. Контейнеры имеют размер 50% от общей высоты и ширины, что похоже на соответствующий пример с Codrops. Разделы позиционируются с помощью transform: translate, вместо того, чтобы использовать позиционирование сверху/слева/снизу/справа. Это потому, что трансформации лучше сказываются на производительности, так как они не требуют интенсивной перерисовки, как это происходит в случае со сменой позиции элемента. 01 :checked + .section-container { 02 height: 100%; 03 overflow: auto; 04 transform: translate(0%, 0%); 05 width: 100%; 06 z-index: 5; 07 } 08 09 :checked + .section-container .exit-label { 10 display: inline-block; 11 visibility: visible; 12 } 13 14 :checked + .section-container .page-label { 15 display: none; 16 } 17 18 :checked + .section-container .section-content { 19 opacity: 1; 20 visibility: visible; 21 } 22 23 #exit:checked + .page .section-container { 24 border: none; 25 opacity: 1; 26 } 27 28 .page :not(:checked) + .section-container { 29 border: 40px solid; 30 } А вот и веселая часть, где мы проверяем, если переключатель был выбран, и если так, то: .section-container изменяет свои размеры до полной высоты и ширины, и трансформируется в верхнее левое положение окна браузера кнопка закрытия и содержимое раздела становятся видимыми метка, на которую кликнули, становится невидимой, так что по ней больше нельзя кликнуть, и не перекрывает содержимое секции, которое только что было отображено секциям, которые не были выбраны, проставляется граница, которая увеличивается до размера 40px, симулируя эффект “уменьшения” на фоне секции, которая разворачивается на весь экран. Альтернативные эффекты перехода На основе слегка переделанной разметки из предыдущего примера, я представляю еще два примера, которые следуют тем же концепциям, что и прошлый пример. Демо на CodePen Демо на CodePen Проблема этих примеров состоит в том, что не смотря на всю их крутость и то, что они показывают, как много всего можно сделать с помощью CSS, в них все очень плохо в плане доступности для пользователей с ограниченными возможностями. Они используют переключатели и метки для обхода необходимости использования семантически корректных кнопок, и JavaScript, необходимого для контроля этих кнопок. Для зрячих пользователей, которые просматривают страницы с помощью мыши и сенсорных экранов, не возникает каких-либо проблем с просмотром этих демонстрационных примеров. Но если мы хотим, чтобы и пользователи, использующие для навигации клавиатуру тоже не испытывали проблем с просмотром, необходимо будет вернуться к использованию JavaScript для написания семантически корректного кода и манипуляции разметки. Также нужно будет писать еще больше JavaScript кода для того, чтобы применять к элементам корректные ARIA атрибуты, и корректно обрабатывать клавиатурные команды, чтобы элементы нашей страницы корректно вели себя при использовании клавиатуры. Честно говоря, мне было нелегко заставить работать эти примеры, и я не хотел браться за написание дополнительного JavaScript, который бы обеспечил соблюдение стандартов доступности - проще было бы использовать подходящие по семантике элементы и обрабатывать их с помощью JavaScript. Приняв это к сведению, давайте перейдем к следующему примеру, который куда как более доступен пользователям с ограниченными возможностями, но позволяет получить похожие результаты. Смена макета с помощью :target Немного изменив CSS и разметку, можно добиться такого же поведения, как в примерах с навигацией с помощью переключателей, но используя ID и псевдо-класс :target. Демо на CodePen Давайте посмотрим на изменения в разметке: 01 <section class="section-container section-one" id="page_1"> 02 <a href="#exit" class="btn btn-exit"> 03 X 04 </a> 05 <a href="#page_1" class="btn btn-page"> 06 <span>Page 1</span> 07 </a> 08 <header class="section-header"> 09 <div class="section-content"> 10 <h1> 11 Title of Section X 12 </h1> 13 </div> 14 </header> 15 <div class="section-info"> 16 <div class="section-content"> 17 <p> 18 ... 19 </p> 20 </div> 21 </div> 22 </section> 23 24 <!-- это повторяется для id="page_2" и других --> вместо использования переключателей и селектора соседних элементов, теперь мы используем ID каждого из элементов-контейнеров. Вместо переключателей роль “кнопок” играют ссылки-якоря, что куда как более семантически правильно. Вот CSS, который претерпел большее количество изменений: 01 .btn-exit { 02 display: none; 03 visibility: hidden; 04 } 05 06 #page_1:target .btn-exit, 07 #page_2:target .btn-exit, 08 #page_3:target .btn-exit, 09 #page_4:target .btn-exit { 10 border: 1px solid #333; 11 display: inline-block; 12 padding: 4px 12px; 13 text-decoration: none; 14 visibility: visible; 15 } 16 17 .btn-page { 18 height: 100%; 19 left: 0; 20 position: absolute; 21 text-align: center; 22 top: 0; 23 width: 100%; 24 } 25 26 .btn-page span { 27 position: relative; 28 top: 50%; 29 } 30 31 #page_1:target, 32 #page_2:target, 33 #page_3:target, 34 #page_4:target { 35 transform: translate(0%, 0%); 36 width: 100%; 37 height: 100%; 38 z-index: 5; 39 overflow: auto; 40 } 41 42 #page_1:target .btn-exit, 43 #page_2:target .btn-exit, 44 #page_3:target .btn-exit, 45 #page_4:target .btn-exit { 46 display: inline-block; 47 visibility: visible; 48 } 49 50 #page_1:target .btn-page, 51 #page_2:target .btn-page, 52 #page_3:target .btn-page, 53 #page_4:target .btn-page { 54 display: none; 55 } 56 57 #page_1:target .section-content, 58 #page_2:target .section-content, 59 #page_3:target .section-content, 60 #page_4:target .section-content { 61 border: 0; 62 opacity: 1; 63 visibility: visible; 64 } 65 66 #exit:target .page .section-container { 67 border: none; 68 opacity: 1; 69 } Здесь я привел только тот CSS код, в котором было сделано больше всего изменений, но сравнив предыдущие и текущий пример, можно заметить, что изменения коснулись только селекторов. Селекторы меток были заменены на использование атрибута href вместо атрибута for, и у нас получилось отказаться от использования переключателей. Так как теперь мы используем :target для того, чтобы ссылаться на разные разделы, то селекторы :checked + были заменены на соответствующие селекторы :target. Если ранее можно было объявить один единственный селектор :checked + для всех вариантов, то использование селектора :target требует от дублировать его для каждого URI. Необходимость писать такое количество селекторов, конечно, минус по сравнению с вариантом с переключателями. Но замечу: этот вариант более правильный с точки зрения семантики. ПО правде говоря, если мы хотим сделать этот пример доступным всем пользователям, необходимо будет использовать дополнительные ARIA атрибуты для каждой из наших активных областей, которые мы сделали визуально похожими на кнопки, но которые на самом деле являются ссылками (что имеет значение по причинам доступности для людей с ограниченными возможностями). И простого добавления ARIA атрибутов недостаточно, так как при смене состояния, атрибуты вроде aria-hidden должны быть обновлены, а фокус должен быть смещен с предыдущего активного элемента на текущих, и это требует наличие JavaScript кода. В этом случае потребуется меньшее количество JavaScript кода, чем в оригинальной версии данного примера. И дополнительным плюсом или минусом является тот факт, что если кто-то, переключившись на конкретный блок, запишет или добавит в закладки URI страницы, то при переходе по этой ссылке он проскочит нормальное состояние страницы. Опять же - это может быть как плюсом, так и минусом, в зависимости от преследуемых целей. Но об этом надо помнить. В завершение CSS - мощный инструмент, который позволяет делать множество вещей, для которых мы ранее использовали Javascript или jQuery. Но нужно избегать реализации решений, которые могут стать настоящим кошмаром в доступности для пользователей с ограниченными возможностями, если чересчур увлечься использованием CSS. Напоследок напомню - убедитесь, что вашими экспериментами вы не добавили пользователям хлопот с использованием вашего приложения. И в случае с веб-приложениями, возможно, лучше сильно не мудрить, и использовать JavaScript для реализации переходов между состояниями. Но приведенные примеры больше служат для вдохновения, чем являются призывом к использованию.