42 KiB
План реорганизации SCSS архитектуры
Дата: 15 января 2026
Статус: 📋 План
Приоритет: HIGH
Время выполнения: 6-8 часов (с Bootstrap) / 40-60 часов (без Bootstrap)
🎯 Цель
Преобразовать хаотичную структуру в чистую, поддерживаемую архитектуру с четкой категоризацией и переиспользуемыми компонентами.
⚠️ КРИТИЧЕСКОЕ РЕШЕНИЕ: Bootstrap или нет?
Пользователь запросил: Полный отказ от Bootstrap в пользу собственных стилей.
Это означает:
- ❌ Удалить зависимость от Bootstrap (~301 KB CSS)
- ✅ Написать свою систему: Grid, Buttons, Forms, Modals, Cards, Utilities
- ⏱️ Увеличение времени реализации с 8 часов до 40-60 часов
- 🎨 Полный контроль над кодом и стилями
- 📦 Уменьшение итогового размера бандла
- 🔧 Необходимость поддержки своего UI Kit
См. раздел "БОНУСНЫЙ ЭТАП: Отказ от Bootstrap" в конце документа.
📊 Целевая архитектура
Feature-based структура (подходит под Django структуру):
scss/
├── abstracts/ # ✅ Оставить как есть
│ ├── _variables.scss # SCSS переменные
│ ├── _css-variables.scss # CSS custom properties
│ ├── _mixins.scss # Миксины
│ └── _index.scss # Экспорт
│
├── core/ # ⭐ НОВАЯ ПАПКА - UI Kit
│ ├── _buttons.scss # Кнопки
│ ├── _forms.scss # Формы и инпуты
│ ├── _badges.scss # Один badge компонент (объединить 2)
│ ├── _cards.scss # Карточки
│ ├── _lists.scss # ОДИН базовый список
│ ├── _modals.scss # ОДИН modal (объединить 2)
│ ├── _avatars.scss # Аватары
│ ├── _search.scss # Поиск (вместо ios-search)
│ └── _comments.scss # Комментарии
│
├── layout/ # Структура приложения
│ ├── _grid.scss # App grid (из base-app.scss)
│ ├── _header.scss # Navbar + page-header (объединить)
│ ├── _sidebar.scss # ✅ Оставить
│ └── _rightbar.scss # Rightbar + calendar (объединить)
│
├── features/ # ⭐ НОВАЯ ПАПКА - Feature-specific
│ ├── feed/
│ │ ├── _layout.scss # Feed layout
│ │ ├── _post-card.scss # Карточка поста
│ │ └── _post-form.scss # ✅ Переместить
│ ├── requests/
│ │ ├── _list.scss # Список заявок
│ │ ├── _detail.scss # Детали заявки
│ │ └── _picker.scss # Recipient picker
│ ├── documents/
│ │ └── _list.scss # Список документов
│ ├── employees/
│ │ ├── _controls.scss # Department controls
│ │ └── _team-viz.scss # Team visualization (team-wheel)
│ ├── notifications/
│ │ └── _center.scss # Notification center
│ └── chat/ # ✅ Оставить подпапку
│ └── ... # 10 файлов как есть
│
└── utilities/ # Utility классы
└── _spacing.scss # Объединить spacing-utils + layout-spacing
📋 Этапы выполнения
Этап 0: Подготовка (30 мин)
0.1 Создать backup
cd backend/static
cp -r scss scss_backup_$(date +%Y%m%d)
git add -A
git commit -m "backup: SCSS before reorganization"
0.2 Создать новые папки
cd scss
mkdir -p core features utilities
mkdir -p features/{feed,requests,documents,employees,notifications}
0.3 Создать tracking файл
# Создать файл для отслеживания прогресса
touch REORGANIZATION_PROGRESS.md
Этап 1: Аудит и группировка (1 час)
1.1 Создать матрицу миграции
Создать MIGRATION_MATRIX.md:
| Старый файл | Новое расположение | Статус | Требуется слияние |
|-------------|-------------------|--------|-------------------|
| _buttons.scss | core/_buttons.scss | ⏳ | Нет |
| _badges-bem.scss | core/_badges.scss | ⏳ | Да (с telegram) |
| _badges-telegram-style.scss | core/_badges.scss | ⏳ | Да (с bem) |
| _ios-search.scss | core/_search.scss | ⏳ | Нет |
| ... | ... | ... | ... |
1.2 Проанализировать зависимости
# Найти все импорты
grep -r "@import" main.scss custom-bootstrap.scss
# Найти использование классов в шаблонах
grep -r "class=" templates/ | grep -E "(ios-|badge-|card-list)"
Этап 2: Core UI Kit (2 часа)
2.1 Простые перемещения (без изменений)
# Buttons - просто переместить
mv components/_buttons.scss core/_buttons.scss
# Cards - переместить
mv components/_cards.scss core/_cards.scss
# Comments - переместить
mv components/_comments.scss core/_comments.scss
# Common - переместить
mv components/_common.scss core/_common.scss
2.2 Переименование файлов
# iOS-search → search
mv components/_ios-search.scss core/_search.scss
# iOS-components разобрать на части или переименовать
mv components/_ios-components.scss core/_ui-elements.scss
Задача: Открыть _ios-components.scss, посмотреть что там, возможно разбить на несколько файлов.
2.3 Объединение badges (ВАЖНО!)
Шаги:
- Открыть
_badges-bem.scssи_badges-telegram-style.scss - Проанализировать что можно объединить
- Создать единый
core/_badges.scss:
// core/_badges.scss
// ===== BASE BADGE =====
.badge {
// Общие стили
}
// ===== BEM VARIANT =====
.badge--bem {
// BEM специфичные стили
}
// ===== TELEGRAM VARIANT =====
.badge--telegram {
// Telegram специфичные стили
}
// ===== STATUS VARIANTS =====
.badge--success { }
.badge--danger { }
.badge--pending { }
- Удалить старые файлы
- Обновить импорты
2.4 Объединение modals
# Объединить modal-overrides в modals
cat components/_modal-overrides.scss >> components/_modals.scss
mv components/_modals.scss core/_modals.scss
rm components/_modal-overrides.scss
Почистить дубли в объединенном файле!
2.5 Создать core/_lists.scss
Проанализировать:
_card-list.scss- общие стили списков_list-view.scss- view стили
Объединить общее в core/_lists.scss:
// core/_lists.scss
// ===== BASE LIST =====
.list {
// Базовые стили списка
}
// ===== LIST ITEM =====
.list__item {
// Стили элемента списка
}
// ===== VARIANTS =====
.list--compact { }
.list--cards { }
Этап 3: Layout реорганизация (1.5 часа)
3.1 Создать layout/_grid.scss
# Переместить base-app
mv layout/_base-app.scss layout/_grid.scss
Внутри файла переименовать классы:
.app-layout→.app-grid- Улучшить документацию
3.2 Объединить header компоненты
Анализ:
_navbar.scss- навигация_page-header.scss- заголовок страницы
Варианты:
A) Объединить в один файл:
cat components/_navbar.scss components/_page-header.scss > layout/_header.scss
B) Оставить раздельно но переименовать:
mv components/_navbar.scss layout/_navbar.scss
mv components/_page-header.scss layout/_page-header.scss
Рекомендация: Вариант B (раздельно), но добавить комментарий что это части header'а.
3.3 Rightbar + Calendar
# Переместить rightbar-calendar
mv components/_rightbar-calendar.scss layout/_rightbar.scss
Внутри переименовать классы:
.rightbar-card→.rightbar__card.calendar-wrap→.rightbar__calendar
Этап 4: Features организация (2 часа)
4.1 Feed feature
# Создать feed папку
mkdir -p features/feed
# Переместить компоненты
mv components/_feed-specific.scss features/feed/_layout.scss
mv components/_post-form.scss features/feed/_post-form.scss
mv components/_join-scroller.scss features/feed/_members-scroller.scss
# Если есть feed-header или подобное - тоже сюда
Создать features/feed/_index.scss:
@forward 'layout';
@forward 'post-form';
@forward 'members-scroller';
4.2 Requests feature
mkdir -p features/requests
mv components/_request-list.scss features/requests/_list.scss
mv components/_request-detail.scss features/requests/_detail.scss
mv components/_recipientPicker.scss features/requests/_recipient-picker.scss
Создать features/requests/_index.scss:
@forward 'list';
@forward 'detail';
@forward 'recipient-picker';
4.3 Documents feature
mkdir -p features/documents
mv components/_document-list.scss features/documents/_list.scss
Создать features/documents/_index.scss:
@forward 'list';
4.4 Employees feature
mkdir -p features/employees
mv components/_department-controls.scss features/employees/_controls.scss
mv components/_team-wheel.scss features/employees/_team-viz.scss
Переименовать внутри _team-viz.scss:
.team-wheel→.team-viz- Обновить комментарии
Создать features/employees/_index.scss:
@forward 'controls';
@forward 'team-viz';
4.5 Notifications feature
mkdir -p features/notifications
mv components/_notifications.scss features/notifications/_center.scss
Создать features/notifications/_index.scss:
@forward 'center';
4.6 Chat feature (оставить как есть)
# Chat уже в подпапке - оставить
# features/chat/ уже существует как components/chat/
# Просто переместить папку целиком
mv components/chat features/chat
4.7 Logout modal (куда деть?)
Варианты:
- В
features/auth/_logout-modal.scss - В
core/_modals.scss(добавить специфичные стили) - Удалить если можно использовать общий modal
Рекомендация: Вариант 2 - добавить .modal--logout в core модалы.
# Слить с core/modals
cat components/_logout-modal.scss >> core/_modals.scss
rm components/_logout-modal.scss
Этап 5: Utilities (30 мин)
5.1 Объединить spacing
# Объединить два spacing файла
cat components/_layout-spacing.scss components/_spacing-utils.scss > utilities/_spacing.scss
# Почистить дубли в объединенном файле
# Удалить старые
rm components/_layout-spacing.scss
rm components/_spacing-utils.scss
5.2 Проверить пустую utilities папку
# Удалить старую пустую папку если осталась
rmdir utilities/ 2>/dev/null || true
Этап 6: Обновление импортов (1.5 часа)
6.1 Создать новый main.scss
Создать main.scss.new:
// =============================================================================
// Main Application SCSS
// =============================================================================
// Главный файл для сборки всех стилей приложения (кроме Bootstrap)
// Компилируется в css/app.css
// =============================================================================
// 1. ABSTRACTS - Переменные, миксины, функции
// =============================================================================
@import 'abstracts/index';
// =============================================================================
// 2. CORE - UI Kit компоненты
// =============================================================================
@import 'core/buttons';
@import 'core/forms';
@import 'core/badges';
@import 'core/cards';
@import 'core/lists';
@import 'core/modals';
@import 'core/avatars';
@import 'core/search';
@import 'core/comments';
@import 'core/common';
// =============================================================================
// 3. LAYOUT - Структура приложения
// =============================================================================
@import 'layout/grid';
@import 'layout/navbar';
@import 'layout/page-header';
@import 'layout/sidebar';
@import 'layout/rightbar';
// =============================================================================
// 4. UTILITIES - Вспомогательные классы
// =============================================================================
@import 'utilities/spacing';
// =============================================================================
// 5. FEATURES - Feature-specific стили
// =============================================================================
// Feed
@import 'features/feed/layout';
@import 'features/feed/post-form';
@import 'features/feed/members-scroller';
// Requests
@import 'features/requests/list';
@import 'features/requests/detail';
@import 'features/requests/recipient-picker';
// Documents
@import 'features/documents/list';
// Employees
@import 'features/employees/controls';
@import 'features/employees/team-viz';
// Notifications
@import 'features/notifications/center';
// Chat
@import 'features/chat/message-inputs';
@import 'features/chat/chat-detail';
@import 'features/chat/chat-date-groups';
@import 'features/chat/chat-list-enhanced';
@import 'features/chat/chat-enhanced';
@import 'features/chat/chat-polls';
@import 'features/chat/chat-new-messages-indicator';
@import 'features/chat/message-selection';
@import 'features/chat/message-context-menu';
@import 'features/chat/message-reactions';
// =============================================================================
// ПРИМЕЧАНИЯ
// =============================================================================
// - Bootstrap подключается отдельно через bootstrap-custom.css
// - CSS переменные (--variable) определены в abstracts/_css-variables.scss
// - SCSS переменные ($variable) определены в abstracts/_variables.scss
// - Порядок импортов критичен для CSS каскада
// - Core компоненты = переиспользуемые UI элементы
// - Features = специфичные для конкретных функций приложения
6.2 Очистить custom-bootstrap.scss
Создать custom-bootstrap.scss.new:
// =============================================================================
// Custom Bootstrap Configuration
// =============================================================================
// Этот файл переопределяет переменные Bootstrap ДО его компиляции
// =============================================================================
// 1. ПЕРЕОПРЕДЕЛЕНИЕ BOOTSTRAP ПЕРЕМЕННЫХ
// =============================================================================
// Система отступов (8px grid)
$spacer: 1rem !default;
$spacers: (
0: 0,
1: 0.25rem, // 4px
2: 0.5rem, // 8px
3: 0.75rem, // 12px
4: 1rem, // 16px
5: 1.5rem, // 24px
) !default;
// Grid
$grid-gutter-width: 0.5rem !default;
// Cards
$card-spacer-y: 1rem !default;
$card-spacer-x: 1rem !default;
$card-border-radius: 14px !default;
// Buttons
$btn-padding-y: 0.5rem !default;
$btn-padding-x: 1rem !default;
// Inputs
$input-padding-y: 0.5rem !default;
$input-padding-x: 1rem !default;
// =============================================================================
// 2. ИМПОРТ ABSTRACTS
// =============================================================================
@import "abstracts/variables";
@import "abstracts/mixins";
// =============================================================================
// 3. ИМПОРТ BOOTSTRAP
// =============================================================================
@import "../node_modules/bootstrap/scss/bootstrap";
// =============================================================================
// 4. CSS ПЕРЕМЕННЫЕ (RUNTIME)
// =============================================================================
@import "abstracts/css-variables";
// =============================================================================
// ВСЕ! Никаких компонентов здесь!
// Все компоненты в main.scss → app.css
// =============================================================================
6.3 Тестовая компиляция
cd backend/static
# Backup старых файлов
mv scss/main.scss scss/main.scss.old
mv scss/custom-bootstrap.scss scss/custom-bootstrap.scss.old
# Применить новые
mv scss/main.scss.new scss/main.scss
mv scss/custom-bootstrap.scss.new scss/custom-bootstrap.scss
# Попробовать собрать
npm run build:app 2>&1 | tee build_errors.log
# Если ошибки - смотреть build_errors.log
6.4 Исправление ошибок импортов
# Найти все missing imports
grep -i "can't find" build_errors.log
# Найти старые пути в файлах
grep -r "@import.*components/" scss/
# Исправить пути
Этап 7: Cleanup (30 мин)
7.1 Удалить старую components папку
# Проверить что components пуста
ls -la scss/components/
# Если пусто - удалить
rm -rf scss/components/
# Оставить только .gitkeep или удалить совсем
7.2 Удалить BUTTONS_README.md из старого места
# Переместить в новое место
mv scss/components/BUTTONS_README.md docs/guides/BUTTONS_COMPONENT.md
# Или в core папку
mv scss/components/BUTTONS_README.md scss/core/README_BUTTONS.md
7.3 Проверить дубликаты
# Найти дублирующиеся селекторы
grep -r "^\.badge" scss/ | sort | uniq -d
# Найти дублирующиеся @import
grep -r "@import" scss/main.scss scss/custom-bootstrap.scss | sort | uniq -d
Этап 8: Тестирование (1 час)
8.1 Сборка финальных CSS
# Собрать все
npm run build:app
npm run build:bootstrap
# Проверить размеры
ls -lh css/app.css css/bootstrap-custom.css
# Сравнить с backup
ls -lh css_backup/app.css css_backup/bootstrap-custom.css
8.2 Визуальное тестирование
Проверить страницы:
- Главная / feed
- Список заявок
- Детали заявки
- Список документов
- Список сотрудников
- Уведомления
- Чат
- Logout modal
- Search
- Forms
Чек-лист для каждой страницы:
- Стили загружаются
- Нет визуальных багов
- Кнопки работают
- Формы выглядят нормально
- Responsive работает
8.3 Проверка консоли браузера
// В DevTools Console
// Проверить нет ли 404 на CSS
// Проверить размер загруженных CSS
performance.getEntriesByType('resource')
.filter(r => r.name.includes('.css'))
Этап 9: Документация (30 мин)
9.1 Создать scss/README.md
# SCSS Architecture
## Structure
### Core (UI Kit)
Переиспользуемые UI компоненты: buttons, cards, badges, modals, etc.
### Layout
Структура приложения: grid, header, sidebar, rightbar.
### Features
Feature-specific стили, организованные по функциям приложения.
### Utilities
Utility классы: spacing, helpers, etc.
## Build
`npm run build:app` - компилирует main.scss → app.css
`npm run build:bootstrap` - компилирует bootstrap-custom.scss → bootstrap-custom.css
## Adding New Components
1. Core component → `core/_component.scss`
2. Feature component → `features/feature/_component.scss`
3. Add import to `main.scss`
4. Run `npm run build:app`
9.2 Обновить docs/guides/SCSS_QUICK_REFERENCE.md
Обновить пути и структуру в quick reference.
9.3 Создать отчет о реорганизации
docs/reports/SCSS_REORGANIZATION_COMPLETE.md с:
- Что было изменено
- Новая структура
- Метрики (файлы, размеры, время)
Этап 10: Git commit (15 мин)
# Добавить все изменения
git add -A
# Коммит
git commit -m "refactor: Complete SCSS architecture reorganization
- Restructured 51 SCSS files into logical categories
- Created core/ (UI Kit), layout/, features/, utilities/ folders
- Merged duplicate components: badges (2→1), modals (2→1), spacing (2→1)
- Renamed files: ios-search→search, team-wheel→team-viz, etc.
- Cleaned custom-bootstrap.scss from component imports
- Moved chat components from bootstrap to main.scss
- Updated all imports in main.scss and custom-bootstrap.scss
Structure:
- core/ (9 files) - Reusable UI components
- layout/ (5 files) - App structure
- features/ (16 files in 6 folders) - Feature-specific styles
- utilities/ (1 file) - Utility classes
Benefits:
- Clear categorization
- No duplicate imports
- Logical feature grouping
- Easier maintenance
- Clean architecture
Breaking changes: None (CSS class names unchanged)
"
# Push
git push
📊 Метрики успеха
До реорганизации:
components/ - 30 файлов (хаос)
layout/ - 2 файла
utilities/ - пуста
Дублирующиеся импорты: 8 компонентов
Странные названия: ios-, telegram-, -specific, team-wheel
После реорганизации:
core/ - 9-10 файлов (UI Kit)
layout/ - 4-5 файлов (структура)
features/ - 16 файлов в 6 папках (по функциям)
utilities/ - 1 файл (spacing)
Дублирующиеся импорты: 0
Понятные названия: search, badges, team-viz
Улучшения:
- ✅ Четкая категоризация
- ✅ Нет дубликатов
- ✅ Логичная группировка
- ✅ Легче найти нужный файл
- ✅ Понятные названия
- ✅ Масштабируемая структура
⚠️ Риски и как их минимизировать
Риск 1: Сломать стили
Минимизация:
- Делать backup перед каждым этапом
- Тестировать после каждого этапа
- Не менять CSS классы, только структуру файлов
- Использовать git для отката
Риск 2: Забыть обновить импорт
Минимизация:
- Вести матрицу миграции
- Проверять build errors
- Grep поиск старых путей
- Тестовая компиляция после каждого шага
Риск 3: Потерять изменения
Минимизация:
- Git commit после каждого этапа
- Backup папка
- Документировать прогресс
Риск 4: Слишком долго
Минимизация:
- Разбить на этапы
- Можно делать частями (по 2-3 этапа в день)
- Приоритет: сначала core, потом features
🎯 Альтернативный подход (если мало времени)
Минимальный рефакторинг (2 часа):
- Убрать дубликаты из custom-bootstrap (30 мин)
- Переместить chat в main.scss (15 мин)
- Объединить badges (20 мин)
- Объединить modals (15 мин)
- Объединить spacing (20 мин)
- Переименовать ios- файлы* (20 мин)
Остальное оставить как есть. Будет лучше но не идеально.
📚 Дополнительные ресурсы
- Sass Guidelines - best practices
- 7-1 Pattern - архитектура
- ITCSS - методология
- Atomic Design - для вдохновения
🚨 БОНУСНЫЙ ЭТАП: Полный отказ от Bootstrap
⚠️ ВНИМАНИЕ: Этот этап увеличивает время реализации в 5-7 раз!
Зачем отказываться от Bootstrap?
Плюсы:
- ✅ Полный контроль над кодом
- ✅ Уменьшение размера бандла (~300 KB → ~100 KB)
- ✅ Нет неиспользуемого кода
- ✅ Точная семантика под наше приложение
- ✅ Легче кастомизация
- ✅ Современный CSS (Grid, Custom Properties, Container Queries)
Минусы:
- ❌ Нужно написать свой UI Kit с нуля
- ❌ Время: 40-60 часов вместо 8 часов
- ❌ Нужно поддерживать свой код
- ❌ Протестировать все компоненты
- ❌ Обеспечить кроссбраузерность
- ❌ Документировать систему
Что нужно переписать?
Bootstrap предоставляет (~180 компонентов):
- Grid System - 12-колоночная сетка
- Typography - заголовки, параграфы, списки
- Buttons - 9 вариантов кнопок + sizes + states
- Forms - inputs, selects, checkboxes, radio, validation
- Cards - карточки с header/body/footer
- Modals - модальные окна с backdrop
- Alerts - уведомления
- Badges - бейджи
- Breadcrumbs - хлебные крошки
- Dropdowns - выпадающие меню
- Navbar - навигация
- Pagination - пагинация
- Progress - прогресс-бары
- Spinners - загрузчики
- Tooltips - подсказки
- Popovers - всплывающие окна
- Utilities - margin, padding, flex, display, colors...
Используется в проекте (по grep):
# Найти что реально используется
grep -r "class=" templates/ | grep -oE '\.(btn|card|modal|form-|input-|alert|badge|nav|dropdown|pagination|container|row|col-|d-|m-|p-|text-|bg-|border-)' | sort | uniq -c
Этап 0: Аудит использования Bootstrap (4 часа)
0.1 Собрать статистику использования
cd backend
# Создать скрипт для анализа
cat > analyze_bootstrap_usage.py << 'EOF'
import re
from pathlib import Path
from collections import Counter
# Bootstrap классы
BS_PATTERNS = [
r'\bbtn-\w+', r'\bcard-\w+', r'\bmodal-\w+', r'\bform-\w+',
r'\balert-\w+', r'\bbadge-\w+', r'\bnav-\w+', r'\bcol-\w+',
r'\brow\b', r'\bcontainer\b', r'\bd-\w+', r'\bm[tblrxy]?-\d',
r'\bp[tblrxy]?-\d', r'\btext-\w+', r'\bbg-\w+', r'\bborder-\w+'
]
templates_dir = Path('templates')
all_classes = Counter()
for html_file in templates_dir.rglob('*.html'):
content = html_file.read_text(encoding='utf-8')
for pattern in BS_PATTERNS:
matches = re.findall(pattern, content)
all_classes.update(matches)
print("=== TOP 50 BOOTSTRAP CLASSES ===")
for cls, count in all_classes.most_common(50):
print(f"{cls:30} - {count:4} uses")
print(f"\n=== TOTAL: {len(all_classes)} unique Bootstrap classes ===")
EOF
.venv/Scripts/python analyze_bootstrap_usage.py > bootstrap_usage_report.txt
0.2 Создать матрицу замены
BOOTSTRAP_REPLACEMENT_MATRIX.md:
| Bootstrap класс | Использований | Наш класс | Статус |
|-----------------|---------------|-----------|--------|
| .btn-primary | 150 | .button--primary | ⏳ |
| .card | 120 | .card | ⏳ |
| .modal | 45 | .modal | ⏳ |
| .form-control | 200 | .input | ⏳ |
| .container | 80 | .container | ⏳ |
| .row | 100 | .grid | ⏳ |
| .col-* | 300 | .grid__col | ⏳ |
0.3 Оценить объем работы
# Посчитать templates с Bootstrap
find templates/ -name "*.html" -exec grep -l "btn\|card\|modal\|form-control" {} \; | wc -l
# Посчитать строк кода
find templates/ -name "*.html" | xargs wc -l
Этап B1: Создать свою Grid систему (6 часов)
B1.1 Создать core/_grid.scss
// core/_grid.scss
// =============================================================================
// GRID SYSTEM
// =============================================================================
// Своя grid система на CSS Grid (не flexbox как Bootstrap)
// Переменные
$grid-columns: 12;
$grid-gap: 1rem;
$grid-container-max: 1200px;
// Container
.container {
width: 100%;
max-width: $grid-container-max;
margin: 0 auto;
padding: 0 1rem;
}
.container--fluid {
max-width: 100%;
}
// Grid
.grid {
display: grid;
grid-template-columns: repeat($grid-columns, 1fr);
gap: $grid-gap;
// Responsive gaps
&--gap-sm { gap: 0.5rem; }
&--gap-lg { gap: 2rem; }
}
// Grid columns
@for $i from 1 through $grid-columns {
.grid__col-#{$i} {
grid-column: span $i;
}
}
// Responsive
@media (max-width: 768px) {
.grid__col-12-mobile {
grid-column: span 12;
}
}
// Auto-fit grid
.grid--auto {
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
B1.2 Создать миграционный скрипт
# scripts/replace_bootstrap_grid.py
import re
from pathlib import Path
def replace_bootstrap_grid(content):
# .row → .grid
content = re.sub(r'\brow\b', 'grid', content)
# .col-{n} → .grid__col-{n}
content = re.sub(r'\bcol-(\d+)', r'grid__col-\1', content)
# .container → .container (оставить)
return content
templates_dir = Path('templates')
for html_file in templates_dir.rglob('*.html'):
content = html_file.read_text(encoding='utf-8')
new_content = replace_bootstrap_grid(content)
if content != new_content:
html_file.write_text(new_content, encoding='utf-8')
print(f"Updated: {html_file}")
Этап B2: Создать свою Button систему (4 часа)
// core/_buttons.scss
.button {
// Base styles
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.5rem 1rem;
border: none;
border-radius: 0.375rem;
font-size: 1rem;
font-weight: 500;
line-height: 1.5;
cursor: pointer;
transition: all 0.2s;
&:hover { opacity: 0.85; }
&:active { transform: scale(0.98); }
&:disabled { opacity: 0.5; cursor: not-allowed; }
// Variants
&--primary { background: var(--color-primary); color: white; }
&--secondary { background: var(--color-secondary); color: white; }
&--success { background: var(--color-success); color: white; }
&--danger { background: var(--color-danger); color: white; }
&--outline { background: transparent; border: 1px solid currentColor; }
// Sizes
&--sm { padding: 0.25rem 0.5rem; font-size: 0.875rem; }
&--lg { padding: 0.75rem 1.5rem; font-size: 1.125rem; }
// Full width
&--block { width: 100%; }
}
// Миграция: .btn-primary → .button.button--primary
Этап B3: Создать Form систему (8 часов)
// core/_forms.scss
.form-group {
margin-bottom: 1rem;
}
.label {
display: block;
margin-bottom: 0.25rem;
font-weight: 500;
font-size: 0.875rem;
}
.input,
.textarea,
.select {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-border);
border-radius: 0.375rem;
font-size: 1rem;
transition: border-color 0.2s;
&:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(var(--color-primary-rgb), 0.1);
}
&--error {
border-color: var(--color-danger);
}
&:disabled {
background: var(--color-gray-100);
cursor: not-allowed;
}
}
.textarea {
resize: vertical;
min-height: 100px;
}
.checkbox,
.radio {
// Кастомные checkbox/radio
appearance: none;
width: 1.25rem;
height: 1.25rem;
border: 2px solid var(--color-border);
border-radius: 0.25rem;
cursor: pointer;
&:checked {
background: var(--color-primary);
border-color: var(--color-primary);
}
}
.radio {
border-radius: 50%;
}
Этап B4: Utilities система (4 часа)
// utilities/_spacing.scss
// Margin и Padding генератор
$spacers: (
0: 0,
1: 0.25rem,
2: 0.5rem,
3: 0.75rem,
4: 1rem,
5: 1.5rem,
6: 2rem,
);
@each $key, $value in $spacers {
// Margin
.m-#{$key} { margin: $value; }
.mt-#{$key} { margin-top: $value; }
.mb-#{$key} { margin-bottom: $value; }
.ml-#{$key} { margin-left: $value; }
.mr-#{$key} { margin-right: $value; }
.mx-#{$key} { margin-left: $value; margin-right: $value; }
.my-#{$key} { margin-top: $value; margin-bottom: $value; }
// Padding
.p-#{$key} { padding: $value; }
.pt-#{$key} { padding-top: $value; }
.pb-#{$key} { padding-bottom: $value; }
.pl-#{$key} { padding-left: $value; }
.pr-#{$key} { padding-right: $value; }
.px-#{$key} { padding-left: $value; padding-right: $value; }
.py-#{$key} { padding-top: $value; padding-bottom: $value; }
}
// Display utilities
.d-none { display: none; }
.d-block { display: block; }
.d-flex { display: flex; }
.d-grid { display: grid; }
.d-inline { display: inline; }
.d-inline-block { display: inline-block; }
// Flexbox utilities
.flex-row { flex-direction: row; }
.flex-column { flex-direction: column; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.justify-end { justify-content: flex-end; }
.align-center { align-items: center; }
.align-start { align-items: flex-start; }
.gap-1 { gap: 0.25rem; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 1rem; }
// Text utilities
.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
.text-bold { font-weight: bold; }
.text-sm { font-size: 0.875rem; }
.text-lg { font-size: 1.125rem; }
Этап B5: Cards, Modals, Alerts (6 часов)
Написать свои версии этих компонентов.
Этап B6: Массовая замена в templates (12 часов)
# scripts/migrate_all_bootstrap_classes.py
REPLACEMENTS = {
# Buttons
r'\bbtn\b': 'button',
r'\bbtn-primary\b': 'button--primary',
r'\bbtn-secondary\b': 'button--secondary',
r'\bbtn-sm\b': 'button--sm',
r'\bbtn-lg\b': 'button--lg',
# Forms
r'\bform-control\b': 'input',
r'\bform-group\b': 'form-group',
r'\bform-label\b': 'label',
# Grid
r'\brow\b': 'grid',
r'\bcol-(\d+)': r'grid__col-\1',
# Cards
r'\bcard\b': 'card',
r'\bcard-body\b': 'card__body',
r'\bcard-header\b': 'card__header',
# Utilities
r'\bd-flex\b': 'd-flex',
r'\bjustify-content-center\b': 'justify-center',
# ... еще сотни замен
}
def migrate_template(filepath):
content = filepath.read_text(encoding='utf-8')
for old, new in REPLACEMENTS.items():
content = re.sub(old, new, content)
filepath.write_text(content, encoding='utf-8')
# Запустить на всех templates
for html_file in Path('templates').rglob('*.html'):
migrate_template(html_file)
Этап B7: Удаление Bootstrap (1 час)
# Удалить из package.json
npm uninstall bootstrap
# Удалить custom-bootstrap.scss
rm scss/custom-bootstrap.scss
# Удалить из templates
grep -r "bootstrap-custom.css" templates/
# Заменить на только app.css
# Обновить build скрипты
# Оставить только npm run build:app
Этап B8: Тестирование (8 часов)
Протестировать каждую страницу:
- Визуальная регрессия
- Responsive
- Кроссбраузерность
- Формы работают
- Модалы открываются
- И т.д.
📊 Сравнение подходов
Вариант А: С Bootstrap (рекомендуется сейчас)
Плюсы:
- ⏱️ Быстро: 6-8 часов
- ✅ Проверенное решение
- ✅ Хорошая документация
- ✅ Обновления безопасности
- ✅ Кроссбраузерность из коробки
Минусы:
- 📦 Размер бандла: ~300 KB
- 🔧 Ограничения кастомизации
- 📚 Неиспользуемый код
Итоговый размер:
- bootstrap-custom.css: ~280 KB
- app.css: ~95 KB
- ИТОГО: ~375 KB
Вариант Б: Без Bootstrap (требует времени)
Плюсы:
- 📦 Размер бандла: ~100 KB (в 3 раза меньше!)
- 🎨 Полный контроль
- 🚀 Только нужный код
- 💅 Современный CSS
Минусы:
- ⏱️ Долго: 40-60 часов
- 🔧 Нужна поддержка
- 🐛 Больше багов
- 📚 Нужна документация
Итоговый размер:
- app.css: ~100 KB
- ИТОГО: ~100 KB
🎯 Рекомендуемая стратегия
Поэтапный отказ от Bootstrap:
- Этап 1-10 (6-8 часов): Реорганизация SCSS с Bootstrap
- Использовать проект 2-3 месяца
- Этап B (40-60 часов): Постепенная замена Bootstrap своими стилями
- Сначала Grid → готово за выходные
- Потом Buttons → неделя
- Потом Forms → неделя
- Постепенно все остальное
- Тестирование на продакшене
- Удаление Bootstrap когда все заменено
Преимущества поэтапного подхода:
- ✅ Не ломаем текущий проект
- ✅ Можно тестировать части
- ✅ Откатиться легко
- ✅ Меньше риска
🤔 Мое мнение
Для корпоративного Django приложения:
👍 ЗА отказ от Bootstrap:
- Полный контроль над кодом
- Меньше размер
- Современные CSS возможности
- Нет jQuery зависимостей (если используете Bootstrap JS)
👎 ПРОТИВ отказа от Bootstrap:
- Огромная работа (40-60 часов)
- Нужно поддерживать свой код
- Риск багов
- Уже работает хорошо
Мой совет:
- Сейчас сделать реорганизацию С Bootstrap (8 часов)
- Использовать 1-2 месяца
- Если реально чувствуете ограничения - начать постепенно заменять
- Если все работает нормально - оставить Bootstrap
Большинство крупных проектов используют Bootstrap/Tailwind/Foundation - это нормально и правильно.
Готов начать? Какой вариант выбираем?
A) Реорганизация с Bootstrap (8 часов, безопасно) Б) Полный отказ от Bootstrap (60 часов, амбициозно) В) Поэтапная замена (8 часов сейчас + 40 часов потом)