Режими і стани у SCSS

Режими і стани у SCSS

Міркування на тему використання режимів і станів компонентів для користувача інтерфейсу. Розглянемо застосування підходу при роботі з препроцессорами стилів. Обережно, в статті дуже багато прикладів коду.


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

Кожному стану кнопки відповідає деякий оформлення. Оформлення інтерфейсу компонента може повторюватися в різних станах одного режиму і в різних режимах. Кількість станів режиму залежить від конкретного режиму.

Співвідношення Режим-Стан

Таким чином, можна домовитися, що

  • View / Подання
    — набір стилів характеризують сутність користувальницького інтерфейсу (в певному Стані);
  • State / Стан
    — умови впливають на Уявлення;
  • Mode / Режим
    — безліч Станів однієї сутності.


В ролі об'єкта Режиму виступає компонент інтерфейсу не нижче рівня Блок в термінах БЕМ.

Два режими не можуть мати Подання з однаковими властивостями. Якщо Режими впливають на одні і ті ж властивості, об'єднайте їх.

Приклад простої
В інтерфейсі кнопка має кілька станів: link — в очікуванні дії користувача, hover — користувач наводить курсор на кнопку, active — користувач затиснув ліву клавішу миші над кнопкою. Кнопка може бути синій і зеленій.

Приклад з кнопками

У даному прикладі обидві кнопки (блакитна і зелена) представлені в трьох станах. Для кожної з кнопок набір станів ідентичний. Набір станів утворює певний режим.

Режими і стану на прикладі кнопок

Я формую стилі для кнопок наступним чином:
// set modes
@each $skinName, $skinColor in (
( 'green', $green ), 
( 'blue', $blue )
) {

.button_color_#{ $skinName } { // set state «link»
@include skinView($skinName, 'link', $skinColor); // call view
}

.button_color_#{ $skinName }:hover:not(.button_disabled) { // set state «hover»
@include skinView($skinName, 'hover', $skinColor); // call view
}

.button_color_#{ $skinName }:active:not(.button_disabled) { // set state «active»
@include skinView($skinName, 'active', $skinColor); // call view
}
}

Опис Уявлень відокремлене від опису Станів:
// set views
@mixin skinView($skin, $state, $color) {
& {
background-color:
if( $state == 'link', $color, null )
if( $state == 'hover', lighten($color, 10%), null )
if( $state == 'active', darken($color, 10%), null );
}

&:before {
border-bottom-color:
if( $state == 'link', darken($color, 10%), null )
if( $state == 'hover', $color, null )
if( $state == 'active', darken($color, 20%), null );
}

.button__text {
color:
if( $state == 'link', #fff, null )
if( $state == 'hover', lighten($color, 45%), null )
if( $state == 'active', lighten($color, 40%), null );
}
}

Таким чином ми отримали розширюваний механізм генерації стилів для різних кнопок в різних станах.

Приклад складніше
моєму сайті ширина тематичній області не фіксована. Контентну частина наповнює безліч блоків: параграфи, списки, зображення, приклади коду і т. д. Кожен з тематичних блоків має кілька модифікацій з різними параметрами. Наприклад, списки можуть бути
ol
,
ul
та
dl
.

І кожен з них може бути як «широким», так і «вузьким» в залежності від різних умов.

Одним з параметрів відображення списків є ширина тематичній частині, яка впливає на ширину самого списку.

Різне поведінка ширини тематичній області

Якщо екран користувача, щодо, великий, то список займає рівно 640px по ширині, якщо екран маленький, — всю ширину тематичній області сторінки.

Схожа залежність властива й іншим контентний блокам. Контентна область фрагменту коду на великому екрані вузька, його ширина становить 640px, а на маленькому — вузька, але інша.

А, «широке» зображення, широке на всіх екранах — воно займає 100% ширини батьків. Таким чином комбінацій Уявлень тематичних блоків занадто велика.

Велика кількість тематичних блоків і наявність декількох умов відображення змушують створити одну універсальну абстрактну сутність, поведінка / характеристики якої наслідували б всі тематичні блоки. Все як в ООП.

На моєму сайті кожен контентний блок приймає один з трьох Режимів поведінки:
  • wide / широкий
    широкий на великому екрані, широкий на маленькому;
  • tricky / хитрий
    вузький на великому екрані, широкий на маленькому;
  • slim / вузький
    вузький на великому екрані, вузький на маленкому.


Умовами відображення є: тип пристрою та розмір екрана. Складності додає Режим «Хитрий», який включає Уявлення від двох інших режимів: на великому екрані він «вузький», а на маленькому — «широкий».

Тематичні блоки різної ширини

Режими і Стани описуються наступним чином:
// set modes
@mixin slimContent($mode) {

// set states for desktop and tablet
@include device(desktop, tablet) {
@include screen_m-- {
@include slimContent__view($mode, 'screenBig'); // call view
}

@include screen_--s {
@include slimContent__view($mode, 'screenSmall'); // call view
}
}

// set state for phone
@include device(phone) {
@include slimContent__view($mode, 'screenSmall'); // call view
}
}


Для опису Уявлень використовуємо хелпер:
// set views
@mixin slimContent__view($mode, $screenSize) {
@if $mode == 'wide' {
@if $screenSize == 'screenBig' {
@include slimContent__view-helper(padding, 0); // call view-helper
}

@if $screenSize == 'screenSmall' {
@include slimContent__view-helper(padding, 0); // call view-helper
}
}

@if $mode == 'tricky' {
@if $screenSize == 'screenBig' {
@include slimContent__view-helper(margin, auto); // call view-helper
}

@if $screenSize == 'screenSmall' {
@include slimContent__view-helper(margin, 0); // call view-helper
}
}

@if $mode == 'slim' {
@if $screenSize == 'screenBig' {
@include slimContent__view-helper(padding, auto); // call view-helper
}

@if $screenSize == 'screenSmall' {
@include slimContent__view-helper(padding, $d); // call view-helper
}
}
}


Зміст хелперу таке:
// view-helper
@mixin slimContent__view-helper($property, $value) {
@if $value == 'auto' {
#{$property}-left: calc((100% - #{$contentWidth})*(1/3));
#{$property}-right: calc((100% - #{$contentWidth})*(2/3));
}

@else {
#{$property}-left: $value;
#{$property}-right: $value;
}
}


Застосування Режиму елементарно:
.ui-snippet {
@include slimContent(slim);
@include ritm(1*$v);
overflow-x: auto;
}

.ui-image {
&.ui-image_wide {
@include slimContent(wide);
}

&.ui-image_narrow {
@include slimContent(tricky);
}
}


Такий підхід дозволяє структурувати код і уникнути багаторазового дублювання, яке мало б місце, при описі даного поведінки у кожного тематичного блоку окремо.

Оригінал статті

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.