Angular IDPStrong Junior
Модулі / Точний layout і Change Detection / Дизайн-токени в Angular
🔥 0

Модуль 3 · урок 8 з 11

Дизайн-токени в Angular

У модулі 1 ти зробив тему на CSS-змінних. Тут вона зустрічається з Angular: як розкласти токени по рівнях, як стилізувати сторонні компоненти без ::ng-deep і що робити з Angular Material, якщо на проєкті використовують саме його.

Три рівні токенів

/* 1. Примітиви — сира палітра. Ніде не використовуються напряму. */
:root {
  --blue-600: #2563EB;
  --slate-900: #0F172A;
  --space-2: 8px;
}

/* 2. Семантичні — ролі. Саме їх пишуть у компонентах. */
:root {
  --color-accent: var(--blue-600);
  --color-ink: var(--slate-900);
  --gap-tight: var(--space-2);
}

/* 3. Компонентні — локальні ручки налаштування. */
:host {
  --card-padding: 16px;
  --card-radius: 10px;
}

Сенс середнього рівня в тому, що тема перевизначає ролі, а не кольори. У темній темі --color-accent стане іншим, і жоден компонент про це не дізнається — саме тому в модулі 1 ми домовились називати токени за роллю, а не за кольором.

Компонент із власними ручками

:host {
  --card-padding: 16px;              /* значення за замовчуванням */

  display: block;
  padding: var(--card-padding);
  border-radius: var(--card-radius, 10px);   /* із запасним значенням */
  background: var(--color-surface);
}

:host([data-density="compact"]) {
  --card-padding: 8px;               /* варіант через атрибут */
}
<!-- батько може підкрутити ззовні, не знаючи внутрішньої розмітки -->
<app-task-card style="--card-padding: 4px" />
<app-task-card data-density="compact" />
Це і є заміна ::ng-deep

Замість того, щоб ззовні пробивати ізоляцію й міняти чужі класи, компонент сам оголошує, що в ньому можна налаштувати. Виходить публічний контракт стилів: список змінних — така сама частина API компонента, як його input().

Динамічні значення з класу

@Component({
  host: {
    '[style.--progress]': 'percent()',
  },
  template: `<div class="bar"></div>`,
})
export class ProgressBar {
  percent = input(0);
}
.bar { width: calc(var(--progress) * 1%); }

Прив'язка кастомної властивості — акуратний спосіб передати число в CSS без інлайнових стилів на кожен елемент. Особливо зручно для індикаторів, графіків і всього, де значення змінюється часто.

Angular Material: як стилізувати правильно

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

// styles.scss
@use '@angular/material' as mat;

html {
  @include mat.theme((
    color: (
      primary: mat.$violet-palette,
      theme-type: light,
    ),
    typography: Roboto,
    density: 0,
  ));
}
/* точкове налаштування окремого компонента — через його ж змінні */
.dense-form {
  @include mat.form-field-density(-2);
}
Правильні способи
  • Тема Material через його міксини — в одному місці.
  • Свої CSS-змінні для власних компонентів.
  • Класи-модифікатори на хості: data-density, .is-compact.
  • Глобальні правки для бібліотеки — свідомо, у styles.scss, з коментарем.
Симптоми проблеми
  • ::ng-deep .mat-mdc-* у файлі компонента.
  • !important поруч із класами бібліотеки.
  • Однакові виправлення Material у пʼятьох різних компонентах.
  • Стилі, привʼязані до внутрішніх класів бібліотеки, які зміняться в наступній версії.

Де що має лежати

src/styles/
  _tokens.scss        // примітиви й семантичні токени, обидві теми
  _reset.scss
  _mixins.scss
  _material.scss      // тема бібліотеки, якщо вона є
styles.scss           // збирає все через @use

// компонентні токени лишаються біля компонента, у його ж .scss
Що показати ліду

Найпереконливіше — перемкнути тему на демо й показати, що жоден компонент не містить жодного кольору напряму: усе через токени. Це рівно те, чого просить перший пункт таблиці, і водночас доказ, що ти розумієш ізоляцію стилів у Angular.