Skip to content

Styling Guidelines

Руководство по написанию стилей в проекте с использованием SCSS, Angular Material 3 и собственных миксинов.

Основные принципы

1. Вложенность

ВСЕГДА пишем стили вложенными, следуя структуре DOM.

scss
// ✅ Правильно
.card {
	padding: 16px;

	.card-header {
		display: flex;

		.title {
			font-size: 18px;
		}
	}

	.card-content {
		margin-top: 16px;
	}
}

// ❌ Неправильно
.card {
	padding: 16px;
}
.card-header {
	display: flex;
}
.card .title {
	font-size: 18px;
}

2. Импорт abstractions

Каждый компонент ВСЕГДА начинается с импорта:

scss
@use "../../../../../../assets/scss/abstractions/index" as *;

Это даёт доступ ко всем миксинам проекта.

3. Миксин @include component()

ВСЕГДА оборачиваем стили компонента в @include component():

scss
@use "../../../../../../assets/scss/abstractions/index" as *;

@include component() {
	.my-component {
		display: flex;
		// остальные стили
	}
}

Это автоматически создаёт:

scss
:host {
	display: block;
}

Design Tokens

Border Radius (Angular Material 3)

Используем системные переменные Material Design:

scss
.card {
	border-radius: var(--mat-sys-corner-medium); // 12px - стандарт
}

.badge {
	border-radius: var(--mat-sys-corner-small); // 8px
}

.container {
	border-radius: var(--mat-sys-corner-large); // 16px
}

.avatar {
	border-radius: var(--mat-sys-corner-full); // 9999px - круглый
}

Доступные значения:

  • --mat-sys-corner-none — 0px
  • --mat-sys-corner-extra-small — 4px
  • --mat-sys-corner-small — 8px
  • --mat-sys-corner-medium12px (стандарт)
  • --mat-sys-corner-large — 16px
  • --mat-sys-corner-extra-large — 28px
  • --mat-sys-corner-full — 9999px (круглые элементы)

Spacing (Padding & Margin)

Используем кастомные переменные для отступов:

scss
.card {
	padding: var(--spacing-md); // 16px - стандарт
}

.container {
	padding: var(--spacing-xl); // 32px - большой
}

.section {
	margin-bottom: var(--spacing-lg); // 24px
}

.small-badge {
	padding: var(--spacing-xs); // 4px
}

Доступные значения:

  • --spacing-xs — 4px
  • --spacing-sm — 8px
  • --spacing-md16px (стандарт)
  • --spacing-lg — 24px
  • --spacing-xl32px (большой)
  • --spacing-2xl — 48px

Цвета (Material 3)

НИКОГДА не используем хардкод цвета. ВСЕГДА только переменные:

scss
// ✅ Правильно
.button {
	background: var(--mat-sys-primary);
	color: var(--mat-sys-on-primary);
}

.card {
	background: var(--mat-sys-surface-container);
	color: var(--mat-sys-on-surface);
}

// ❌ Неправильно
.button {
	background: #8c68cb;
	color: white;
}

Основные цветовые токены:

scss
// Primary (фиолетовый)
--mat-sys-primary: #8c68cb;
--mat-sys-on-primary: #ffffff;
--mat-sys-primary-container: #e9ddff;

// Surface (серые)
--mat-sys-surface: #ffffff;
--mat-sys-surface-container: #f5f5f5;
--mat-sys-surface-container-high: #eeeeee;
--mat-sys-on-surface: #1c1b1f;
--mat-sys-on-surface-variant: #424242;

// Error (красный)
--mat-sys-error: #ee6352;
--mat-sys-error-container: #ffdad4;

// Tertiary (зеленый)
--mat-sys-tertiary: #49ca8d;
--mat-sys-tertiary-container: #c8f5dc;

// Outline
--mat-sys-outline: #bdbdbd;
--mat-sys-outline-variant: #e0e0e0;

Типографика (Material 3)

Используем типографические токены вместо font-size и font-weight:

scss
// ✅ Правильно
.title {
	font: var(--mat-sys-headline-large); // 32px, 600 weight
}

.subtitle {
	font: var(--mat-sys-title-medium); // 16px, 500 weight
}

.body-text {
	font: var(--mat-sys-body-medium); // 14px, 400 weight
}

// ❌ Неправильно
.title {
	font-size: 32px;
	font-weight: 600;
}

Доступные токены:

  • --mat-sys-headline-large — 32px
  • --mat-sys-headline-medium — 28px
  • --mat-sys-headline-small — 24px
  • --mat-sys-title-large — 22px
  • --mat-sys-title-medium — 16px
  • --mat-sys-title-small — 14px
  • --mat-sys-body-large — 16px
  • --mat-sys-body-medium — 14px
  • --mat-sys-body-small — 12px
  • --mat-sys-label-large — 14px
  • --mat-sys-label-medium — 12px
  • --mat-sys-label-small — 11px

Миксины

@include component()

Основной миксин для компонента. Создаёт :host с display: block.

scss
@use "../../../../../../assets/scss/abstractions/index" as *;

@include component() {
	.container {
		padding: var(--spacing-md);
	}
}

С параметром display:

scss
@include component(flex) {
	// :host { display: flex; }
	flex-direction: column;
}

@include mobile()

Медиа-запрос для мобильных устройств (max-width: 55.5em / 888px).

scss
.sidebar {
	width: 300px;

	@include mobile() {
		width: 100%;
		position: fixed;
	}
}

@include breakpoint($n)

Универсальный миксин для брейкпоинтов (1-5).

scss
.container {
	padding: 32px;

	@include breakpoint(3) {
		// max-width: 55.5em (888px)
		padding: 16px;
	}

	@include breakpoint(1) {
		// max-width: 36em (576px)
		padding: 8px;
	}
}

Доступные брейкпоинты:

  • breakpoint(1) — max-width: 36em (576px)
  • breakpoint(2) — max-width: 48em (768px)
  • breakpoint(3) — max-width: 55.5em (888px) - используется в mobile()
  • breakpoint(4) — max-width: 75em (1200px)
  • breakpoint(5) — max-width: 87.5em (1400px)

@include noWrap()

Обрезка текста с многоточием.

scss
.username {
	@include noWrap();
	max-width: 200px;
}

// Результат:
// white-space: nowrap;
// overflow: hidden;
// text-overflow: ellipsis;

@include noScrollbar()

Скрывает scrollbar.

scss
.content {
	@include noScrollbar();
}

@include hideOnMobile()

Скрывает элемент на мобильных устройствах.

scss
.desktop-menu {
	@include hideOnMobile();
}

@include hideOnDesktop()

Показывает элемент только на мобильных.

scss
.mobile-menu {
	@include hideOnDesktop();
}

Структура SCSS файла

Правильная структура компонента:

scss
// 1. Импорт abstractions
@use "../../../../../../assets/scss/abstractions/index" as *;

// 2. Миксин component()
@include component() {
	// 3. Корневой элемент компонента
	.my-component {
		display: flex;
		padding: var(--spacing-md);
		border-radius: var(--mat-sys-corner-medium);
		background: var(--mat-sys-surface-container);

		// 4. Вложенные элементы
		.header {
			padding: var(--spacing-sm);
			font: var(--mat-sys-title-large);

			.title {
				color: var(--mat-sys-on-surface);
			}
		}

		.content {
			flex: 1;
			padding: var(--spacing-md);

			.text {
				font: var(--mat-sys-body-medium);
				@include noWrap();
			}
		}

		// 5. Медиа-запросы последними
		@include mobile() {
			flex-direction: column;
			padding: var(--spacing-sm);
		}
	}
}

Примеры

Карточка с адаптивностью

scss
@use "../../../../../../assets/scss/abstractions/index" as *;

@include component() {
	.card {
		background: var(--mat-sys-surface);
		border-radius: var(--mat-sys-corner-medium);
		padding: var(--spacing-xl);
		box-shadow: var(--mat-sys-level1);

		.card-header {
			display: flex;
			justify-content: space-between;
			margin-bottom: var(--spacing-md);

			.title {
				font: var(--mat-sys-headline-small);
				color: var(--mat-sys-on-surface);
			}

			.actions {
				display: flex;
				gap: var(--spacing-sm);
			}
		}

		.card-content {
			font: var(--mat-sys-body-medium);
			color: var(--mat-sys-on-surface-variant);
		}

		@include mobile() {
			padding: var(--spacing-md);

			.card-header {
				flex-direction: column;
				gap: var(--spacing-sm);
			}
		}
	}
}

Список с overflow

scss
@use "../../../../../../assets/scss/abstractions/index" as *;

@include component() {
	.list-container {
		height: 400px;
		@include noScrollbar();

		.list-item {
			padding: var(--spacing-sm) var(--spacing-md);
			border-radius: var(--mat-sys-corner-small);
			cursor: pointer;

			&:hover {
				background: var(--mat-sys-surface-container);
			}

			.item-title {
				font: var(--mat-sys-label-large);
				@include noWrap();
			}

			.item-subtitle {
				font: var(--mat-sys-label-small);
				color: var(--mat-sys-on-surface-variant);
				@include noWrap();
			}
		}
	}
}

Кнопка с состояниями

scss
@use "../../../../../../assets/scss/abstractions/index" as *;

@include component() {
	.custom-button {
		padding: var(--spacing-sm) var(--spacing-md);
		border-radius: var(--mat-sys-corner-small);
		background: var(--mat-sys-primary);
		color: var(--mat-sys-on-primary);
		font: var(--mat-sys-label-large);
		cursor: pointer;
		border: none;

		&:hover {
			background: var(--mat-sys-primary-container);
			color: var(--mat-sys-on-primary-container);
		}

		&:disabled {
			background: var(--mat-sys-surface-container);
			color: var(--mat-sys-on-surface-variant);
			cursor: not-allowed;
		}

		&.error {
			background: var(--mat-sys-error);
			color: var(--mat-sys-on-error);
		}
	}
}

Чек-лист перед коммитом

  • [ ] Используется @include component()
  • [ ] Все стили вложенные (не плоские селекторы)
  • [ ] Используются переменные для цветов (var(--mat-sys-*))
  • [ ] Используются переменные для border-radius (var(--mat-sys-corner-*))
  • [ ] Используются переменные для padding/margin (var(--spacing-*))
  • [ ] Используются типографические токены вместо font-size/font-weight
  • [ ] Миксины @include mobile(), @include noWrap() где нужно
  • [ ] Нет хардкода цветов, размеров шрифтов
  • [ ] Stylelint проходит без ошибок

Что НЕ делать

Плоские селекторы:

scss
.card {
}
.card-header {
}
.card-title {
}

Хардкод значений:

scss
.card {
	border-radius: 12px;
	padding: 16px;
	background: #8c68cb;
	font-size: 16px;
	font-weight: 600;
}

Без @include component():

scss
@use "../../scss/index" as *;

.my-component {
	display: flex;
}

Игнорирование миксинов:

scss
.text {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	// Вместо @include noWrap()
}

Следующие шаги

SaaS Admin Documentation