Личное · Референсы

Стиль-борд

Коллекция UI-элементов, которые мне нравятся и которые я использую в проектах. Ссылаюсь на неё, чтобы точно понимать, что имеется в виду — конкретную таблицу, прогрессбар или шаблон. Добавляется постепенно, по мере появления удачных решений.

Прогрессбар загрузки файлов

Откуда: PTO.pro → карточка задачи → Исходные данные Дата: 27.08.2026 Формат: HTML + CSS

Компактный прогресс на строку файла: слева имя (обрезается многоточием), в середине тонкая шкала с бирюзово-синим градиентом, справа проценты и крестик отмены. Занимает мало места, читается быстро, легко ставится в стек. Использую в модалках загрузки и в drag-and-drop зонах.

Смета_подряд_объект_Загорьевская_2026-08.pdf
32%
Приложение_1_чертежи.dwg
78%
Отчёт_ПТО.xlsx
100%
Показать HTML
<div class="upload">
  <div class="upload-row">
    <span class="upload-name">имя-файла.pdf</span>
    <div class="upload-bar"><div class="upload-bar-fill" style="width:32%"></div></div>
    <span class="upload-pct">32%</span>
    <button class="upload-cancel">×</button>
  </div>
</div>
Показать CSS
.upload{ padding:8px 10px; border:1px solid #e2e8f0;
  border-radius:10px; background:#fff; }
.upload-row{ display:flex; align-items:center; gap:10px; font-size:13px; }
.upload-name{ flex:0 1 auto; min-width:0; max-width:38%; overflow:hidden;
  text-overflow:ellipsis; white-space:nowrap; font-weight:600; }
.upload-bar{ flex:1 1 auto; height:8px; background:#e5e7eb;
  border-radius:4px; overflow:hidden; }
.upload-bar-fill{ height:100%; transition:width .12s ease-out;
  background:linear-gradient(90deg, #0ABAB5, #2563eb); }
.upload-pct{ flex:0 0 auto; min-width:34px; text-align:right;
  font-variant-numeric:tabular-nums; font-weight:600; font-size:12px; }
.upload-cancel{ flex:0 0 auto; width:22px; height:22px;
  background:transparent; border:0; color:#94a3b8;
  font-size:16px; line-height:1; cursor:pointer; border-radius:4px; }
.upload-cancel:hover{ color:#dc2626; background:#fef2f2; }

Полоса прокрутки: тонкая, появляется при наведении, затухает

Откуда: «Связь» (m.alldocs.pro) → лента сообщений, списки, настройки Дата: 17.09.2026 Формат: CSS + 25 строк JS

Системный скролл — широкий, со стрелками и всегда на виду. Здесь он 10px, без стрелок, со скруглённым ползунком и колеёй, полупрозрачный (серый днём, светло-серый в тёмной теме). По умолчанию невидим: появляется, когда курсор входит в область или идёт прокрутка, и плавно затухает через секунду; пока курсор на самой полосе — держится. Место под полосу зарезервировано, контент не прыгает. Работает в Chrome/Edge/Safari и Firefox 128+.

светлая тема

Абзац для прокрутки. Полоса появляется, когда курсор входит в область или при прокрутке колесом, и затухает через секунду бездействия.

Пока курсор лежит на самой полосе (правые 16px области), она не прячется — можно тянуть ползунок.

Ширина 10px, без стрелок сверху и снизу, ползунок и колея скруглены; днём серые, ночью — светло-серые полупрозрачные.

Место под полосу зарезервировано всегда, поэтому контент не дёргается, когда она появляется.

Псевдоэлементы ::-webkit-scrollbar не анимируются — затухание идёт через зарегистрированное свойство --sb (0…1), которое умеет transition, а цвет считается от него.

Firefox стилизуется через стандартные scrollbar-width / scrollbar-color; отдельно от webkit-правил, потому что Chrome 121+ при заданном scrollbar-color игнорирует ::-webkit-scrollbar.

Ещё немного текста, чтобы было что прокручивать до самого конца области.

тёмная тема

Абзац для прокрутки. Полоса появляется, когда курсор входит в область или при прокрутке колесом, и затухает через секунду бездействия.

Пока курсор лежит на самой полосе (правые 16px области), она не прячется — можно тянуть ползунок.

Ширина 10px, без стрелок сверху и снизу, ползунок и колея скруглены; днём серые, ночью — светло-серые полупрозрачные.

Место под полосу зарезервировано всегда, поэтому контент не дёргается, когда она появляется.

Псевдоэлементы ::-webkit-scrollbar не анимируются — затухание идёт через зарегистрированное свойство --sb (0…1), которое умеет transition, а цвет считается от него.

Firefox стилизуется через стандартные scrollbar-width / scrollbar-color; отдельно от webkit-правил, потому что Chrome 121+ при заданном scrollbar-color игнорирует ::-webkit-scrollbar.

Ещё немного текста, чтобы было что прокручивать до самого конца области.

Показать CSS
/* Цвета: компоненты + альфы отдельно, чтобы затухать через --sb */
:root{ --scroll-rgb:60 70 85; --scroll-thumb-a:.28; --scroll-thumb-hover-a:.45; --scroll-track-a:.09; }
:root[data-theme="dark"]{ --scroll-rgb:154 165 177; --scroll-thumb-a:.35; --scroll-thumb-hover-a:.55; --scroll-track-a:.10; }

/* Псевдоэлементы скролла не анимируются — анимируем зарегистрированное свойство */
@property --sb{ syntax:'<number>'; inherits:true; initial-value:0; }
.scroll-area{ --sb:0; transition:--sb .4s ease; }               /* затухание */
.scroll-area.scroll-visible{ --sb:1; transition-duration:.15s; } /* появление */

::-webkit-scrollbar{ width:10px; height:10px; }
::-webkit-scrollbar-button{ display:none; width:0; height:0; }   /* без стрелок */
::-webkit-scrollbar-corner{ background:transparent; }
::-webkit-scrollbar-track{ background:rgb(var(--scroll-rgb) / calc(var(--scroll-track-a) * var(--sb))); border-radius:10px; }
::-webkit-scrollbar-thumb{
  background:rgb(var(--scroll-rgb) / calc(var(--scroll-thumb-a) * var(--sb)));
  border-radius:10px;
}
.scroll-visible::-webkit-scrollbar-thumb:hover{ background-color:rgb(var(--scroll-rgb) / var(--scroll-thumb-hover-a)); }

/* Firefox — только здесь: Chrome 121+ при scrollbar-color игнорирует webkit-правила */
@supports not selector(::-webkit-scrollbar){
  *{ scrollbar-width:thin;
     scrollbar-color:rgb(var(--scroll-rgb) / calc(var(--scroll-thumb-a) * var(--sb)))
                     rgb(var(--scroll-rgb) / calc(var(--scroll-track-a) * var(--sb))); }
}
Показать JS
// Показать при входе курсора в область и при прокрутке; спрятать через секунду
// бездействия; пока курсор на самой полосе (правые 16px) — держать.
const SCROLL_AREAS = '.scroll-area';   // селектор прокручиваемых контейнеров
const SCROLL_HIDE_MS = 1000;
const scrollTimers = new Map();

function revealScrollbar(node, sticky = false) {
  node.classList.add('scroll-visible');
  clearTimeout(scrollTimers.get(node));
  scrollTimers.delete(node);
  if (!sticky) scrollTimers.set(node, setTimeout(() => {
    node.classList.remove('scroll-visible'); scrollTimers.delete(node);
  }, SCROLL_HIDE_MS));
}

document.addEventListener('scroll', (e) => {
  if (e.target?.classList) revealScrollbar(e.target);
}, true);

let hoveredScrollArea = null;
document.addEventListener('mousemove', (e) => {
  const area = e.target.closest?.(SCROLL_AREAS) || null;
  if (area && area.scrollHeight > area.clientHeight + 1) {
    const onBar = area.getBoundingClientRect().right - e.clientX <= 16;
    if (onBar) revealScrollbar(area, true);
    else if (area !== hoveredScrollArea || (!scrollTimers.has(area) && area.classList.contains('scroll-visible'))) {
      revealScrollbar(area);
    }
  }
  hoveredScrollArea = area;
});