Спойлер: атомарный дизайн
Спойлер: атомарный дизайн

Это работа является логическим продолжением моего первого подробного текста для сообщества об актуальных подходах к верстке Как верстать веб-интерфейсы быстро, качественно и интересно. Но, если, в первом трактате, внимание уделялось, прежде всего, стилю кода, его качеству и эффектным современным возможностям различных препроцессоров и фреймворков, что демонстрировалось на некоторых конкретных специфических задачах, теперь хочется сфокусироваться на архитектурных или даже организационных аспектах веб-производства. Если вы не читали мой первый текст, но собираетесь при этом прочесть этот — не поленитесь перейти по ссылке и пробежать глазами самые последние разделы каждой из двух частей первого пособия: «Готовые решения» и «Песочницы». Этот текст начинает прямо с этих мест и развивает именно эти идеи: и о пагубности применения раскрученных-популярных «на все готовых» UI-«дизайн-систем»-фреймворков для создания кастомизированных веб-морд любой сложности и, о, по сути, полезности использования хотя бы минимального документирования и явных соглашений при разработке веб-GUI на фронтенде. Но я не стану тратить время, доказывая, что «ни в коем случае нельзя использовать Vuetify или AntDesign» для создания крупных UI-систем с полностью кастомным оформлением. Вам не нужно прикручивать себе огромный геморрой непроницаемый слой плохо кастомизируемого готового GUI для того чтобы написать кнопку или поле ввода! Если вам нужен датапикер — найдите и допилите что-нибудь под себя. Это понимание может только прийти или так и не придти с годами тяжелого опыта, когда вы будете постоянно тратить непростительно много своего времени на то, чтобы написать очевидно отвратительный CSS — «кряки с !important`ами поверх стилей библиотеки», выдумывать чудные костыли на javascript чтобы изменить дефолтное поведение виджетов на кастомное и хитрое-нестандартное затребованное дизайнерами... И, при этом, ваши шаблоны, стили и js-обвязки будут превращаться во все менее читаемые, запутанные нагромождения разнообразно оформленного кода, с различным подходом к наименованию и прочими бедами… Этот текст и написанный для него проект призваны наглядно показать «а как надо?».

Симонов Игорь Иванович. «Бракодел». 1953 год.
Симонов Игорь Иванович. «Бракодел». 1953 год.

Верстка по-прежнему остается достаточно свободной дисциплиной, в которой сосуществуют множество совершенно разных методологий, подходов и связанных с ними технических решений. Часто программисты принимают какой-то один определенный способ, и, в результате, стагнируют в общем понимании и развитии. Привычки формируют удобную зону комфорта и это мешает осознать риски в ситуациях для которых они неадекватны. Знаете, какой аргумент мне уже несколько раз приходилось слышать в технической дискуссии от оппонентов защищавших привычную для них систему, технику разметки, но совершенно непригодную, на мой взгляд, для решения текущих насущных задач, в реальной ситуации данного конкретного проекта и сроков? «Ну это же общепринятая технология?», «Гугл, Фейсбук, … это используют, чем мы хуже»... Если вы слышите от кого-то, что нечто, не относящееся к действительно используемому всеми нами известному перечню базовых спецификаций — «общепринято», это повод сразу сделать вывод о том, что ваш собеседник имеет мало разнообразного опыта в этом и просто защищает свою лень и нежелание учиться новому. Рядом с аргументом про странные пристрастия акул капитализма обычно следует еще и что-нибудь совершенно несостоятельное про «наши программисты не все знают Stylus», при том что, любой препроцессор CSS даже и тем более начинающий программист способен изучить до базового уровня за один полезный приятный вечер. Мне всегда нравилось менять технологии, синтаксисы — это освежает и придает драйву, интереса в работе. И именно совершенно разнообразный опыт реальной коммерческой практики с различными технологиями сформировал мои представления о том «что хорошо, что плохо» в тех или иных ситуациях.

Закон рабочей чести!
Закон рабочей чести!

Возможно, я когда-нибудь накоплю достаточно злости и найду время для того чтобы на наглядных вопиющих примерах показать «а почему вот это плохо?». Но эта статья и пример модуля о том «как надо?», что точно будет аккуратно и эффективно. Если вы являетесь упертым сторонником CSS-in-JS-подходов, или, вообще, по-прежнему наносите оформление на разметку в «бородатом» «утилитарном» стиле «много классов с говорящими названиями содержащих одно-два правило» (тут уместно вспомнить уже «продвинутую» — CSS-in-JS — реализацию Tailwind с ее оголтелым слоганом-оксюмороном: «"Best practices” don’t actually work».) — предложенная и в моей первой книжке и в ее практическом продолжении — здесь — методология, использующая только любой классический препроцессор как «абстрактный медиатор стилей» для доставки дизайн-констант и прочей стилевой абстракции, и сам компонентный фреймворк для, простите за сплошную тавтологию — аккуратной декларативной компонентности — вам все это не зайдет, наверняка. Например, попытки посадить Styled Components на дизайн-константы приводят к безобразному коду в таких «стилях»... Или утилиты «ишак вижу — ищак пою» «в стиле Taiwind» вполне способны представлять «атомы», но все равно ограничивают гибкость в случае непрогнозируемых и частых изменений, ну или просто — также выглядят излишне-невменяемо уже на самой разметке. Я встречал проекты где прикручено «вообще сразу все» — препроцессор, CSS-модули и Styled Components, Flow к зачем-то “отключенному” CRA, например — и это уже даже нельзя назвать «оверинжинирингом», так как это просто отвратительная глупость и очевидно плохая архитектура. Возможно, раскаяние и отрезвление наступит когда вы опять превысите сроки и бюджет на очередном проекте в пять, да, Карл, пять раз — и такое бывает сплошь и рядом. И/или — дизайнеры, по просьбе клиента которому никак нельзя отказать в очередной раз «все переделают» в макетах, которые «уже сверстаны»…

Мотивация

Давайте, прежде всего, четко обрисуем проблему которую мы собираемся решать. В реальном мире, в очень многих командах — проектирование практически никак не «дружит» с разработкой, общаясь, по сути — только через конечные «макеты». Даже действительно опытные «фуллы», которых бизнес предпочитает нанимать «и в качестве фронтендеров», на самом то деле, верстают совсем слабо. Кодеры сдают только им самим известно из какого гавна как собранные страницы на тестирование, которое, в свою очередь — тупо сравнивает их с макетами — замыкая порочный круг. Обычно и, тем более, в условиях жестких дедлайнов и/или в разношерстных, наспех собранных командах из специалистов с совершенно разными скилами и опытом — бывает очень сложно ввести соглашения. Общий фокус процесса неминуемо смещается на то чтобы «сделать хоть как-нибудь», «протолкнув» быстрый гавнокод через тестировщиков…

Советский плакат
Советский плакат

Такая организация работы как раз очень выгодна мракоделам. Мракодел отгораживается «только своей задачей» — «моя хата скраю» — и пишет ее так, как ему удобно, игнорируя best practices и напряжные соглашения, часто вообще — просто изображая работу удаленно — скопировал, чутка перековырял под макеты — ушел гулять, вечером отправил — ждет когда тестировщики обязательно и несколько раз вернут на доработку… «Есть баги — есть работа!». «Переиспользование» превращается в простое копирование и «интуитивную» модификацию «уже готовых кусков» — лавинообразно усугубляя мрачность и запутанность стилей, разметки и js-обвязок для них. И если javascript бывает еще более-менее адекватен, все его «умеют» и понимают — то стили часто превращаются в просто непроходимые дебри и бесконечную излишнюю неоптимизированную колбасу, несмотря на то какие именно технологии используются… При этом руководство, менеджеры которые не ходят читать репо могут пребывать в наивной уверенности что «все готово, только кнопку нажать» или хотя бы — «вот это уже сделано на том проекте и можно переиспользовать»... Мне приходилось получать репо с тонной «индусского» — нереально небрежного и явно копированного целыми крупными кусками и даже файлами кода, игнорирующего вполне имеющийся у фирмы единый UI-кит, его константы и компонентность, с посылом от руководства что «все готово», «надо слегка переписать»… Мне приходилось годами исправлять за «тимлидами-сеньорами» проекты с ужасной архитектурой блокирующей быструю доставку именно того, что нужно заказчику, например — сложного современного дизайна… Причем, когда проект начинали — самые важные требования остались проигнорированы, зато с совершенно ненужными в этом случае «модными трендами» покуражились вовсю...

Я заметил что с молодыми специалистами часто работать и добиваться качественного результата бывает намного проще чем с закостенелыми самоуверенными «фуллстеками», рефлексующими свою недооцененность и прочее… Ребята помоложе еще не потеряли азарт и интерес к тому что делают, не успели несколько раз перегореть, устать, и большинство как раз стремиться стать настоящими профессионалами, открыты новым идеям, подходам, ищут и перенимают хороший стиль, адекватно реагируют на критику и так далее.. Хотя бывают исключения и с теми, и с другими, конечно… Но хватить абстрактной лиричной боли — перейдем уже к «хорошим практикам по Гамбаряну»…

Советский плакат
Советский плакат

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

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

Советский плакат
Советский плакат

Программисты такие же ленивые, хоть и мыслящие, животные как и все другие люди и коты. Да, мне кажется, что мой полосатый много думает и иногда даже пытается поделиться... Вот даже котик пытается рассказать о результатах своего умственного труда? А многие из вас не то что никогда не оставляют комментариев — в принципе — пишут такой код что я бы сразу уволил как это развидеть, к которому очевидно любые комментарии излишни, так скажем. Причем, причины такого состояния кода множества проектов всегда одни и те же: «времени совсем не было», «надо было что-то показать», «руководство попросило сделать очень быстро» и тому подобное, «будет время — перепишем»… Поверьте, в реальной коммерции — оплаченного отдельного времени на оптимизацию и рефакторинг практически никогда не бывает. Клиенты в аутсорсе, например, почти никогда не покупают его как дополнительную опцию. Даже если некий продукт очевидно неаккуратно написан, работает со сбоями и совершенно не годен для масштабирования — бизнес практически всегда будет заинтересован, прежде всего, в доставке нового функционала любой ценой, а не в исправлении «уже проданного» — и все что он действительно захочет, чаще всего, это опять — быстро — «как-нибудь подлатать все баги»… Главная задача команды разработчиков в этом смысле — прямо на старте выработать четкую систему приемлемых и удобных для всех участников соглашений, которая с самого начала позволит писать максимально консистентный, понятный, и, самое главное, в идеале — и гибкий, и, одновременно, железно надежный код.

Среднестатистический разработчик-мракодел, тянущий лямку в капитализме никогда не возвращается к своему поспешному или даже откровенно, простите мой спесишизм, «индусскому» копипасте-коду для того чтобы улучшить его, кроме ситуаций, когда служба контроля качества вернула некий конкретный баг на доработку. И, конечно, тут совсем нельзя говорить о каком-либо структурном улучшении, так как подобные правки всегда носят не системный, а полностью локальный-частный характер «быстрых фиксов», «кряков» и только усугубляют беспорядок. Не дай бог трогать код по «уже закрытым таскам», «так ведь можно что-нибудь поломать!»... Среднестатистический разработчик-мракодел нацелен, прежде всего, на закрытие своих тасков-багов по трекеру и не интересуются общим состоянием проекта, приходящим чужим кодом. Полезные соглашения его напрягают, он игнорирует или даже активно противиться им. Кроме того, я заметил, что профессиональные мракоделы в командах и на проектах стремятся объединяться в устойчивые группы… Мракоделы копируют мракокод из файла в файл, из проекта в проект, покрывая друг-друга, ломая волю ПМов и регулярно рапортуя через них начальству которое не читает репо что «все почти готово, только кнопку нажать»... Мракоделы даже иногда дорастают до сеньоров и лидов. Тогда они начинают занимаются сомнительным оверинжинирингом, накручивая ненужные, но модные технологии, пока не оказывается что все сроки сорваны и клиент не получил того чего хотел, а код такой что заплатки негде ставить без поллитры не прочитать… Как простому верстальщику противостоять вселенскому мраку?

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

  • Все оформление вплоть до перекастомизаций необходимых сторонних модулей неукоснительно основано на константах-«атомах» и уже собираемых на их основе более сложных паттернах которые диктует ваш дизайн, руководство по стилю.

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

  • Даже на «отдельном проекте» нужно стараться организовывать компонентную архитектуру таким образом, как будто ваши @/src/components — находятся в удаленном репозитории — «UI-библиотеке», и, поэтому — недоступны для «обвязок» напрямую. Компоненты «реальных вьюх» [соответствующие дизайн-макетам], например на @/src/views во Vue или @/src/pages и @/src/layouts с Nuxt —должны взаимодействовать с UI-компонентами которые они просто «собирают, предоставляя данные» — исключительно через пропсы — через явно обозначенный интерфейс, а не «состоянием». Концептуально, это напоминает нам «инкапсуляцию» из «серьезного программирования», а практически будет приводить к тому что — стили за редким исключением будут сосредоточены практически только «в UI-компонентах», а вся бизнес-логика — совершенно точно исключительно на видах. Это еще и способно организовать работу более эффективно — участники «с хорошей версткой» могут, прежде всего — поставлять качественную разметку.

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

Советский плакат
Советский плакат

Антибиблиотека

UI Library Starter это точно не еще одна библиотека «готового на все UI». Это, прежде всего, концепт и пример системного подхода, которые могут позволить вам легко поддерживать свои собственные библиотеки только из необходимых компонент, чистого оптимального кода, сопровождаемого необходимой наглядной документацией и с оформлением точно согласующимся с руководством по стилю. Проект содержит некоторый минимальный набор готовых компонент именно в качестве примера. Это то, что встречается практически в любом большом интерфейсе: простые, базовые примеры — обертка для контента, сетки, и пара важных «дорогих» контролов: датапикер с диапазоном на одном листе и кварталами, обертка над сторонним селектом...

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

В этом смысле сама идея UI Library Starter противоположна концепции любой обычной популярной библиотеки где «все включено». Она не в том чтобы дать вам возможность лениться, бояться и тратить рабочее время неэффективно мучаясь трудными кастомизациями через !important с неизвестным результатом, другими адовыми кряками, превращая свои проекты в неведомы зверушки из скрытых под капотом чужих кривых поделок, нечитаемые лоскутные одеяла повторяющегося кода или запутанные описания одного и того же совсем по-разному, с позорно огромным опасным пулом зависимостей. Идея этого проекта в том, чтобы аккуратно помочь вам начать писать действительно оптимальный, понятный и задокументированный, гибкий и легко масштабируемый код, который вы сможете постоянно переиспользовать и улучшать.

Пример дочернего проекта использующего модуль-библиотеку.

Далее — простите мне странные вкрапления моего никакого английского в документации…)))

Getting Started

Installation

Скачайте код ui-library-starter и оформите его в отдельный репозиторий. При выборе имени для нового репозитория необходимо сразу убедиться в том, что оно не занято на npmjs.com. Пусть это будет ui-library-starter-test.

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

$ npm install

Customization

README.md

Поправьте первую строчку в @/README.md:

# Ui-library-starter test project

package.json

Далее в @/package.json вам необходимо крайне аккуратно переписать актуальной информацией следующие поля, ничего не пропустив:

{
  "name": "ui-library-starter-test",
  "description": "UI Library Starter Demonstration",
  "version": "0.1.0",

  "main": "dist/ui-library-starter-test.umd.min.js",
  "unpkg": "dist/ui-library-starter-test.umd.min.js",
  "jsdelivr": "dist/ui-library-starter-test.umd.min.js",

  "scripts": {
    "build": "rimraf ./src/static && cp -r ./docs/.vuepress/public ./src/static && vue-cli-service build --target lib --name ui-library-starter-test src/main.js"
  },

  "author": "Levon Gambaryan",
  "license": "MIT",

  "homepage": "",
  "repository": {
    "type": "git",
    "url": "https://github.com/ushliypakostnik/ui-library-starter-test"
  },
  "bugs": {
    "url": "https://github.com/ushliypakostnik/ui-library-starter-test/issues"
  },

  "keywords": []
}

Обратите внимание на имя проекта в конце длинной команды деплоя build!

Documentation config

Перейдите к документации на VuePress и сконфигурируйте ее под себя @/docs/.vuepress/config.js:

module.exports = {
  locales: {
    '/': {
      lang: 'en-US',
      title: 'UI Library',
      description: 'Vue Component UI Library',
    },
  },

  themeConfig: {
    repoLabel: 'GitHub repo',
    repo: 'https://github.com/ushliypakostnik/ui-library-starter-test.git',
    docsDir: 'docs',
    search: false,
    locales: {
      '/': {
        nav: [{ text: '', link: '' }],
        sidebar: [
          {
            title: `Components`,
            children: [
              // ... готовые компоненты библиотеки без Sandbox и папки /Tests с тестовыми 
            ],
          },
          {
            title: `Sandbox`,
            path: '/sandbox/sandbox',
          },
        ],
      },
    },
  },
};

Connecting fonts

Перепишите имя шрифта и переменные начертаний если требуется в файле ~/src/stylus/utils/_typography.styl:

$font-family = "Open Sans"

$font-weight = {
  regular: 400,
  bold: 700,
}

Поместите папку с правильным шрифтом рядом с папкой /Ubuntu в @/docs/.vuepress/public/fonts/.

Пропишите правильные импорты и пути в файле кастомизации документации на VuePress @/docs/.vuepress/styles/palette.styl/:

@import "../../../src/stylus/_stylebase.styl"

// Import fonts
//////////////////////////////////////////////////////

@font-face {
  font-family: $font-family;
  src: url('/fonts/OpenSans/OpenSans-Regular.eot');
  src: local('Open Sans Regular'), local('OpenSans-Regular'),
    url('/fonts/OpenSans/OpenSans-Regular.eot?#iefix') format('embedded-opentype'),
    url('/fonts/OpenSans/OpenSans-Regular.woff') format('woff'),
    url('/fonts/OpenSans/OpenSans-Regular.ttf') format('truetype');
  font-weight: $font-weight.regular;
  font-style: normal;
}

@font-face {
  font-family: $font-family;
  src: url('/fonts/OpenSans/OpenSans-Bold.eot');
  src: local('Open Sans Bold'), local('OpenSans-Bold'),
    url('/fonts/OpenSans/OpenSans-Bold.eot?#iefix') format('embedded-opentype'),
    url('/fonts/OpenSans/OpenSans-Bold.woff') format('woff'),
    url('/fonts/OpenSans/OpenSans-Bold.ttf') format('truetype');
  font-weight: $font-weight.bold;
  font-style: normal;
}

// Customization
//////////////////////////////////////////////////////

...

Удалите директорию со старым шрифтом @/docs/.vuepress/public/fonts/Ubuntu.

Сleaning project

Если вы хотите получить полностью чистую документацию — произведите следующую очистку папок и файлов.

Удалите все папки и файлы в @/docs/ кроме директорий @/docs/.vuepress@/docs/components и @/docs/sandbox — если желаете оставить Песочницу. И файла @/docs/README.md, который нужно оставить, но очистить:

# UI Library

...

Удалите директорию @/src/components/tests.

Вычистите импорты тестовых компонент в индексном файле @/src/components/index.js:

// Tests - следующие три строчки удалить!!!
export { default as TestColors } from './tests/TestColors';
export { default as TestBreakpoints } from './tests/TestBreakpoints';
export { default as TestTypography } from './tests/TestTypography';

// Elements
...

Вы можете выбрать какие компоненты оставить или даже удалить их все, если уверенны в себе и не нуждаетесь в наглядных примерах под рукой. Вернитесь к конфигурации документации и отразите изменения в @/docs/.vuepress/config.js.

Style setting

Запустите разработку документации командой:

$ npm run docs:dev

Прочитайте раздел Constants этой документации.

Вам необходимо настроить препроцессор вашей библиотеки в точном соответствии с вашим руководством по фирменному стилю.

Adding a component

После того как стили библиотеки настроены вы можете добавлять свои специфические компоненты.

Выберете имя для компонента в PascalCase стиле написания, предположим это ComponentName.

Некоторые имена могут оказаться зарезервированы VuePress. Layout, например. Самая достойная замена Layout видится как View.

Добавьте директорию @/src/components/ComponentName.

Добавьте в нее индексный файл c импортом-экспортом @/src/components/ComponentName/index.js:

import ComponentName from './ComponentName.vue';

export default ComponentName;

И сам компонент @/src/components/ComponentName/ComponentName.vue:

<template>
  <div
    class="component-name"
    :class="{
      '.component-name__element--modifier1': prop1,
      '.component-name__element--modifier2': prop2,
    }"
  >
    This is test component!!!
  </div>
</template>

<script>
export default {
  name: 'ComponentName',

  props: {
    prop1: {
      type: Boolean,
      required: true,
    },
    prop2: {
      type: Boolean,
      required: false,
      default: false,
    },
  },
};
</script>

<style lang="stylus" scoped>
@import "~/src/stylus/_stylebase.styl";

.component-name
  background $colors.primary // test styles
  // add adaptive
  +$mobile()
    display block

  &__element
    $text("natasha") // add typography

    &--modifier1
      color $colors.primary // add good color

    &--modifier2
      color $colors.secondary
</style>

Добавьте экспорт в индексный файл библиотеки @/src/components/index.js:

export { default as ComponentName } from './ComponentName';

Добавьте документацию компонента в файл @/docs/components/component-name.md:

# ComponentName

## Description

This is new custom component.

## Connection

```vue
<template>
  <ComponentName prop1 />
</template>
```

## Render

<ComponentName prop1 />

## API

### Props

| **Name**  | **Type** | **Description** | **Default** |
| :-------- | :------- | :-------------- | ----------: |
| **prop1** | Boolean  | -               |  (required) |
| **prop2** | Boolean  | -               |     `false` |

## Source code

<code>@/src/components/ComponentName/ComponentName.vue</code>

<<< @/src/components/ComponentName/ComponentName.vue

И далее — рендер-тест и исходный код по аналогии с другими файлами.

Добавьте компонент в конфигурацию VuePress @/docs/.vuepress/config.js:

module.exports = {
  themeConfig: {
    locales: {
      '/': {
        sidebar: [
          {
            title: `Components`,
            children: [
              {
                title: `ComponentName`,
                path: '/components/component-name',
              },
            ],
          },
        ],
      },
    },
  },
};

Using third party modules

Используйте только относительные пути для импорта чего-либо в javascript ваших компонентов. Не используйте «абсолютные» алиасы:

<script>
import { dateFilter } from '../../../node_modules/vue-date-fns/src/index';

import Icon from '../Icon/Icon';

export default {
  components: {
    Icon,
  },
};
</script>

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

Установите и импортируйте модуль как обычно в главном файле @/src/main.js:

import vSelect from 'vue-select';
import 'vue-select/dist/vue-select.css';

Vue.component('v-select', vSelect);

Так как мы используем глобальные стили собственной кастомизации модуля — невозможно будет защитить стили перекастомизации в SFC-обертке с помощью scoped:

<style lang="stylus">
@import "~/src/stylus/_stylebase.styl";

.vs
  &__dropdown-toggle
    // ...

  // ...

// ...
</style>

Use the sandbox

Используйте специальный компонент @/src/components/Sandbox/Sandbox.vue и роут документации Sandbox как экспериментальную площадку и холст для создания новых компонент на простых мокках или тестирования взаимодействия между ними. Хотя, очевидно, некоторые компоненты, такие как, например, лейаут — удобнее создавать непосредственно в проекте и уже после этого переносить в библиотеку.

Library publishing

Зарегистрируйтесь на npmjs.com и подтвердите регистрацию (дождитесь письма на почту).

$ npm run build
$ npm version patch
$ npm publish

Connecting to projects

Вы можете либо использовать стартовый шаблон для новых проектов ui-library-start, тогда вам придется заменить библиотеку:

$ npm uninstall ui-library-starter --save-dev
$ npm install ui-library-starter-test --save-dev

Либо установить библиотеку как любой другой модуль в любой другой проект:

$ npm install ui-library-starter-test --save-dev

Организация стилей дочерних проектов может или иметь подобную библиотеке структуру или любую другую (например, если вы внедряете бибилиотеку в старый проект). Единственное требование: первый импорт в основном файле — основного файла библиотеки. Второй — подключение шрифтов и стилизация :root и body.

@/src/stylus/_stylebase.styl проекта использующего библиотеку:

// Import UI Library stylebase
@import '~ui-library-starter-test/src/stylus/_stylebase.styl';

// core
@import "core/_base"; // normalize

@/src/stylus/core/_base.styl проекта использующего библиотеку:

// Import UI Library fonts

@font-face {
  font-family: $font-family;
  src: url('~ui-library-starter-test/src/static/fonts/OpenSans/OpenSans-Regular.eot');
  src: local('Open Sans Regular'), local('OpenSans-Regular'),
    url('~ui-library-starter-test/src/static/fonts/OpenSans/OpenSans-Regular.eot?#iefix') format('embedded-opentype'),
    url('~ui-library-starter-test/src/static/fonts/OpenSans/OpenSans-Regular.woff') format('woff'),
    url('~ui-library-starter-test/src/static/fonts/OpenSans/OpenSans-Regular.ttf') format('truetype');
  font-weight: $font-weight.regular;
  font-style: normal;
}

@font-face {
  font-family: $font-family;
  src: url('~ui-library-starter-test/src/static/fonts/OpenSans/OpenSans-Bold.eot');
  src: local('Open Sans Bold'), local('OpenSans-Bold'),
    url('~ui-library-starter-test/src/static/fonts/OpenSans/OpenSans-Bold.eot?#iefix') format('embedded-opentype'),
    url('~ui-library-starter-test/src/static/fonts/OpenSans/OpenSans-Bold.woff') format('woff'),
    url('~ui-library-starter-test/src/static/fonts/OpenSans/OpenSans-Bold.ttf') format('truetype');
  font-weight: $font-weight.bold;
  font-style: bold;
}


// Base normalize

:root
  scroll-behavior smooth

body
  font-family $font-family, sans-serif
  -moz-osx-font-smoothing grayscale
  -webkit-font-smoothing antialiased
  text-rendering: optimizeSpeed
  color $colors.text
  overflow-x hidden

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

Подключите все это к главному шаблону @/src/App.vue:

<template>
  <div id="app"></div>
</template>

<script>
export default {
  name: 'App',
};
</script>

<style lang="stylus">
@import "~/src/stylus/_stylebase.styl";
</style>

Исправьте имя библиотеки в импортах в точку входа @/src/main.js если вы брали готовый репо или подключите:

import ComponentLibrary from 'ui-library-starter-test';
import 'ui-library-starter-test/dist/ui-library-starter-test.css';

Vue.use(ComponentLibrary);

Исправьте имя или добавьте команду update в @/package.json:

{
  "name": "ui-library-start-test",
  "scripts": {
    "update": "npm install ui-library-starter-test@latest"
  },
}

Updating in projects

Обновляйте библиотеку до последней версии в проектах:

$ npm run update

Constants

_stylebase.styl

Глобальный медиатор стилей собирает не компилируемые /utils, компилируемые /core сущности и необходимые глобально кастомизации используемых сторонних модулей /libs (но, те которые позволяют это сделать без scoped— стоит разместить в SFC-обертках).

@/src/stylus/_stylebase.styl библиотеки:

// utils
@import "utils/_variables";
@import "utils/_placeholders";
@import "utils/_mixins";
@import "utils/_typography";

// core
@import "core/_base"; // normalize
@import "core/_animations";

// libs
...

Теперь можно использовать всю эту кухню на компонентах:

<style lang="stylus" scoped>
@import "~/src/stylus/_stylebase.styl";

...
</style>

Это можно назвать «глобальными стилями-невидимками», что-то такое — они, с одной стороны — участвуют в правильном оформлении везде, но, при этом, «их не видно». Мы предоставляем глобальные константы гайдлайна и всю прочую мощь препроцессора всем компонентным системам — библиотеке и всем ее «читателям».

Mixins and placeholders

UI Library Starter дает надежду на то, что в вашей разметке код будет полностью оптимальный и консистентный. Это способно сделать даже крупную систему из нескольких проектов базирующихся на одном визуальном языке — и полностью управляемой, и, в тоже время — гибкой.

Не копируйте код кусками по компонентам — оптимизируйте очевидно одинаковые наборы в миксинах и плейсхолдерах!

Или вы можете делать примеси без параметров для того, чтобы — забегая вперед (см. раздел Breakpoints) — такой же набор стал доступен внутри медиа-миксинов @/src/stylus/utils/_mixins.styl:

// Utilities
//////////////////////////////////////////////////////
// Mixins without arguments duplicate placeholders,
// so it can be passed to media mixins

$flex--center()
  display flex
  align-items center
  justify-content center

Теперь:

.selector
  +$tablet()
    $flex--center()

Colors

Абстрагируйте все цвета из гайдлайна в короткие имена-маркеры.

~/src/stylus/utils/_variables.styl:

// Palette
//////////////////////////////////////////////////////
$colors = {
  cat: #515bd4,
  dog: #ffffff,
  bird: #fd5f00,
  wood: #fed564,
  stone: #8bc24c,
  sea: #13334c,
}
// Dependencies colors
$colors["text"] = $colors.sea
$colors["placeholder"] = rgba($colors.sea, $opacites.rock)
$colors["primary"] = $colors.cat
$colors["secondary"] = $colors.dog

В любом месте кода препроцессора или секции стилей SFC (при условии импорта стилевой базы) библиотеки или дочерних проектов вы можете передавать правильные цвета:

.selector
  color $colors.primary

Легко поддерживать тестовый компонент наглядно демонстрирующий палитру в @/src/components/tests/TestColors/TestColors.vue.

Breakpoints

Переменные-брекпоинты лучше называть более интуитивно-понятно.

~/src/stylus/utils/_variables.styl:

// Breakpoints
//////////////////////////////////////////////////////

$breakpoints = {
  tablet: 768px,
  desktop: 1025px,
}
$breakpoints["mobile--max"] = $breakpoints.tablet - 1
$breakpoints["tablet--max"] = $breakpoints.desktop - 1

Основные точки перехода: в стилевой базе препроцессора в px и в константах скриптов библиотеки в Number — должны соответствовать друг-другу.

@/src/utils/сonstants.js:

// Design constants
//////////////////////////////////////////////////////

export const DESIGN = {
  BREAKPOINTS: {
    tablet: 768,
    desktop: 1025,
  },
};

В препроцессоре — мощнейшее, очень удобное средство — построенные на брекпоинтах примеси принимающие контент:

// Media
//////////////////////////////////////////////////////

$no-gadgets()
  @media only screen and (max-width $breakpoints.desktop--max)
    {block}

$desktop()
  @media only screen and (min-width $breakpoints.desktop)
    {block}

$gadgets()
  @media only screen and (max-width $breakpoints.tablet--max)
    {block}

$tablet()
  @media only screen and (min-width $breakpoints.tablet) and (max-width $breakpoints.tablet--max)
    {block}

$not-mobile()
  @media only screen and (min-width $breakpoints.tablet)
    {block}

$mobile()
  @media only screen and (max-width $breakpoints.mobile--max)
    {block}

Использование в любом блоке стилей SFC:

.selector
  +$desktop()
    // styles only for desktops

  +$tablet()
    // styles only for tablet

  +$mobile()
    // styles only for mobile

В строгой традиции запрещается использование любых глобальных классов со стилями, за исключением анимаций для Vue и вынужденных кастомизаций действительно необходимых сторонних модулей где «классический ад с !important»))). Мы стараемся минимизировать количество зависимостей и «точечно» закрываем самые «дорогие», неподъемные по ресурсам проблемные места.

Точки перехода скриптов обрабатываются специальным модулем-помощником для экрана через matchMedia:

// Viewport utils module
//////////////////////////////////////////////////////

import { DESIGN } from './constants.js';

const ScreenHelper = (() => {
  const TABLET = DESIGN.BREAKPOINTS.tablet;
  const DESKTOP = DESIGN.BREAKPOINTS.desktop;

  const isMobile = () => {
    return window.matchMedia(`(max-width: ${TABLET - 1}px)`).matches;
  };

  const isTablet = () => {
    return window.matchMedia(
      `(min-width: ${TABLET}px) and (max-width: ${DESKTOP - 1}px)`,
    ).matches;
  };

  const isDesktop = () => {
    return window.matchMedia(`(min-width: ${DESKTOP}px)`).matches;
  };

  const getOrientation = () => {
    if (window.matchMedia('(orientation: portrait)').matches) {
      return 'portrait';
    }
    return 'landscape';
  };

  const getPixelRatio = () => {
    // eslint-disable-next-line prettier/prettier
    return (
      window.devicePixelRatio ||
      window.screen.deviceXDPI / window.screen.logicalXDPI
    );
  };

  return {
    isMobile,
    isTablet,
    isDesktop,
    getOrientation,
    getPixelRatio,
  };
})();

export default ScreenHelper;

Для того чтобы компоненты могли всегда верно определять типоразмер устройства предоставлена общая функциональность обновляющая переменные в событии ресайза. Этот миксин может быть невероятно полезен и на этапе конечной сборки адаптивных видов — в дочерних проектах.

@/src/mixins/resize.js:

import ScreenHelper from '../utils/screen-helper.js';

export default {
  data() {
    return {
      isMobile: null,
      isTablet: null,
      isDesktop: null,
    };
  },

  mounted() {
    window.addEventListener('resize', this.onWindowResizeCommon, false);
    this.onWindowResizeCommon();
  },

  beforeDestroy() {
    window.removeEventListener('resize', this.onWindowResizeCommon, false);
  },

  methods: {
    onWindowResizeCommon() {
      // console.log('onWindowResizeCommon!!!!');

      this.isMobile = ScreenHelper.isMobile();
      this.isTablet = ScreenHelper.isTablet();
      this.isDesktop = ScreenHelper.isDesktop();
    },
  },
};

На любых компонентах или видах в библиотеке:

<template>
  <Component v-show="isDesktop" />
  
  <div v-if="isDesktop" />
</template>

<script>
import resize from '../../../src/mixins/resize.js';

export default {
  name: 'ComponentName',

  mixins: [resize],
};
</script>

В проектах:

<script>
import resize from 'ui-library-starter/src/mixins/resize.js';

export default {
  name: 'Test',

  mixins: [resize],
};
</script>

Тестовый компонент в @/src/components/Tests/TestBreakpoints/TestBreakpoints.vue.

Typography

Абстрагируйте все гарнитуры из гайдлайна в короткие имена-маркеры.

~/src/stylus/utils/_typography.styl:

// Typography
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////

// Typographic Variables
//////////////////////////////////////////////////////

// Guide

$font-family = "Ubuntu"

$font-weight = {
  regular: 400,
  bold: 700,
}

$letter-spacing = {
  normal: normal
}

// Universal Typographic Mixin
//////////////////////////////////////////////////////

// We use one, only one, Karl, a universal mixin for all cases!

$text($name)
  font-family $font-family
  letter-spacing $letter-spacing.normal

  if $name == "elena"
    font-size 72px
    line-height 72px
    font-weight $font-weight.bold
  if $name == "olga"
    font-size 56px
    line-height 56px
    font-weight $font-weight.bold
  if $name == "anna"
    font-size 40px
    line-height 44px
    font-weight $font-weight.bold
  if $name == "maria"
    font-size 32px
    line-height 36px
    font-weight $font-weight.bold
  if $name == "katya"
    font-size 24px
    line-height 28px
    font-weight $font-weight.regular
  if $name == "alena"
    font-size 20px
    line-height 28px
    font-weight $font-weight.regular
  if $name == "natasha"
    font-size 16px
    line-height 28px
    font-weight $font-weight.regular
  if $name == "nina"
    font-size 13px
    line-height 16px
    font-weight $font-weight.regular

Others

~/src/stylus/utils/_variables.styl:

// Others from guide
//////////////////////////////////////////////////////

$border-radiuses = {
  soccer: 0,
  dancing: 2px,
  swimming: 3px,
  shooting: 10px,
}

$opacites = {
  waltz: 1,
  funky: 0.75,
  rock: 0.66,
  psy: 0.45,
  pop: 0.2,
  reggae: 0,
}

$effects = {
  duration: 0.1s,
}

Можно получить из этого миксины для более лаконичного синтаксиса ~/src/stylus/utils/_mixins.styl:

// Rounding
//////////////////////////////////////////////////////

$border-radius($name)
  if $name == "soccer"
    border-radius $border-radiuses.soccer // 0
  if $name == "dancing"
    border-radius $border-radiuses.dancing // 2px
  if $name == "swimming"
    border-radius $border-radiuses.swimming // 3px
  if $name == "shooting"
    border-radius $border-radiuses.shooting // 10px


// Opacity
//////////////////////////////////////////////////////

$opacity($name)
  if $name == "waltz"
    opacity $opacites.waltz // 1
  if $name == "funky"
    opacity $opacites.funky // 0.75
  if $name == "rock"
    opacity $opacites.rock // 0.66
  if $name == "psy"
    opacity $opacites.psy // 0.45
  if $name == "pop"
    opacity $opacites.pop // 0.2
  if $name == "reggae"
    opacity $opacites.reggae // 0

Теперь можно:

.selector
  $opacity("dancing")
  $border-radius("funky")

Переменные все равно могут пригодиться если дизайнеры захотят сделать новый цвет с разрешенной прозрачностью:

.selector
  color rgba($colors.dog, $opacites.psy)

Анимации

Единственные глобально компилируемые стилевые классы которые в строгой традиции разрешено использовать — для анимаций Vue. Вы можете добавлять их после соответсвующих @keyframes в специальном файле стилевой базы ~/src/stylus/core/_animation.styl:

// Project animations
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////
//////////////////////////////////////////////////////

// Keyframes
//////////////////////////////////////////////////////

@keyframes fade
  0%
    opacity 0

  100%
    opacity 1


// Vue classes for animation
//////////////////////////////////////////////////////

.fade
  &-enter-active
    animation fade ($effects.duration * 2) forwards

  &-leave-active
    animation fade ($effects.duration * 2) reverse

Теперь в разметке:

<template>
  <transition name="fade">
    <div v-if="Boolean" />
  </transition>
</template>

Structure

.
├─ docs/ // documentation folder
│  ├── .vuepress/ // VuePress
│  │   ├─ stylus/ // import of fonts and customization of documentation
│  │   │  └─ palette.styl
│  │   ├─ config.js // configuration
│  │   └─ enhanceApp.js // improvements - installation of library components
│  ├─ components/ // components documentation folder
│  │  ├─ link.md
│  │  └─ ...
│  ├─ constants/ // library documentation folder
│  │  ├─ breakpoints.md
│  │  ├─ colors.md
│  │  ├─ others.md
│  │  ├─ stylebase.md
│  │  └─ typography.md
│  ├─ sandbox/ // sandbox view
│  │  └─ sandbox.md
│  ├─ links.md // useful reading links
│  ├─ README.md // homepage
│  ├─ start.md // getting started
│  └─ structure.md // structure
├─ src/ // source folder
│  ├─ components/
│  │  ├─ Icon
│  │  │  ├─ index.js
│  │  │  └─ Icon.vue
│  │  ├─ Sandbox
│  │  │  ├─ index.js
│  │  │  └─ Sandbox.vue
│  │  ├─ Tests
│  │  │  └─ ...
│  │  └─ ...
│  ├─ mixins/
│  │  ├─ resize.js // adaptive to components
│  │  └─ ...
│  ├─ static/ // after build fonts will be copied here
│  │  └─ fonts/
│  │     └─ ...
│  ├─ stylus/
│  │  ├─ core
│  │  │  ├─ _animations.styl // keyframes and Vue animationss classes
│  │  │  └─ _base.styl // normalize
│  │  ├─ utils
│  │  │  ├─ _mixins.styl
│  │  │  ├─ _placeholders.styl
│  │  │  ├─ _typography.styl // Use one, only one, Karl, a universal mixin for all cases!
│  │  │  └─ _variables.styl
│  │  └─ _stylebase.styl // main file of stylus 
│  ├─ utils/ // scripts
│  │  ├─ constants.js // javascript constants
│  │  ├─ screen-helper.js // adaptive viewport
│  │  └─ ...
│  └─ main.js // library connection
├─ .browserslistrc // configuration of supported browsers
├─ .eslintrc.js // linter configuration
├─ .gitignore // git ignore
├─ .prettierrc // prettier configuration
├─ babel.config.js // babel configuration
├─ colors.jpg // image for README
├─ package.json // project configuration
└─ README.md
```

Мы сегодня многое поняли

Чистый код начинается с четких концепций и годной архитектуры, эффективная работа команды — с аккуратной коммуникации и внятных соглашений. Я потратил всего один вечер для того чтобы написать сам пример модуля-библиотеки на Vue со Stylus (без компонент). Точно тоже самое можно быстро легко осуществить для связки любого современного компонентного фреймворка и препроцессора. Даже если вы не хотите, или по каким-то внешним причинам — не можете — выделить атомарный и подробно-компонентный UI своего интерфейса в модуль-библиотеку, изложенные выше подходы все равно способны помочь вам и вашей команде писать действительно красивые и удобные проекты фронтенда с по-настоящему качественной версткой.

Удачи в написании собственных библиотек!

Советский плакат
Советский плакат

@@

Комментарии (13)


  1. VanishMax
    11.10.2021 08:29
    +4

    Одно дело хорошо верстать, другое – качественно создавать архитектуру библиотеки. Важно задумываться о том, что поставляешь пользователю библиотеки, и поэтому vue-cli-service совсем не подходит. В вашем случае было бы уместно обернуть проект в Rollup и настроить оптимизацию и минификацию, выпилить external dependencies из бандла. В конце концов, ни в коем случае нельзя использовать Moment JS, особенно вместе с Date-fns. Во-первых, они делают одно и то же, так что вы просто дублируете код. А во-вторых, просто зайдите в readme moment'а и сами все поймете.

    Также хорошая библиотека будет использовать современные инструменты ради улучшения Developer Experience: Vue 3 с composition API для быстродействия, TypeScript для читаемости и документации на уровне кода, Vite для одновременно и бандлинга и настройки демо-проектов.

    Иконки в виде .vue компонентов – тоже вредно. Вероятнее всего, у пользователей библиотеки есть свои .svg- иконки, и они захотят прокинуть их в компонент через slots. Говорили о недостатках существующих компонентных библиотек, а сами на них наткнулись.

    Используйте только относительные пути для импорта чего-либо в javascript ваших компонентов. Не используйте «абсолютные» алиасы


    Зачем? Дайте хоть одно объяснение такому импорту в вашей статье. Кто-то ведь пойдет копировать такой деструктивный код.

    Непонятно, зачем вы залили library starter на npm, если это шаблон кода. В тексте статьи всю вторую половину можно было бы опустить в пользу понятного readme на гитхабе, а первую – сделать читабельной с помощью дробления на мелкие абзацы и секции.


    1. ushliypakostnik Автор
      11.10.2021 12:28

      Ого, понеслась!.. Но, прежде всего - от души спасибо за такой подробный, разгромный "не в бровь а в глаз" классически пассивно-агрессивный, но предельно предметный и вежливый ответ. Серьезно. Я вот на Хабре только камменты читаю, особенно когда такие пассивно-агрессивные вдумчивые)))... Баталии в камментах это ... призвание...)))

      Во-первых, вы, конечно, во всем практически "правы" и ваш оверинжириниг ваши актуальные познания просто блестящи, очевидно. Наверняка вы трудитесь программистом тимлидом-сеньором, поэтому мне - простому дизайнеру и верстальщику в адовом аутсорсе остается только стыдливо краснеть, мычать и отмазываться как смогу... Слишком разный, вероятно, опыт и восприятие. Само мое техническое решение решение это действительно - хоть и основанный на опыте некоторых предыдущих реальных коммерческих проектов, но быстрый пример "из того что было ..." - в принципе никак не претендующий на безукоризненную идеальность. Кроме того я рассказываю ну совсем о другом - скорее о неких "гуманитарных" идеях и вещах - навроде - "простой доставке атомарного дизайна в код с помощью "5 лет назад как похороненных" препроцессоров" - подходах, которые обычно игнорируют программисты с сухим сугубо техническим восприятием... И вот в этом смысле - вы откровенно передергиваете, но наверняка не сможете это осознать-признать.

      Про Rollup - ок, но повторюсь - статья о другом. Датапикер-диапазон на одном листе и с кварталами - был добавлен в последний момент, и это форк который, действительно - есть еще в чем улучшать, что из него выпиливать, исходник был еще сильно хуже - вот про Moment JS, особенно вместе с Date-fns - вы совершенно правы, конечно же - надо найти на это время. На самом деле, когда читаешь такие камменты, аж завидно становится - вот все эти моменты пришли с реальных проектов в которые я попадал последние годы в своем аутсорсе - на достаточно именитые фирмы... И там такое - не будем... Видимо, где-то есть спокойные продукты на которых настоящие синьеры могут спокойно заниматься себе оверинжинирингом аккуратной кастомизацией бандла и тд. Поверьте в реальном коммерческом фронтенде - в вполне даже приличных конторах - иногда идет речь о том чтобы смотивировать коллег просто даже писать компонентами на компонентном фреймворке, например... Vue cli, фейл с моментом или использование неидеального vue-select (с которым в свое время пришлось ох как повоевать) - оно на самом деле приближает пример к реальности.

      Далее продолжается совсем "выставка тщеславия". Vue 3 с composition API для быстродействия, ну а почему не Svelte тогда если о быстродействии речь? ))) Я вот его посмотрел и он мне ну прямо очень понравился именно дизайном и для дизайна, хочется что-нибудь на нем реальное написать, но наверное, все-таки не либу, в виду того что, утрируя: "датапикер под форк не найдешь"... Vue 3 начинал ковырять, и даже с TS, чтобы себя заставлять ), но вроде в доках написано что "еще не стабильно на прод"? ) Ну и вот это сказки все про ts... В реальной ситуации когда надо ну "дофига всего сверстать очень быстро и тд" - "скрипач не нужен"... Про Vite не понял про настройку демо-проектов...

      Про иконки вы категорически не правы. Как раз - сильно вреден слот, "свои или какие-то левые иконки" и какая-нибудь "подгонка viewBox" при этом. Иконки должны быть все четко нарисованы дизайнерами в UI-ките - на SVG-холстах с одинаковым размером - только тогда проблем не будет. И все равно при этом очень часто бывает нужно "пойти в SVG как в код" именно. А в контексте либы нужен аккуратный учет, "перечень" - и чтобы "в одном месте лежали". Эта придирка уже явно говорит о том у вас мало релевантного опыта именно со стороны дизайна и верстки - вы не совсем понимаете о чем я вообще пишу - ну я был готов что будут минусы от "фуллстеков" и "лидов-синьоров".)))) Я готов что статья вообще по итогу в минус улетит, но надо было выговориться и некоторые вещи показать для удобства коммуникации с потенциальными будущими коллегами.

      Про импорты... Вообще пару лет назад мы с партнером-фуллом пилили проекты для заказчика у которого вот как раз классический кейс - разные отделы пилят разное на якобы "одном фирменном стиле", но который, еще, на самом деле, не был никак формализован четко в руководство по стилю и тд и тп... Мы продали им в результате либу которая все это мерджила в единый стиль и документацию... Но сделано было так, что по некоторым выходящим за рамки этого холивара причинам в том числе и "корпоративного свойства" - либа устанавливалась и обновлялась не как npm-модуль в @/node_modules а в отдельную папку в сорцах проекта @/scr/library. Ну и при такой конструкции - в контексте проекта, данный импорт будет искать зависимость в директории @/node_modules проекта, а не библиотеки! Так как случай из реальной коммерческой корпоративной практики - значит вполне вероятен. Но расшифровывать подробность показалось излишне, переборно... А чем вообще аккуратные относительные импорты "деструктивны" то? )))

      Ну и по поводу - "зачем на npm" - прямо совсем не смешно... Там, поглядите, кроме самого "шаблона кода с доками", есть еще и получившаяся в результате тестирования "очищенная версия" https://www.npmjs.com/package/ui-library-starter-test и использующий его проект в github и на vercel: https://ui-library-start-test.vercel.app/. Занимался, знаете ли - проверял то что написал... Вообще, одной из причин писать на Хабр считаю что вот такие пассивно-агрессивные комментаторы дают отличную возможность прорабатывать рефлексию, синдром самозванца и прочие эффекты, расти и развиваться... Спасибо что комментируете!

      Пишу то я отлично, не надо - ну как "техпис" точно... Ну и правда - вы не очень поняли о чем статья... Она не о "идеальной современной технологии для реализации библиотеки", а о "способе доставки атомарного дизайна в код" и "правильной организации верстки"... Я вот последние месяцы - тружусь в корпорации с очень громким названием, но API - никак не заработает при этом, беда на бекэнде не заканчивается... Зато у них просто шикарный UI-кит... Либу делать нельзя - по "корпоративным причинам"... "Сборку" также - тупо Nuxt который по сути не нужен... Ну так я просто кинул роут @/pages/ui - и пилю там все - атомы-молекулы-организмы-шаблоны, обертки над сторонними модулями, примеры на мокках с простейшей "псевдодокументацией"...


      1. VanishMax
        11.10.2021 12:54

        Вау! Если честно, в "коммент-баталиях" еще никогда не участвовал. Благо, мы можем общаться конструктивно.

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


        1. ushliypakostnik Автор
          11.10.2021 13:13

          Ага, тогда - мир! И наверное это действительно минус моей работе что не очень четко основной мессейдж прозвучал... С другой стороны, "более лучшие модные модули" - мне все-таки не кажутся "архитектурным" моментом - вы говорите о конкретных реализациях и оптимизации. А вот то как "проектирование коммуницирует с разработчиками" или очень важный момент про "не надо обвязывать UI-компоненты напрямую" - как раз именно - архитектура. Мне вон там уже один минус прилетел за то что "статья плохо оформлена" даже - картинки для привлечения внимания не понравились, видимо... )))


          1. little-brother
            11.10.2021 18:37

            Удалите картинки-плакаты - хоть под кат (если они вам дороги), их слишком много и они большие, а также убрать сопли про то, что кругом все пропало (имхо).

            Как начинающий фронтендер честно пытался осилить, но "что-то пошло не так". возможно из-за того, что четко не обозначены "плюшки" данной простыни (стартовые цели -> что хотим получить -> как получаем -> преимущества и недостатки решения).


            1. ushliypakostnik Автор
              11.10.2021 19:01

              Спасибо за фидбек! Но "не котиками едиными" - советские плакаты тоже прекрасны, создают нужную атмосферу - как и котиков их может, должно быть много?... ))) Ну и меня вот такой стиль специфический, фриковатый! )))

              По поводу того "в чем соль": статья о том как "подружить дизайн с кодом" "не оставив шансов мракоделам которые тупо копируют как попало вьюху" и "срать хотели не интересуются тем что там у дизайнеров", "атомарный дизайн не слышали" (сейчас вот в Фигме можно "указывать атомы", например, а не только сами компоненты)... Еще когда я писал первую статью казалось "что CSS-in-JS похоронили препроцессоры" - даже если камменты там почитать - тогда адцке забурлило у многих... Но вот теперь - с атомарным дизайном - "шах и мат, программисты!"... Рано они похоронили абстракцию в стилях, короче...


        1. ushliypakostnik Автор
          26.10.2021 23:01

          Молодой человек, moment удален с версии 1.1.0! )))


  1. Geobot
    11.10.2021 20:17

    А почему выбрали stylus?


    1. ushliypakostnik Автор
      11.10.2021 20:59

      Если честно - просто потому что он на текущих проектах и можно было "просто скопипастить и переименовать атомы" ))))... Ну вот такой кодестайл как я сейчас использую еще и действительно очень аккуратно-лаконичный и "очень идет" этому остальному "совсем мало кода в компонентах" - все выглядит очень легко и изящно... ))) На самом деле, в контексте данной темы нет ну никакой разницы - суть в том, что ваш код "дружит" с "атомарностью" дизайна, а какой препроцессор и фреймворк использовать - не важно. Просто многие "тимлиды-сеньоры" - давно и плотно подсели "на иглу" унылого CSS-in-JS - что снимает с них ответственность и необходимость "якобы лишней" коммуникации с проектированием и между-собой... Ну и вообще - "верстка - удел джунов", "настоящим программистам" "это уже не интересно"... А потом вдруг - "все горит" и приходится целые репо переписывать... Потому что в реальности - существует очень много задач и интересного современного дизайна который невозможно реализовать вот так - "как угодно - копипастой", "с помощью некастомизируемых готовых дизайн-систем", "джун пишет стилевые обертки, наборы правил" и тд и тп... Я, например, могу описать реальную ситуацию "нереального дедлайна" - "огромный кастомный и очень важный сайт" - который начали "примерно когда надо было сдать" - недавно адцке вывозил - когда "подробную компонентность" - просто ну никак не успеть, хоть с препроцессором, хоть с CSS-in-JS (тем более, потому что не оптимально)... И еще вот - "целый репо стартового гавна" - и олдовые фуллы-PHP-шники "которые возомнили себя фронтами" - "еще копируют", ну и, самое главное, по классике - макеты продолжают перерисовываются всю дорогу, гаджеты приезжают только после сдачи десктопа - и тд и тп... А проект "ну очень серьезный, очень серьезные заказчики" - не будем... Так вот - в такой ситуации реально вывезти можно только "с помощью старых-добрых глобальных стилей", но оптимизированных через препроцессор - настолько насколько времени хватило, успел... Важно просто понимать что делаешь и для чего, ну и иметь реальный разнообразный опыт, пробовать разное...


  1. amakhrov
    12.10.2021 02:40

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

    Так давайте же! А то дальше несколько экранов текста про мракоделов и капитализм. Написано живо и эмоциально - что есть, то есть. Но очень уж размыто. Мысль я так и не уловил.

    Вам не трудно было бы уточнить (в одном-двух предложениях), в чем конкретно проблема, с которой боремся. Можно с примерами кода для наглядности. Спасибо!

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

    Меня не покидает мысль, что я упустил что-то основное и главное в статье, без чего пазл не складывается.


    1. ushliypakostnik Автор
      12.10.2021 03:32

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

      Наверное я все-таки плохой "теоретик", университетов не кончал, курсов не посещал, книжек не читал толком - было некогда - приходилось все время много работать... Но, кажется - "общая методика" вкратце изложена в конце раздела статьи "Разрабатывайте визуальный язык и основанные на нем шаблоны своих интерфейсов через простые четкие соглашения и документирование, а не хаотическое копирование!!!" - список из трех советов-абзацев?

      1) Проектирование/дизайн и разработка/код должна говорить на одном языке

      2) Оцениваем объем работ, производим декомпозицию задач и проектируем компонентную структуру проекта именно с точки зрения элементов этого языка - "считаем компонентами, а не макетами", если совсем тупо

      3) Отделяем бизнес-логику на видах от этого абстрактного визуального языка выраженного в виде конкретной кодовой базы - библиотеки UI, переиспользуемых компонентов. Виды предоставляют данные в компоненты через пропсы, явный интерфейс. Не обвязываем компоненты напрямую со стором состоянием. Важно!

      А далее следует не методика, а "пример простой реализации" - показывающий как элегантно можно доставить "атомарный дизайн" в код компонентного фреймворка с помощью абстракций препроцессора. CSS-in-JS реализации или тем более - "готовые дизайн-системы-фреймворки" для этого непригодны.

      Так лучше?


      1. amakhrov
        12.10.2021 19:36

        Спасибо, так понятней!

        Не вполне очевидно, почему

        CSS-in-JS реализации или тем более - "готовые дизайн-системы-фреймворки" для этого непригодны.


        1. ushliypakostnik Автор
          12.10.2021 19:48

          Достаточно сложно написать статью с примерами "ужасного кода" - так как я сам такой не пишу, а то что приходится встречать по работе - под NDA, понятно... "Изображать" - ну посмотрим - ну такая статья точно в минус улетит, разозлить тимлидов-сеньеров и прочих фуллстеков... ))) Но может быть, когда нибудь устрою и такое "неконструктивное" бурление-кипишь... )))

          Здесь есть пассаж: "Например, попытки посадить Styled Components на дизайн-константы приводят к безобразному коду в таких «стилях»... Или утилиты «ишак вижу — ищак пою» «в стиле Taiwind» вполне способны представлять «атомы», но все равно ограничивают гибкость в случае непрогнозируемых и частых изменений, ну или просто — также выглядят излишне-невменяемо уже на самой разметке."

          В первой книжке тоже есть и со ссылками - нам мои аккуратные попытки-примеры https://good-layout-book.netlify.app/start/ - глава Препроцессоры, раздел Styled Components:

          "Для полноты повествования, в теме про компонентность нельзя не вернуться к альтернативному направлению развития CSS-технологий, упомянутому в самом начале. Возможно ли реализовать некоторые из рассмотренных выше преимуществ глобального препроцессора используя CSS-in-JS подход, например, Styled Components? В некотором ограниченном виде - да, возможно.

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

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

          Очевидно, что код который в таком случае приходится писать, кажется, выглядит достаточно пугающе, неловко и неуклюже. Впрочем, и без попыток стандартизации значений у этих технологий присутствуют странности, вот, например, если нам нужно запилить картинок в бекграунд.

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

          По поводу всяких Vuetify или AntDesign - ну вообще нет сил что-то доказывать. Я много лет всем этим занимался - это несовместимо даже просто "с хорошим дизайном и версткой", не только "атомарным трендом" отличным... Если для вас это не очевидно - значит у вас нет с этим достаточного опыта, ну и, вероятно - это вам не нужно. А надо будет - поймете на собственной боли. ))