Corp/FOUC_FIX.md
Игорь Надеин 263a7cbe90 Исправлена разметка и стили страницы профиля
- Заменен лишний div.container на стандартный container для единообразия
- Заменен класс .card на .ios-card в секции активности для устранения конфликтов стилей
- Добавлен card-list.css в base.html для корректного отображения карточек новостей
- Добавлены CSS правила для правильного поведения .row и колонок в grid-layout
- Исправлена проблема с выходом календаря за границы страницы
- Убраны видимые скроллбары у modal-body и modal-content для чистого UI
2025-11-29 13:52:21 +03:00

8.4 KiB
Raw Blame History

Устранение FOUC (Flash of Unstyled Content)

Проблема

При переходе между страницами происходило мигание белой темы, даже если пользователь выбрал тёмную тему.

Ошибочное предположение

❌ "Стили не кэшируются" - НЕверно!

  • Статические файлы (CSS/JS) всегда кэшируются браузером
  • Nginx выдаёт их с Cache-Control: public, max-age=31536000
  • Проблема была НЕ в кэше!

Настоящая причина

✅ Неправильный порядок выполнения скриптов:

1. HTML загружается с data-bs-theme="auto" 
2. CSS загружается и применяется (тема = auto = светлая)
3. 🔥 МИГАНИЕ БЕЛОЙ ТЕМЫ 🔥
4. JavaScript выполняется и меняет тему на тёмную

Решение

До (неправильно)

<html data-bs-theme="auto">
<head>
  <link rel="stylesheet" href="bootstrap.css">
  <!-- Стили применяются с auto = светлая тема -->
</head>
<body>
  <script defer>
    // Слишком поздно! Уже видели белую тему
    document.documentElement.setAttribute('data-bs-theme', 'dark');
  </script>
</body>
</html>

После (правильно)

<html>
<head>
  <!-- 1. СРАЗУ применяем тему ДО загрузки CSS -->
  <script src="themeInitializer.js"></script>
  
  <!-- 2. Preload критичных стилей -->
  <link rel="preload" href="bootstrap.css" as="style">
  <link rel="preload" href="variables.css" as="style">
  
  <!-- 3. Теперь загружаем CSS - тема уже установлена! -->
  <link rel="stylesheet" href="bootstrap.css">
  <link rel="stylesheet" href="variables.css">
</head>
<body>
  <!-- Никакого мигания! -->
</body>
</html>

Как работает themeInitializer.js

Синхронное выполнение (НЕ defer, НЕ async, НЕ module)

(function() {
  'use strict';
  
  // 1. Читаем сохранённую тему из localStorage
  const savedTheme = localStorage.getItem('theme') || 'auto';
  
  // 2. Определяем эффективную тему
  let effectiveTheme = savedTheme;
  if (savedTheme === 'auto') {
    const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
    effectiveTheme = prefersDark.matches ? 'dark' : 'light';
  }
  
  // 3. НЕМЕДЛЕННО устанавливаем атрибут
  document.documentElement.setAttribute('data-bs-theme', effectiveTheme);
  
  // Всё это происходит ДО парсинга <body> и ДО загрузки CSS!
})();

Почему это работает

  • ✅ Скрипт выполняется синхронно в <head>
  • ✅ Браузер блокируется до выполнения скрипта
  • ✅ Атрибут data-bs-theme устанавливается ДО применения CSS
  • ✅ CSS применяется сразу с правильной темой
  • ✅ Никакого мигания!

Preload критичных стилей

Зачем нужен preload?

<link rel="preload" href="bootstrap.css" as="style">
<link rel="stylesheet" href="bootstrap.css">

Без preload:

  1. Браузер парсит HTML
  2. Доходит до <link rel="stylesheet">
  3. Начинает загрузку CSS (задержка!)
  4. Парсит CSS
  5. Применяет стили

С preload:

  1. Браузер парсит HTML
  2. Видит <link rel="preload"> → начинает загрузку параллельно
  3. Доходит до <link rel="stylesheet"> → CSS уже загружен!
  4. Сразу применяет стили

Результат: ускорение на 100-300ms

Кэширование и FOUC

Важно понимать!

Кэш НЕ решает проблему FOUC:

С кэшем (неправильный порядок):
1. HTML из disk cache (0ms)
2. CSS из disk cache (0ms) ← быстро!
3. 🔥 Но всё равно мигание! 🔥
4. JS меняет тему (слишком поздно)

С кэшем (правильный порядок):
1. HTML из disk cache (0ms)
2. themeInitializer.js СИНХРОННО (0ms)
3. data-bs-theme установлен ДО парсинга CSS
4. CSS из disk cache (0ms) применяется с правильной темой
5. ✅ Никакого мигания!

Вывод:

  • Кэш ускоряет загрузку
  • Правильный порядок устраняет FOUC
  • Нужны оба решения!

Тестирование

Как проверить, что FOUC устранён

  1. Выберите тёмную тему на сайте
  2. Hard refresh (Ctrl+Shift+R) для сброса кэша
  3. Быстро переходите между страницами
  4. Результат: НЕ должно быть белых вспышек

Проверка в DevTools

// Откройте Console
// Проверьте, что атрибут установлен ДО DOMContentLoaded
console.log('Theme on parse:', document.documentElement.getAttribute('data-bs-theme'));

// Должно вывести 'dark' или 'light', НЕ 'auto'

Проверка порядка загрузки

Network tab → фильтр "CSS":

  1. ✅ themeInitializer.js - загружен первым
  2. ✅ bootstrap.css (preload)
  3. ✅ variables.css (preload)
  4. ✅ Остальные стили

Частые ошибки

❌ Использование defer/async

<!-- НЕПРАВИЛЬНО: defer выполнится ПОСЛЕ парсинга -->
<script src="themeInitializer.js" defer></script>

<!-- ПРАВИЛЬНО: синхронное выполнение -->
<script src="themeInitializer.js"></script>

❌ Использование type="module"

<!-- НЕПРАВИЛЬНО: модули выполняются с defer -->
<script type="module" src="themeInitializer.js"></script>

<!-- ПРАВИЛЬНО: обычный скрипт -->
<script src="themeInitializer.js"></script>

❌ Установка темы в DOMContentLoaded

// НЕПРАВИЛЬНО: слишком поздно!
document.addEventListener('DOMContentLoaded', () => {
  document.documentElement.setAttribute('data-bs-theme', 'dark');
});

// ПРАВИЛЬНО: немедленно
document.documentElement.setAttribute('data-bs-theme', 'dark');

❌ data-bs-theme="auto" в HTML

<!-- НЕПРАВИЛЬНО: браузер применит auto ДО JS -->
<html data-bs-theme="auto">

<!-- ПРАВИЛЬНО: без атрибута, JS установит сразу -->
<html>
  <head>
    <script>/* устанавливаем тему */</script>

Совместимость с кэшированием

Статика кэшируется правильно

themeInitializer.js:
- Cache-Control: public, max-age=31536000
- Loaded: (disk cache)

bootstrap.css:
- Cache-Control: public, max-age=31536000  
- Loaded: (disk cache)

HTML кэшируется с правильным порядком

base.html:
- Cache-Control: private, max-age=60
- Loaded: (disk cache)
- Порядок скриптов сохранён!

Результат:

  • ✅ Мгновенная загрузка из кэша
  • ✅ Без мигания темы
  • ✅ Идеальный UX!

Резюме

Проблема Решение Результат
Мигание белой темы Синхронный скрипт в <head> ✅ Нет FOUC
Медленная загрузка CSS Preload критичных стилей ✅ +100-300ms
"Стили не кэшируются" Это было заблуждение ✅ Всегда кэшировались
data-bs-theme="auto" Убрали из HTML ✅ JS устанавливает сразу

Главный вывод: Проблема была НЕ в кэшировании, а в порядке выполнения! 🎯