fa
Feedback

فریب کلاهبرداران را نخورید! تل‌متریو این کانال‌ها را شناسایی و برچسب‌گذاری می‌کند 👉 برای مشاهده برچسب، اشتراک تهیه کنید

Glo Academy

Glo Academy

رفتن به کانال در Telegram

Бесплатные уроки по HTML/CSS/JavaScript, лайфхаки для верстальщика и бесплатные дизайн-макеты для портфолио. По рекламе — @yankovsky_ads Купить рекламу напрямую https://telega.in/c/glo_academy

نمایش بیشتر
3 759
مشترکین
اطلاعاتی وجود ندارد24 ساعت
-57 روز
-2030 روز
آرشیو پست ها
📐 Интернет-магазин ретро-электрики Сложный макет на русском языке без адаптива @glo_academy | #макеты

🧰 Tabler Icons Берёшь иконки из трёх разных наборов, и в интерфейсе они спорят: у одной линия тоньше, у другой углы круглее, третья залита целиком. Даже если не можешь объяснить, что не так, глаз это замечает. В Tabler Icons больше 6200 иконок, и все нарисованы по одной сетке 24×24 с линией в 2 пикселя. Цвет иконка берёт из currentColor, то есть красится так же, как текст рядом. Толщину линии меняет атрибут stroke-width прямо в разметке. На ролике слева одни и те же иконки становятся то тоньше, то толще: тонкие подойдут для мелкого интерфейса, жирные для крупных кнопок, а файл при этом один. Справа меняется только цвет текста в меню, и иконки перекрашиваются вместе с ним. Ставится пакетом под React, Vue, Svelte или Angular, а можно скопировать SVG с tabler.io/icons и вставить в разметку. Лицензия MIT, у тысячи иконок есть залитая версия. https://github.com/tabler/tabler-icons 🔥 — если было полезно @glo_academy | #инструменты

⚡ Цена в любой валюте без регулярок Цену с пробелами и знаком ₽ часто собирают своей функцией с регуляркой. Появятся покупатели из других стран, и её придётся переписывать под доллары. А в JS уже есть форматтер для любой валюты:
const price = (sum, currency, locale) =>
  new Intl.NumberFormat(locale, {
    style: 'currency',
    currency,
  }).format(sum);

price(1234567.5, 'RUB', 'ru-RU'); // 1 234 567,50 ₽
price(1234567.5, 'USD', 'en-US'); // $1,234,567.50
price(1234567.5, 'EUR', 'de-DE'); // 1.234.567,50 €
В currency идёт код валюты из стандарта ISO 4217: RUB, USD, EUR, GBP и любой другой. Локаль решает, как цену пишут в этой стране: какие разделители и где стоит знак. Поэтому у доллара знак впереди, а у рубля в конце. На гифке одна сумма проходит через пять валют. Копейки форматтер дописывает сам, убрать их можно опцией maximumFractionDigits: 0. Работает в Chrome 24+, Firefox 29+, Safari 10+. 🔥 — если было полезно @glo_academy | #js_day

🎨 Зебра в таблице не ломается после фильтра Таблицу делают полосатой через tr:nth-child(odd), и всё хорошо, пока не включишь фильтр. Скрытая строка пропадает с экрана, но в счёте остаётся, и две серые строки встают подряд. Считать только видимые строки можно прямо в селекторе:
/* было: скрытые строки тоже считаются */
tr:nth-child(odd) {
  background: #e4e7f2;
}

/* стало: считаются только видимые */
tr:nth-child(odd of :not(.hidden)) {
  background: #e4e7f2;
}
Запись odd of :not(.hidden) работает в два шага. Сначала браузер отбирает строки без класса hidden, потом нумерует только их и красит нечётные. Скрытые строки в нумерацию больше не попадают. На гифке включается фильтр «Только в наличии». Слева обычный nth-child, и полосы сбиваются. Справа новая запись, полосы идут через одну. Если фильтр прячет строки атрибутом hidden, а не классом, в скобках пиши :not([hidden]). Работает в Chrome 111+, Firefox 113+, Safari 9+. 🔥 — если было полезно @glo_academy | #css_day

🧰 HyperUI Нужна на сайт секция с тарифами или карточки товаров, а верстать их с нуля долго. Готовые UI-библиотеки обычно ставятся пакетом, и ради пары блоков в проект едут лишние зависимости. В HyperUI больше 300 бесплатных блоков на Tailwind: шапки, тарифы, футеры, формы, таблицы, модалки. Ставить ничего не надо. Находишь блок на hyperui.dev, жмёшь Copy и вставляешь разметку к себе. На ролике так переезжает блок тарифов: слева сайт HyperUI, справа твоя страница. Дальше это обычный HTML с классами Tailwind, и править его можно как угодно. Заменил в разметке indigo на emerald, и тариф стал зелёным, как в конце ролика. У многих блоков есть тёмная версия. Написаны блоки под Tailwind 4, в проекте на третьей версии часть классов называется иначе. https://github.com/markmead/hyperui 🔥 — если было полезно @glo_academy | #инструменты

⚡ Прогресс загрузки файла на fetch без XHR await fetch(url) возвращается, как только пришли заголовки. Тело ещё качается, и его можно читать по кускам через response.body.
const res = await fetch(url);
const total = +res.headers.get('Content-Length');
const reader = res.body.getReader();
const chunks = [];
let loaded = 0;
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  chunks.push(value);
  loaded += value.length;
  progress.value = loaded / total;
}
Каждый reader.read() приносит следующий кусок: складываешь длины, делишь на размер файла из Content-Length и двигаешь полосу загрузки. Так умеют Chrome с 43-й версии, Firefox с 65-й и Safari с 10.1. Если сервер сжимает ответ, в Content-Length окажется размер сжатого файла, а reader отдаёт распакованные байты, и прогресс уедет за 100%. Заголовка может и не быть, тогда показывай, сколько мегабайт уже скачано. Прогресс отправки так не посчитать, для неё по-прежнему берут XHR. 🔥 — если было полезно @glo_academy | #js_day

🎨 Тултип у края экрана переворачивается без JS Тултип над кнопкой обычно ставят через position: absolute и bottom: 100%. Стоит кнопке оказаться у верхнего края, и подсказка уезжает за экран. Раньше это чинили JS-библиотекой вроде Floating UI. Сейчас хватает CSS:
.button {
  anchor-name: --save;
}

.tooltip {
  position: absolute;
  position-anchor: --save;
  position-area: top;
  margin-bottom: 8px;
  position-try-fallbacks: flip-block;
}
anchor-name даёт кнопке имя, position-anchor привязывает к нему тултип, а position-area: top ставит его над кнопкой. Последняя строка нужна для края экрана: если сверху места не хватает, flip-block переносит тултип под кнопку. Отступ из margin-bottom переезжает вместе с ним. На гифке кнопка едет вверх. Слева старый способ: подсказку срезает край. Справа она переворачивается вниз. Пересчитывать позицию при скролле и ресайзе браузер тоже будет сам, без обработчиков в JS. Работает в Chrome 129+, Firefox 147+, Safari 26+. 🔥 — если было полезно @glo_academy | #css_day

🧰 svgo Экспортируешь иконку колокольчика из графического редактора, а файл весит почти 2 КБ. Нарисованы в нём две линии, остальное служебное: комментарий редактора, метаданные, пустые группы, скрытый слой и координаты вида 2.000000. Всё это браузер скачивает вместе с иконкой. svgo чистит файл одной командой npx svgo bell.svg. На ролике тот самый колокольчик: красным подсвечено то, что svgo удалит, жёлтым то, что перепишет короче. Было 1911 байт, стало 353, а иконка не изменилась. На странице с двадцатью такими иконками это около 30 КБ лишнего. Обычно svgo подключают к сборщику, и иконки чистятся сами при каждой сборке. svgo удаляет и переименовывает id внутри файла. Если твой CSS или JS находит части иконки по id, отключи в настройках плагин cleanupIds. https://github.com/svg/svgo 🔥 — если было полезно @glo_academy | #инструменты

✂️ Шрифт как в нативном приложении без загрузки файлов Подключаешь красивый шрифт с сервера, и на медленном интернете человек первые секунды смотрит на пустые карточки. Браузер прячет текст, пока файл не догрузится, а потом перерисовывает страницу. Если шрифт не часть бренда, файл можно не грузить совсем.
body {
  font-family: system-ui, sans-serif;
}
system-ui берёт шрифт, который уже стоит у пользователя: San Francisco на macOS и iOS, Segoe UI на Windows, Roboto на Android. Текст виден с первого кадра, и сайт выглядит так же привычно, как приложения в этой системе. На гифке слева карточка ждёт свой шрифт, а справа текст на месте сразу. На каждой системе шрифт свой, поэтому ширина строк немного гуляет: проверяй вёрстку хотя бы на маке и на Windows. sans-serif в конце — запасной вариант для старых браузеров. Работает в Chrome 56+, Firefox 92+, Safari 11+. ❤️ — если было полезно @glo_academy | #snippets

🎨 Звёздочка у обязательного поля озвучится словами Ставишь звёздочку у обязательного поля через ::after — и незрячий пользователь слышит «Телефон звёздочка». Символ до него дошёл, а смысл нет: из «звёздочки» никак не следует, что поле обязательное. Чинится одной косой чертой.
.required::after {
  content: '*' / 'обязательное поле';
}
После значения в content ставь косую черту и пиши альтернативный текст. На экране остаётся та же звёздочка, а скринридер прочитает «Телефон обязательное поле». На гифке форма не меняется, меняется только то, что слышит пользователь: сначала «Телефон звёздочка», потом «Телефон обязательное поле». Так же чинится любой декор из content: галочка, стрелка в хлебных крошках. Если символ нужен только глазам, поставь после черты пустые кавычки, и скринридер его пропустит. Работает в Chrome 77+, Firefox 128+, Safari 17.4+. 🔥 — если было полезно @glo_academy | #css_day

📐 Лендинг жилого комплекса Макет средней сложности без адаптива на русском языке @glo_academy

⚡ Одна строка разлогинит все вкладки сайта Нажал «Выйти», а в соседней вкладке всё ещё открыт кабинет: кнопки живые, данные на месте. Обычно это чинят через localStorage: пишут туда значение, в других вкладках ловят событие storage, парсят строку и удаляют ключ.
const channel = new BroadcastChannel('auth');

// во вкладке, где нажали «Выйти»
channel.postMessage({ type: 'logout' });

// в остальных вкладках сайта
channel.onmessage = (e) => {
  if (e.data.type === 'logout') redirect('/login');
};
new BroadcastChannel('auth') открывает канал с именем auth. Вкладка, где нажали «Выйти», отправляет в него объект через postMessage. Остальные вкладки открыли канал с тем же именем и ловят этот объект в onmessage. Объект уходит как есть, без JSON.stringify, и в хранилище после него ничего не остаётся. Отправитель своё сообщение не получает, поэтому вкладку с кнопкой уводи на вход отдельно. Работает во всех браузерах, в Safari с версии 15.4. 🔥 — если было полезно @glo_academy | #js_day

🧰 pico Нужна страница входа или админка для пет-проекта, а возиться со стилями полей и кнопок не хочется. Без CSS браузер вытягивает форму в одну строку шрифтом Times — слева на ролике видно, как это выглядит. Тащить ради пары экранов Bootstrap с классами на каждом теге — перебор. Pico стилизует сами теги. Подключаешь один файл, пишешь обычные form, label, input и button, и форма выглядит как справа на ролике, хотя классов в разметке нет ни одного. Состояния pico берёт из атрибутов доступности, которые в форме стоит ставить и так: поле с aria-invalid краснеет, чекбокс с role="switch" становится тумблером, кнопка с aria-busy показывает спиннер. Тёмная тема подхватывается из настроек системы, а data-theme="dark" включает её вручную — на всю страницу или на один блок. Весит pico около 11 КБ в gzip, в комплекте 20 цветовых схем. https://github.com/picocss/pico 🔥 — если было полезно @glo_academy | #инструменты

😎
😎

✂️ Состояние элемента в атрибуте вместо трёх классов classList.add('loading'), потом add('error'), где-то забыли снять — и на элементе одновременно висят loading и completed. Какой стиль победит, решает порядок правил в файле. Вместо трёх классов держи состояние в одном data-атрибуте:
<div class="order" data-state="loading">…</div>

order.dataset.state = 'completed';

.order[data-state="loading"]   { opacity: 0.5; }
.order[data-state="error"]     { border-color: red; }
.order[data-state="completed"] { border-color: green; }
У атрибута всегда одно значение: новое вытесняет старое, двух состояний сразу не бывает. Из JS оно меняется одной строкой через dataset.state, а CSS цепляется за него селектором [data-state="…"]. На гифке: клик по вкладке пишет новое значение в атрибут, и карточка заказа перекрашивается сама. Селектор сравнивает значение целиком. Если в атрибут попадёт «loading completed», не сработает ни одно правило — поломку видно сразу. ❤️ — если было полезно @glo_academy | #snippets

🎨 Две карточки в гриде не растягиваются на пол-экрана Сетка на repeat(auto-fit, minmax(240px, 1fr)) отлично выглядит на двенадцати карточках. Но на странице, где карточек две, каждая растягивается на половину экрана, и макет разваливается. Обычно это лечат max-width на карточке или отдельным классом для коротких списков.
.cards {
  display: grid;
  grid-template-columns:
    repeat(auto-fill, minmax(240px, 1fr));
  gap: 16px;
}
Разница в одном слове. auto-fit создаёт столько колонок, сколько влезает по ширине, а пустые схлопывает в ноль, и две карточки делят всю ширину между собой. auto-fill пустые колонки оставляет: они невидимы, но место держат, поэтому карточки остаются той же ширины, что и в полной сетке. Когда карточек всегда больше, чем колонок, разницы нет. Она вылезает на коротких списках: результаты поиска, два товара в категории, одна новость. На ролике слева auto-fit, справа auto-fill. 🔥 — если было полезно @glo_academy | #css_day

📐 Лендинг ювелирного магазина Макет лендинга средней сложности без адаптива на русском языке @glo_academy