Corp/backend/static/scss/layout/_page-header.scss
Игорь Надеин 9df9f78666 refactor: Complete SCSS architecture reorganization
SCSS Architecture 2.0 - Feature-based structure

### Major Changes:
- ✅ Reorganized 51 SCSS files into logical categories
- ✅ Created feature-based structure: core/, layout/, features/, utilities/
- ✅ Merged duplicate components: badges (2→1), modals (3→1), lists (2→1), spacing (2→1)
- ✅ Renamed files: ios-search→search, team-wheel→team-viz, recipientPicker→recipient-picker
- ✅ Cleaned custom-bootstrap.scss from all app components
- ✅ Moved chat components from custom-bootstrap to main.scss

### Results:
- Files: 51 → 38 (-13 files, -25%)
- bootstrap-custom.css: 301 KB → 229 KB (-72 KB, -24%)
- app.css: 95 KB → 137 KB (+42 KB, expected)
- Total CSS: 396 KB → 366 KB (-30 KB, -7.5%)

### New Structure:
scss/
├── abstracts/           # Variables, mixins (4 files)
├── core/               # UI Kit - reusable components (9 files)
│   ├── buttons, badges, cards, lists, modals, etc.
├── layout/             # App structure (6 files)
│   ├── grid, navbar, sidebar, rightbar, etc.
├── utilities/          # Utility classes (1 file)
│   └── spacing
└── features/           # Feature-specific styles (18 files in 6 folders)
    ├── feed/          (3 files)
    ├── requests/      (3 files)
    ├── documents/     (1 file)
    ├── employees/     (2 files)
    ├── notifications/ (1 file)
    └── chat/          (10 files)

### Benefits:
- Clear categorization
- No duplicate imports (removed 8 duplicates)
- Logical feature grouping
- Easier maintenance
- Clean architecture
- Better naming conventions

### Documentation:
- scss/README.md - full architecture documentation
- scss/MIGRATION_MATRIX.md - file migration matrix
- docs/reports/SCSS_REORGANIZATION_COMPLETE.md - completion report

Bootstrap remains as base UI framework.
2026-01-15 14:11:14 +03:00

478 lines
13 KiB
SCSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* _page-header.scss
* Универсальная шапка для страниц списков с:
* - Полупрозрачным фоном (sticky positioning)
* - Иконкой и заголовком
* - Счётчиком элементов
* - Переключателем области (Мои/Все)
* - Кнопкой фильтров
* - Кнопкой создания (вытянутая на десктопе, иконка на мобильных)
* - Поиском iOS-стиль
*/
/* ─────────────────────────────────────────────────────────────
ОСНОВНОЙ КОНТЕЙНЕР ШАПКИ
────────────────────────────────────────────────────────────── */
.page-header {
position: sticky;
top: var(--navbar-h, 60px);
z-index: 10;
padding: var(--space-2) 0;
margin-bottom: var(--space-3);
/* Полупрозрачный фон с градиентом */
background: linear-gradient(
to bottom,
color-mix(in srgb, var(--bs-body-bg) 90%, transparent) 0%,
color-mix(in srgb, var(--bs-body-bg) 85%, transparent) 30%,
color-mix(in srgb, var(--bs-body-bg) 70%, transparent) 50%,
color-mix(in srgb, var(--bs-body-bg) 40%, transparent) 70%,
transparent 100%
);
@include transition((background, box-shadow), $transition-fast);
&::before {
content: '';
@include absolute-cover();
backdrop-filter: saturate(180%) $blur-lg;
-webkit-backdrop-filter: saturate(180%) $blur-lg;
mask-image: linear-gradient(
to bottom,
black 0%,
black 30%,
rgba(0, 0, 0, 0.7) 50%,
rgba(0, 0, 0, 0.4) 70%,
transparent 100%
);
-webkit-mask-image: linear-gradient(
to bottom,
black 0%,
black 30%,
rgba(0, 0, 0, 0.7) 50%,
rgba(0, 0, 0, 0.4) 70%,
transparent 100%
);
pointer-events: none;
z-index: -1;
}
> * {
position: relative;
z-index: $z-base;
}
/* При скролле - более выраженный фон */
&.scrolled {
background: linear-gradient(
to bottom,
color-mix(in srgb, var(--bs-body-bg) 95%, transparent) 0%,
color-mix(in srgb, var(--bs-body-bg) 90%, transparent) 30%,
color-mix(in srgb, var(--bs-body-bg) 75%, transparent) 50%,
color-mix(in srgb, var(--bs-body-bg) 45%, transparent) 70%,
transparent 100%
);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
/* Кнопка создания в виде карточки */
.card {
margin-top: var(--space-2);
width: 100%;
button.dept-row,
a.dept-row {
cursor: pointer;
transition: background-color 0.2s ease;
width: 100%;
padding: var(--space-1) var(--space-2);
text-align: center;
.card-title {
font-weight: 700;
color: var(--bs-body-color);
}
i {
color: var(--bs-primary);
}
&:hover {
background-color: var(--bs-light);
}
}
}
/* Адаптивность */
.d-flex.align-items-center {
flex-wrap: wrap;
}
}
/* ─────────────────────────────────────────────────────────────
CHAT AVATAR WITH TYPE BADGE
────────────────────────────────────────────────────────────── */
.chat-avatar-wrapper {
position: relative;
width: 48px;
height: 48px;
flex-shrink: 0;
}
.chat-avatar {
width: 100%;
height: 100%;
border-radius: 12px;
object-fit: cover;
display: block;
border: 1px solid var(--bs-border-color);
}
.chat-type-badge {
position: absolute;
bottom: -2px;
right: -2px;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--bs-primary);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
border: 2px solid var(--bs-body-bg);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
i {
line-height: 1;
}
[data-bs-theme="dark"] & {
border-color: var(--bs-body-bg);
}
}
/* ─────────────────────────────────────────────────────────────
TOP BAR (заголовок + действия)
────────────────────────────────────────────────────────────── */
.page-header-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
width: 100%;
}
.page-header-title {
display: flex;
align-items: center;
gap: var(--space-2);
flex: 1;
min-width: 0;
h1 {
font-size: 1.5rem;
font-weight: 700;
margin: 0;
color: var(--bs-body-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
i {
font-size: 1.75rem;
flex-shrink: 0;
}
}
.page-header-actions {
display: flex;
align-items: center;
gap: var(--space-1);
flex-shrink: 0;
}
/* ─────────────────────────────────────────────────────────────
ЗАГОЛОВОК И СЧЁТЧИК
────────────────────────────────────────────────────────────── */
.page-title {
font-weight: 800;
font-size: 1.75rem;
letter-spacing: -0.01em;
line-height: 1.2;
color: var(--bs-body-color);
flex-shrink: 0;
}
.page-count {
font-size: 0.875rem;
color: var(--bs-secondary-color);
white-space: nowrap;
flex-shrink: 0;
.count-value {
font-weight: 600;
color: var(--bs-body-color);
}
}
/* ─────────────────────────────────────────────────────────────
ПЕРЕКЛЮЧАТЕЛЬ ОБЛАСТИ (МОИ/ВСЕ)
────────────────────────────────────────────────────────────── */
.page-scope-switch {
flex-shrink: 0;
.btn {
min-width: 60px;
font-size: 0.875rem;
border-radius: 999px;
padding: 0.375rem 0.75rem;
transition: all 0.2s ease;
&:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&.active {
background-color: var(--bs-primary);
border-color: var(--bs-primary);
color: white;
font-weight: 600;
}
}
}
/* ─────────────────────────────────────────────────────────────
КНОПКА ФИЛЬТРОВ
────────────────────────────────────────────────────────────── */
.page-filter-toggle {
&:hover {
background-color: color-mix(in srgb, var(--bs-primary), transparent 85%);
border-color: var(--bs-primary);
color: var(--bs-primary);
}
&[aria-expanded="true"] {
background-color: var(--bs-primary);
border-color: var(--bs-primary);
color: white;
}
}
/* ─────────────────────────────────────────────────────────────
КНОПКА СОЗДАНИЯ
────────────────────────────────────────────────────────────── */
.page-create-btn {
font-weight: 600;
font-size: 0.875rem;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
&:active {
transform: translateY(0);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
}
/* ─────────────────────────────────────────────────────────────
ПОИСК
────────────────────────────────────────────────────────────── */
.page-search {
flex-shrink: 1;
min-width: 200px;
max-width: 400px;
flex-grow: 1;
.ios-search {
width: 100%;
}
}
/* ─────────────────────────────────────────────────────────────
БЛОК ФИЛЬТРОВ
────────────────────────────────────────────────────────────── */
.page-filters-collapse {
margin-top: var(--space-2);
width: 100%;
}
.page-filters {
border-radius: var(--radius-lg, 14px);
border: 1px solid color-mix(in srgb, var(--bs-border-color), transparent 10%);
background: var(--bs-body-bg);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
padding: var(--space-3);
animation: slideDown 0.3s ease;
.btn {
white-space: nowrap;
min-width: fit-content;
}
.d-flex.gap-2 {
flex-wrap: wrap;
}
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ─────────────────────────────────────────────────────────────
АДАПТИВНОСТЬ
────────────────────────────────────────────────────────────── */
/* Планшеты */
@media (max-width: 991.98px) {
.page-title {
font-size: 1.5rem;
}
.page-header {
padding: var(--space-2) 0;
}
.page-search {
flex-basis: 100%;
max-width: none;
margin-top: var(--space-2);
order: 10;
}
.page-filter-toggle {
order: 11;
margin-top: var(--space-2);
margin-left: auto;
margin-right: 0 !important;
}
}
@media (max-width: 767.98px) {
.page-header-title {
h1 {
font-size: 1.25rem;
}
i {
font-size: 1.5rem;
}
}
}
/* Мобильные */
@media (max-width: 575.98px) {
.page-header {
margin-bottom: var(--space-2);
padding: var(--space-1) 0;
top: var(--navbar-h, 60px);
}
.page-title {
font-size: 1.125rem;
font-weight: 700;
}
.page-count {
font-size: 0.7rem;
}
.page-scope-switch {
gap: 0.25rem;
.btn {
min-width: auto;
font-size: 0.7rem;
padding: 0.2rem 0.4rem;
}
}
.page-filters {
margin-top: 0.5rem;
.btn {
padding: 0.4rem 0.75rem;
font-size: 0.85rem;
}
}
.page-search {
margin-top: 0.5rem;
}
@media (max-width: 480px) {
.page-count {
display: none;
}
.page-header .card {
margin-top: 0;
width: 100%;
button.dept-row,
a.dept-row {
padding: var(--space-1) var(--space-1);
font-size: 0.9rem;
}
}
}
}
/* ─────────────────────────────────────────────────────────────
ТЕМНАЯ ТЕМА
────────────────────────────────────────────────────────────── */
@media (prefers-color-scheme: dark) {
.page-header {
background: linear-gradient(
to bottom,
color-mix(in srgb, var(--bs-body-bg) 85%, transparent) 0%,
color-mix(in srgb, var(--bs-body-bg) 80%, transparent) 45%,
color-mix(in srgb, var(--bs-body-bg) 65%, transparent) 50%,
color-mix(in srgb, var(--bs-body-bg) 35%, transparent) 70%,
transparent 100%
);
border-bottom-color: color-mix(in srgb, var(--bs-border-color), transparent 60%);
&.scrolled {
background: linear-gradient(
to bottom,
color-mix(in srgb, var(--bs-body-bg) 90%, transparent) 0%,
color-mix(in srgb, var(--bs-body-bg) 80%, transparent) 30%,
color-mix(in srgb, var(--bs-body-bg) 72%, transparent) 50%,
color-mix(in srgb, var(--bs-body-bg) 40%, transparent) 70%,
transparent 100%
);
}
.card {
button.dept-row:hover,
a.dept-row:hover {
background-color: color-mix(in srgb, var(--bs-body-bg) 95%, white);
}
}
}
.page-filters {
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
}
}