Борба с BEM Топ 10 грешки и как да ги избегнем
Вече нямам никакви съмнения относно начина на назоваване на нещата. Единственото нещо, което ме отблъсна за дълго време, беше грозният синтаксис. Дизайнерът вътре в мен не искаше да затрупва великолепната ми маркировка с ужасни двойни долни черти и тирета.
Разработчикът в мен го погледна прагматично. И в крайна сметка модулният начин за изграждане на потребителски интерфейс надделя над дясната страна на мозъка ми, която каза: „Но не е достатъчно добър!“. В такива случаи предпочитам функционалността пред външната форма. Във всеки случай стига празни приказки. Ето 10 проблема, с които се борих, и няколко съвета как да ги разреша.
1. "Какво да правим със селекторите "внучка" (и не само)?"
За да бъде ясно, селекторът на внук се използва, когато трябва да се обърнете към елемент, който е вложен на две нива. Тези лоши момчета са проклятието на живота ми и съм сигурен, че това е една от причините хората веднага да изпитват отвращение към BEM. Ще дам пример:
Както можете да видите, имената могат бързо да излязат извън контрол и колкото повече са вложени, толкова по-грозно става името на класа на елемента. Използвах кратка кутия, наречена c-card, и кратки имена body, text, link, но опитайте се да си представите какво се случва, ако началният блоков елемент се нарича c-drop-down-menu.
Вярвам, че двойното подчертаване трябва да се появява само веднъж, в име на селектор. BEM означава Block_Element--Modifier, а не Block_Element__Element--Modifier. Също така избягвайте именуване на многостепенни елементи. Ако получите нива на пра-пра-пра-правнуци, вероятно трябва да преосмислите структуратакомпоненти.
Наименуването на BEM не е строго обвързано с DOM, така че няма значение на колко нива е вложеният елемент. Конвенцията за именуване основно ви помага да видите връзката с блока на най-високо ниво - в нашия случай c-card.
Как бих разгледал същия компонент на картата:
Това означава, че всички вложени елементи ще бъдат засегнати само от блока на картата. Също така бихме могли да преместим текст и изображения в c-card__header или дори да въведем нов елемент c-card__footer, без да нарушаваме семантичната структура.
2. "Какви имена да използвам?"
Досега може би сте забелязали използването на c- в моите примери. Този префикс означава "компонент" и е в основата на всички мои имена на BEM класове. Идеята е заимствана от техниката за именуване на Хари Робърт, която подобрява четливостта на кода.
Системата, която възприех, и многото префикси, които ще бъдат използвани в примерите в тази статия, са:
Открих, че имена като това са подобрили значително четливостта на моя код. Дори и да не мога да ви накарам да се пристрастите към BEM, този трик определено си струва да запомните.
Можете също така да използвате други префикси, като qa- за тестове за осигуряване на качество, ss- за различни кукички от страна на сървъра и т.н. Но списъкът по-горе вече е добро начало и можете да въведете нови имена, след като се запознаете с тази техника.
Ще видите добър пример за този стил на именуване в следващия брой.
3. "Как да наричам обвивки?"
Някои компоненти изискват родителска обвивка (или контейнер), която задава оформлението на дъщерните елементи. В тези случаи винаги се опитвам да абстрахирам оформлението в модул за оформление,като l-grid и добавете всеки компонент като съдържание на l-grid__item.
В нашия пример с карти, ако искаме да съставим списък от четири c-карти, бих използвал следното маркиране:
Вече трябва да имате солидна представа как оформлението на модула и имената на компонентите трябва да си пасват.
Не се страхувайте да използвате малко по-разширено маркиране, за да нямате главоболие по-късно. Никой няма да бъде по-добре, ако отрежете двойка.
В някои ситуации това не е възможно. Ако, например, вашата мрежа няма да ви слуша или просто искате смислено име за вашия родителски елемент, тогава какво правите? Склонен съм да избера думата контейнер или списък, в зависимост от ситуацията. Прилагайки това към нашия пример с карта, бих могъл да използвам
4. "Кръстокомпонентни... Компоненти?"
Друг често срещан проблем са компонентите, чийто стил или позициониране се влияе от техния родителски контейнер. Различни решения на този проблем са подробно описани в Simurai. Ще ви разкажа само за подхода, който считам за най-ефективен.
Накратко, нека приемем, че искаме да добавим c-бутон към нашето card__body от предишния пример. Този бутон вече е негов компонент и е проектиран по следния начин:
Ако нямате никакви разлики между обикновения компонент на бутона, тогава няма проблем, просто ще го преместим така:
Но какво се случва, когато има леки разлики в стила - например искаме да го намалим малко, да заоблим ъглите, но само когато е част от компонента на c-card?
Класът между компонентите ми се стори най-надеждното решение:
В горния пример класът c-card__c-button се опитва да промени единили няколко от съществуващите свойства на c-button, но зависи от произхода на повикването (или дори спецификата) за успешно приложение. Класът c-card__c-button ще работи само ако е дефиниран след блока c-button, който може бързо да излезе извън контрол, ако изградите повече от тези кръстосани компоненти. (Можете да разчитате на !important, разбира се, но аз не бих!).
Стилът на един наистина модулен UI елемент трябва да бъде напълно независим от родителския контейнер - той трябва да изглежда по същия начин, независимо къде го поставите. Добавянето на клас от друг компонент за задаване на стила, както прави методът „смесване“, нарушава отворените/затворените принципи на компонентно-ориентирания дизайн – т.е. не трябва да има зависимост от друг модул за естетика.
Най-добре е да използвате модификатор за тези малки козметични разлики, защото можете лесно да ги използвате отново, докато проектът ви расте.
Дори и да не използвате отново тези допълнителни класове, поне няма да бъдете обвързани с родителския контейнер или оригиналния ред за прилагане на промените.
Друг начин е да отидете при вашия дизайнер и да му кажете, че бутоните трябва да са съвместими с останалите и да избегнете този проблем напълно.
5. "Модификатор или нов компонент?"
Един от най-големите проблеми е да решите къде свършва един компонент и къде започва нов. В примера с c-card можете да създадете друг компонент, наречен c-panel, с много подобни стилови атрибути, но много забележими разлики.
Но какво определя необходимостта от използване на два компонента, c-panel и c-card, или прост модификатор за c-card, който прилага уникални стилове.
Могалесно е да препълните проект с модули и да направите всичко като компоненти. Препоръчвам да започнете с модификатори, ако установите, че управлението на някой от компонентните CSS файлове става твърде сложно, тогава може би е време да отделите някои модификатори. Добър индикатор, когато трябва да нулирате всичко в блок от CSS, за да оформите нов модификатор, според мен е, че е време да създадете нов компонент.
Най-добре е да работите с екип от други разработчици или дизайнери, за да получите тяхното мнение. Съберете ги за няколко минути и обсъдете с тях. Въпреки че този отговор изглежда по-скоро като извинение, за големи приложения е много важно да се разбере какви модули са налични и какво е компонент.
6. "Как да управляваме състоянията?"
Това е доста често срещан проблем, особено когато оформяте компонент в активно или отворено състояние. Да приемем, че нашите карти са активни; така че, когато щракнем върху тях, те се подчертават с красив щрих. Какво ще кажете за името на този клас?
Имате две възможности: или да използвате кукичката за офлайн състояние, или да използвате BEM-подобно наименуване на модификатор на ниво компонент:
Логично е да се придържате към стандартния набор от държавни куки. Крис Пиърс е съставил добър списък, който препоръчвам да разгледате.
7. "Кога е по-добре да не добавяте нов клас към елемент"
Мога да разбера хората, които са претоварени от големия брой класове, необходими за изграждането на сложна част от потребителския интерфейс, особено ако не са присвоили клас на всеки етикет.
Обикновено прикрепям класове към всичко, което трябва да бъде стилизирано по различен начин в контекста на даден компонент. Често оставям p тагове без клас, освен ако компонентът не изисква,за да изглеждат специални в този контекст.
Съгласен съм, това ще означава, че вашето маркиране ще съдържа много класове. Но в крайна сметка вашите компоненти ще могат да живеят независимо и да се движат без странични ефекти.
Поради глобалния характер на CSS, присвояването на клас на всичко ни дава пълен контрол върху изобразяването. Първоначалният дискомфорт си заслужава предимствата на напълно модулната система.
8. "Как да вложим компоненти?"
Да речем. че искаме да покажем контролния списък в нашия компонент c-card. Пример как да не правите това:
Тук имаме няколко проблема. Първият е селекторът на две нива, за който научихме в първия раздел. Второ, всички стилове, приложени към c-card__checklist__item, са специфични за този конкретен случай и не могат да бъдат използвани отново.
Предпочитам да разделя списъка на модул за маркиране, елементите на контролния списък на техните собствени компоненти, което им позволява да се използват самостоятелно другаде. Това връща нашия префикс l- в игра:
Това ви спестява от необходимостта да повтаряте тези стилове и също така означава, че можем да използваме l-list и c-checkbox другаде в нашето приложение. Това изисква малко повече маркиране, но в замяна получаваме четливост, капсулиране и повторно използване. Може би сте забелязали, че това са основните теми!
9. "Няма ли компонентите да имат милион класове?"
Някои хора смятат, че наличието на множество класове на елемент е лошо и могат да се добавят и --модификатори. Лично на мен това не ми се струва проблем, защото прави кода по-четлив и знам точно какво трябва да прави.
Пример за четири класа, с които е стилизиран бутона:
Разбирам, че товасинтаксисът е малко грозен, но е разбираем.
Ако обаче това ви създава главоболия, можете да погледнете техниката на Сергей Заровски. Просто казано, трябва да използваме. ] в листа със стилове, за да симулирате допълнителна функционалност. Ако този синтаксис ви звучи познато, това е защото Icomoon използва подобен начин за управление на селектори на икони.
С тази техника вашият код може да изглежда така:
Не знам дали ударът в производителността е голям при използване на > и > повече от използването на отделни класове, но на теория това е страхотна алтернатива. За мен многокласовият вариант е достатъчен за мен, но ми се струва, че този начин определено си заслужава да се спомене за тези, които предпочитат алтернатива.
10. "Можем ли да променим типа отговор на компонента?"
Този проблем ми беше поставен от Arie Thulank и се опитах да намеря 100% решение.
Пример може да бъде падащо меню, което се превръща в набор от раздели в даден момент, или странична навигация извън екрана, която се превръща в лента с менюта.
По принцип един компонент може да има две различни състояния, продиктувани от медийна заявка.
Хари Робъртс спомена отзивчивите префикси като един от начините за управление на това. Неговият подход е по-скоро за промени в оформлението и стила, отколкото за преместване на всички компоненти, но не виждам причина защо не можем да приложим тази техника тук. Така че можете да именувате класове така:
Занапред това ще продължи да съществува във вашите медийни заявки за подходящите размери на екрана. Съвет: Ще трябва да екранирате @ с обратна наклонена черта, като това:
Нямах причина да създавам такива компоненти, но този начин изглежда много приятелски настроен къмкъм разработчика. Трябва да е лесно за другия човек да разбере вашите намерения. Но аз не рекламирам имена като малък и голям екран - те са там само за подобряване на четливостта.
Заключение
Hardcore conf в C++. Каним само професионалисти.