Atomic design в Stencil

Основы подхода Atomic Design

Atomic Design — методология создания интерфейсов через систематизацию компонентов по уровням: атомы, молекулы, организмы, шаблоны и страницы. В контексте Stencil, который является фреймворком для создания Web Components, этот подход идеально подходит для построения модульного, повторно используемого и легко поддерживаемого кода.

  • Атомы — минимальные элементы интерфейса, неразделимые и самостоятельные. Примеры: кнопка, иконка, поле ввода.
  • Молекулы — комбинации атомов, выполняющие простую функциональность. Например, поле ввода с лейблом и кнопкой отправки.
  • Организмы — более сложные блоки, состоящие из молекул и атомов. Пример: форма авторизации.
  • Шаблоны — структуры страниц, где организмы размещаются в определённой иерархии, формируя макет.
  • Страницы — конкретные реализации шаблонов с реальными данными.

Создание атомов в Stencil

В Stencil каждый компонент — это отдельный веб-компонент, который инкапсулирует логику, разметку и стили. Создание атома начинается с генерации компонента через CLI:

npm init stencil

Структура атома в Stencil:

import { Component, Prop, h } from '@stencil/core';

@Component({
  tag: 'my-button',
  styleUrl: 'my-button.css',
  shadow: true,
})
export class MyButton {
  @Prop() label: string;
  @Prop() disabled: boolean = false;

  render() {
    return (
      <button disabled={this.disabled}>
        {this.label}
      </button>
    );
  }
}

Ключевые моменты:

  • @Component определяет тег, стили и использование Shadow DOM.
  • @Prop позволяет передавать данные внутрь компонента.
  • render() возвращает JSX-разметку.

Создание молекул

Молекула объединяет несколько атомов. В Stencil это делается простым импортом и использованием атомов внутри другого компонента:

import { Component, h } from '@stencil/core';

@Component({
  tag: 'login-input',
  styleUrl: 'login-input.css',
  shadow: true,
})
export class LoginInput {
  render() {
    return (
      <div class="login-field">
        <label htmlFor="username">Username:</label>
        <my-input id="username" placeholder="Введите имя" />
      </div>
    );
  }
}

Особенности:

  • Использование вложенных компонентов повышает повторное использование кода.
  • Стили можно наследовать или комбинировать для сохранения визуальной целостности.

Организмы и их взаимодействие

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

import { Component, State, h } from '@stencil/core';

@Component({
  tag: 'login-form',
  styleUrl: 'login-form.css',
  shadow: true,
})
export class LoginForm {
  @State() username: string = '';
  @State() password: string = '';

  handleLogin() {
    console.log('Logging in:', this.username, this.password);
  }

  render() {
    return (
      <form onSub mit={(e) => { e.preventDefault(); this.handleLogin(); }}>
        <login-input />
        <login-input />
        <my-button label="Войти" />
      </form>
    );
  }
}

Здесь:

  • @State используется для локального состояния компонента.
  • Обработка событий через JSX позволяет интегрировать бизнес-логику.

Шаблоны и страницы

Шаблоны определяют расположение организмов на макете, а страницы подставляют реальные данные:

import { Component, h } from '@stencil/core';

@Component({
  tag: 'login-page',
  styleUrl: 'login-page.css',
  shadow: false,
})
export class LoginPage {
  render() {
    return (
      <main>
        <header>Авторизация</header>
        <login-form />
        <footer>© 2025</footer>
      </main>
    );
  }
}

Отличия шаблонов и страниц:

  • Шаблон — структура без реальных данных, абстрактная.
  • Страница — конкретная реализация шаблона с наполнением.

Организация проекта по Atomic Design

Рекомендуемая структура проекта:

src/components/
├── atoms/
│   ├── my-button/
│   └── my-input/
├── molecules/
│   └── login-input/
├── organisms/
│   └── login-form/
├── templates/
│   └── login-template/
└── pages/
    └── login-page/

Преимущества:

  • Лёгкое масштабирование интерфейса.
  • Ясная иерархия компонентов.
  • Простота тестирования отдельных блоков.

Использование Shadow DOM и стилей

Stencil автоматически изолирует стили через Shadow DOM, что особенно полезно в Atomic Design, где одни и те же атомы используются в разных контекстах.

Пример объединения локальных и глобальных стилей:

/* my-button.css */
button {
  padding: 0.5em 1em;
  border-radius: 4px;
  background-color: var(--primary-color, #007bff);
  color: white;
  border: none;
  cursor: pointer;
}

Стили атомов наследуются молекулами и организмами без конфликта имен.

Взаимодействие компонентов и событий

Stencil поддерживает EventEmitter для коммуникации между компонентами:

import { Component, Event, EventEmitter, h } from '@stencil/core';

@Component({
  tag: 'my-button',
  styleUrl: 'my-button.css',
  shadow: true,
})
export class MyButton {
  @Event() clicked: EventEmitter<void>;

  handleClick() {
    this.clicked.emit();
  }

  render() {
    return <button onCl ick={() => this.handleClick()}>Нажми меня</button>;
  }
}

На уровне организма можно подписаться на событие:

<my-button onClic ked={() => console.log('Button clicked')} />

Преимущества подхода Atomic Design в Stencil

  • Повторное использование компонентов повышает скорость разработки.
  • Изоляция стилей через Shadow DOM предотвращает конфликты.
  • Чёткая структура проекта упрощает сопровождение и масштабирование.
  • Управление состоянием через @State и события позволяет строить сложные взаимодействия между организмами и страницами.

Atomic Design и Stencil вместе создают мощный инструмент для построения модульных и масштабируемых интерфейсов, где каждый уровень компонентов имеет своё место и строго определённые функции.