Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Архітектура атомного дизайну». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Atomic Design** - методологія побудови UI-систем через п'ять рівнів ієрархії: атоми (Button, Input), молекули (SearchForm = Input + Button), організми (Header = Logo + Nav + SearchForm), шаблони (каркаси сторінок) і сторінки (шаблони з реальним контентом). ```jsx const Button = () => <button>Знайти</button>; // атом const SearchForm = () => <form><input /><Button /></form>; // молекула const Header = () => <header><SearchForm /></header>; // організм ``` **Головне:** кожен рівень збирається з перевірених частин рівня нижче.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Atomic Design** - методологія Бреда Фроста для побудови UI-систем компонентів. Інтерфейс ділиться на п'ять рівнів ієрархії: атоми, молекули, організми, шаблони і сторінки. ## Теорія ### TL;DR - Атоми - окремі UI-елементи (кнопка, поле вводу, мітка). Молекули об'єднують їх для одного завдання (пошуковий рядок = поле + кнопка). Організми збирають секції (хедер = логотип + навігація + пошук). Шаблони - скелет макета. Сторінки наповнюють шаблон реальним контентом. - Головна відмінність від звичайної бібліотеки компонентів: будуєш знизу вгору і не можеш зібрати сторінку без перевірених дрібніших частин. - Варто використовувати, якщо команда налічує 5+ розробників/дизайнерів або UI повторюється на 10+ сторінках. - Storybook - стандартний інструмент для документування кожного рівня ізольовано. ### Швидкий приклад ```jsx // Атом: окремий елемент інтерфейсу const Button = ({ children }) => <button>{children}</button>; // Молекула: об'єднує атоми для одного завдання const SearchForm = () => ( <form> <input placeholder="Пошук..." /> <Button>Знайти</Button> </form> ); // Організм: секція зі своїх молекул і атомів const Header = () => ( <header> <SearchForm /> </header> ); // Результат: однаковий Header на кожній сторінці без дублювання коду ``` Атоми складаються в молекули. Молекули - в організми. Без дублювання, без розбіжностей між сторінками. ### П'ять рівнів **Атоми** - найменші можливі шматки UI: кнопка, поле вводу, мітка, колірний токен, стиль типографіки. Без бізнес-логіки і без думок про layout. **Молекули** групують два або більше атоми для одного завдання. Форма пошуку (поле + кнопка) - це молекула. Поле форми (мітка + input + повідомлення про помилку) - теж молекула. Одне завдання, одна одиниця. **Організми** збирають молекули й атоми в самодостатню секцію. Хедер сайту з логотипом, навігацією і пошуком - це організм. Він однаковий чи то на сторінці блогу, чи то на сторінці оформлення замовлення. **Шаблони** - це макети сторінок зі слотами для організмів. Без реального контенту, тільки структура. Тут дизайнери й розробники узгоджують скелет до того, як заповнювати його змістом. **Сторінки** - це шаблони з реальними даними. Те, що бачить користувач. Тільки на цьому рівні живуть реальні дані. ### Головна відмінність Atomic Design нав'язує підхід від дрібного до великого. Не можна випустити сторінку без того, щоб вона не зібралась із перевірених, задокументованих частин. Звичайна бібліотека компонентів такого обмеження не має - можна написати одноразовий `<BigSection>`, що дублює половину дизайн-системи, і ніхто не зауважить. Атомна ієрархія робить це очевидним. Кейс-стаді Бреда Фроста фіксують на 40-60% менше UI-невідповідностей у командах, які перейшли на цей підхід. ### Коли використовувати - Соло-розробник, маленький застосунок: не варто. Прості компоненти впораються. - Команда 5+ людей зі спільним UI: починай організовувати хоча б атоми й молекули. - 50+ компонентів у системі: повна атомна ієрархія плюс Storybook для документації. - Figma-to-code handoff: шаблони тут найкорисніші - вони напряму відповідають фреймам у Figma. - Рефакторинг legacy-застосунку: починай з атомів. Аудит повторних кольорів, відступів, контролів. Спочатку нижній рівень. ### Як це влаштовано зсередині Runtime немає, крок компілятора немає. Atomic Design - це методологія процесу. Ти організовуєш файли за рівнями (atoms, molecules, organisms, templates, pages), документуєш кожен рівень у Storybook і дотримуєшся ієрархії через code review та лінтери. Storybook через Webpack або Vite рендерить компоненти в ізольованих iframe, щоб дизайнер міг переглянути молекулу без запуску всього застосунку. TypeScript або PropTypes фіксують контракти пропсів під час розробки. Браузер рендерить фінальний результат звичайним способом. ### Типові помилки **Стилізація молекули ззовні** ```jsx // Неправильно: перебиваємо внутрішні відступи ззовні <SearchMolecule style={{ margin: '10px' }} /> // Правильно: батьківський layout-слот керує відступами <div className="header-search-slot"> <SearchMolecule /> </div> ``` Молекула повинна відповідати за свій внутрішній layout. Якщо перебивати відступи ззовні, одна й та сама молекула виглядатиме по-різному в хедері й у картці. Використовуй CSS-змінні або дизайн-токени. **Запити даних всередині організму** ```jsx // Неправильно: організм сам завантажує дані const UserCard = ({ userId }) => { const user = useFetchUser(userId); // непередбачені re-renders у списках return <div>{user.name}</div>; }; // Правильно: батьківська сторінка передає дані const UserCard = ({ name, avatar }) => ( <div> <img src={avatar} alt={name} /> <span>{name}</span> </div> ); ``` Організми, що самі завантажують дані, непередбачувано перерендерюються в списках і погано тестуються в Storybook. Підніми запити на рівень сторінки. **Пропуск шаблонів і хардкод layout у сторінках** ```jsx // Неправильно: layout живе всередині компонента сторінки const HomePage = () => ( <div style={{ display: 'grid', gridTemplateColumns: '1fr 3fr' }}> ... </div> ); // Правильно: шаблон відокремлює структуру від контенту const TwoColumnTemplate = ({ sidebar, main }) => ( <div className="two-col"> <aside>{sidebar}</aside> <main>{main}</main> </div> ); ``` Без шаблонів мобільний і десктопний layout розходяться. Кожна сторінка будує власну сітку. Шаблон змушує домовитись про структуру до того, як заповнювати її контентом. **Надмірна атомізація семантичного HTML** ```jsx // Неправильно: обгортаємо кожен HTML-тег як атом const H1Atom = ({ children }) => <h1>{children}</h1>; // Правильно: семантичні теги використовуй напряму; атомізуй інтерактивні контроли ``` Обгортання кожного HTML-тегу в атом додає шари без жодної вигоди. Губиться семантика, страждає доступність. Атоми - для інтерактивних і стилізованих контролів: кнопки, поля вводу, бейджі, іконки. ### Де використовується в реальних проектах - Storybook: стандарт для ізоляції та документування атомів і молекул, використовується в Shopify, GitHub та більшості великих дизайн-систем. - Chakra UI: молекули типу `FormControl` зібрані з атомарних `Input` і `Label`. - Material UI: `AppBar` - це організм із атомарної `Icon` і молекули `Toolbar`. - PatternFly (Red Hat): повна атомна система для enterprise-дашбордів. - Дизайн-система Airbnb всередині слідує атомній ієрархії, маппуючи дизайн-токени на атоми. ### Питання на співбесіді **Q:** В чому різниця між молекулою і організмом? **A:** Молекула вирішує одне вузьке завдання (поле з міткою, пошуковий рядок). Організм об'єднує кілька молекул і атомів у самодостатню секцію (хедер із пошуком, навігацією і меню користувача). Якщо компонент може стояти окремо на сторінці як впізнавана секція - скоріш за все, це організм. **Q:** Чим шаблон відрізняється від сторінки? **A:** Шаблон - порожній структурний каркас. Сторінка заповнює шаблон реальним контентом і даними. Шаблон каже "тут буде hero-секція, потім сітка карток"; сторінка каже "в hero - ось це зображення й текст, картки - ось ці товари." **Q:** Навіщо будувати свою систему, якщо є готові бібліотеки як Material UI? **A:** Бібліотеки дають UI-примітиви, а не твою систему. У них немає твоїх брендових токенів, твоїх бізнес-патернів компонентів і командних конвенцій. Atomic Design дозволяє побудувати систему під продукт, при цьому використовуючи сторонні атоми як основу. **Q:** Як Atomic Design масштабується на команду з 50 розробників? **A:** Через Storybook як живу документацію стилів, TypeScript-контракти пропсів і лінтер-правила, що закріплюють ієрархію. Зміни в атомах поширюються автоматично, бо всі рівні будуються на них. Реалістичний орієнтир для здорової системи - коефіцієнт повторного використання компонентів вище 70%. **Q:** Senior: Як мігрувати застосунок зі 100 сторінок на Atomic Design? **A:** Знизу вгору. Спочатку аудит кодової бази: повторювані кнопки, поля, колірні значення, відступи. Витягуємо їх як атоми. Потім знаходимо повторювані UI-групи (поля форм, картки) і переносимо в молекули. Використовуємо codemods для заміни inline-варіантів. Паралельно будуємо документацію в Storybook. Шаблони і сторінки не чіпаємо, поки нижні два рівні не стабілізуються. Прогрес вимірюємо коефіцієнтом повторного використання, а не кількістю компонентів. Після міграції - A/B-тест сторінок для виявлення layout-регресій. ## Приклади ### Базовий: пошукова форма з атомів ```jsx // Атом const Input = ({ placeholder }) => ( <input className="input" placeholder={placeholder} /> ); // Атом const Button = ({ children }) => ( <button className="btn">{children}</button> ); // Молекула: два атоми, одне завдання const SearchForm = () => ( <form className="search-form"> <Input placeholder="Пошук товарів..." /> <Button>Знайти</Button> </form> ); // SearchForm можна використати в Header, Sidebar або Modal // без жодних змін в атомах ``` Два атоми, одна молекула, нуль дублювання. Компоненти `Input` і `Button` не знають, що вони всередині пошукової форми. Вони приймають пропси і рендеряться. ### Середній рівень: картка товару як організм ```jsx // Атом const Button = ({ children }) => <button className="btn">{children}</button>; // Молекула: відображення ціни з опційним станом знижки const Price = ({ amount, sale }) => ( <div className="price"> {sale && <span className="price--sale">${sale}</span>} <span className={sale ? 'price--original' : ''}>${amount}</span> </div> ); // Організм: картка товару зі своїх молекул та атомів const ProductCard = ({ name, price, image, salePrice }) => ( <div className="card"> <img src={image} alt={name} /> <h3>{name}</h3> <Price amount={price} sale={salePrice} /> <Button>До кошика</Button> </div> ); // Використовується всередині шаблону сітки товарів // Пропси валідуються через TypeScript або PropTypes ``` `ProductCard` - організм. Він не знає скільки карток на сторінці, не завантажує дані і не керує layout. Батьківський шаблон займається всім цим. ### Просунутий рівень: шаблон із адаптивною композицією організмів ```jsx // Шаблон: структура сторінки зі слотами для організмів const DashboardTemplate = ({ sidebar, mainContent }) => ( <div className="dashboard"> <aside className="dashboard__sidebar">{sidebar}</aside> <main className="dashboard__main">{mainContent}</main> </div> ); // Сторінка: заповнює шаблон, керує адаптивністю const AnalyticsPage = () => { const [isMobile, setIsMobile] = useState(false); useEffect(() => { const check = () => setIsMobile(window.innerWidth < 768); window.addEventListener('resize', check); return () => window.removeEventListener('resize', check); }, []); return ( <DashboardTemplate sidebar={isMobile ? null : <NavOrganism />} mainContent={<ChartOrganism />} /> ); }; // Мобільний: сайдбар прибирається, без зсуву layout // Десктоп: повний двоколонковий layout // Шаблон не змінюється - тільки сторінка вирішує що рендерити ``` Шаблон ніколи не змінюється. Сторінка вирішує, які організми потрапляють у слоти. На мобільному сайдбар-організм зникає - шаблон просто отримує `null` у цей слот. Я бачив команди, які уникали цього патерну й отримували п'ять різних реалізацій сітки на п'яти окремих "мобільних сторінках." Один шаблон вирішує всі ці проблеми одразу.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.