ru
Feedback
Glo Academy

Glo Academy

Открыть в Telegram

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

Больше
3 772
Подписчики
-124 часа
-77 дней
-4130 дней
Архив постов
Таймаут для fetch без setTimeout Таймаут для fetch обычно собирают из трёх частей: AbortController, setTimeout, который дёргает abort(), и clearTimeout в finally.
try {
  const res = await fetch(url, {
    signal: AbortSignal.timeout(5000)
  });
  const data = await res.json();
} catch (err) {
  if (err.name === 'TimeoutError') {
    // ответа нет 5 секунд
  }
}
AbortSignal.timeout(5000) создаёт сигнал, который сам сработает через 5 секунд. Передаёшь его в fetch как обычный signal, таймер живёт внутри сигнала, чистить его в finally не нужно. Ошибка приходит с именем TimeoutError, а не AbortError — по нему в catch отличаешь «сервер не ответил» от «пользователь отменил». Таймер не останавливается после первого await. Если сервер прислал заголовки, а тело отдаёт по капле, то res.json() тоже упадёт с TimeoutError, когда 5 секунд истекут. Отдельный таймаут на чтение ответа ставить не нужно. Работает в Chrome с 124-й версии, Firefox со 100-й, Safari с 16-й. 🔥 — если было полезно @glo_academy | #js_day

🧰 NumberFlow Цена в калькуляторе тарифа меняется рывком: было 199, стало 249, и глаз не успевает понять, что произошло. Обычно либо оставляют как есть, либо пишут свой счётчик на setInterval, который перебирает числа по одному. NumberFlow крутит каждый разряд отдельно, как барабан механического счётчика: в 199 → 249 сотни поднимаются на единицу, десятки прокручиваются с 9 на 4, последняя девятка стоит на месте. Форматирование через Intl.NumberFormat: валюта, разделители тысяч, суффикс вроде «/мес» встают сами, а когда разрядов становится больше, новая цифра выезжает, старая гаснет. На ролике слева обычная подмена, справа NumberFlow. Без зависимостей, есть пакеты под React, Vue, Svelte и веб-компонент для чистого JS: flow.update(249) — и всё. Уважает prefers-reduced-motion; в старых браузерах без mod() и linear() число просто подменяется, без анимации. https://github.com/barvian/number-flow 🔥 — если было полезно @glo_academy | #инструменты

Так или не так, скажите? 😂 @glo_academy
Так или не так, скажите? 😂 @glo_academy

✂️ Длинное название не растягивает карточку Название товара на три строки — и кнопки «В корзину» в ряду встают на разной высоте. В JS обрезают по числу символов, но буквы разной ширины: где-то оборвёт на полуслове, где-то не влезет. CSS режет по ширине блока:
/* одна строка */
.title {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* три строки */
.desc {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
Одна строка: nowrap запрещает перенос, hidden прячет хвост, ellipsis дорисовывает многоточие. Несколько строк: -webkit-line-clamp задаёт, сколько оставить; соседние три строки обязательны. На гифке: без обрезки карточки в ряду разной высоты, с обрезкой — ровные. Однострочному варианту нужна ширина у элемента или родителя. В разметке текст целиком, поиск его найдёт. Префикс -webkit- обязателен: без него line-clamp понимает только Safari 18.2+. ❤️ — если понравился пост @glo_academy | #snippets

🎨 Колонки едут в противоположные стороны при скролле Три колонки карточек: при прокрутке крайние уезжают вверх, средняя — вниз. Раньше это делали слушателем scroll и transform в JS, теперь хватает CSS:
.col {
  animation: up linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 100%;
}
.col:nth-child(2) { animation-name: down; }

@keyframes up   { to { translate: 0 -30% } }
@keyframes down { to { translate: 0  30% } }
view() привязывает анимацию к положению колонки на экране: проехала колонка — продвинулись кадры. animation-range задаёт участок: от появления снизу (entry 0%) до ухода за верхний край (cover 100%). В @keyframes крайние колонки едут на 30% высоты вверх, средняя — вниз. На гифке ползунок справа — положение скролла, колонки едут за ним. Обработчика scroll и расчётов на кадр нет. animation-timeline ставь после шорткода animation, иначе он его сбросит. Chrome 115+, Safari 26+, Firefox пока Nightly — оборачивай в @supports. 🔥 — если было полезно @glo_academy | #css_day

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

Node распаковывает ZIP без сторонних библиотек Раньше zip в Node открывали пакетом — adm-zip или unzipper. С Node 26.8 это умеет сам node:zlib.
import { ZipBuffer } from 'node:zlib';
import { readFileSync, writeFileSync } from 'node:fs';

const zip = new ZipBuffer(readFileSync('archive.zip'));
for (const [name, entry] of zip) console.log(name, entry.size);

await zip.add('readme.txt', Buffer.from('Привет'));
zip.delete('draft.txt');
writeFileSync('archive.zip', await zip.toBuffer());
Конструктор берёт архив буфером и даёт перебирать записи. add кладёт файл, delete убирает, toBuffer собирает архив заново. Главный выигрыш — память. adm-zip читает архив целиком, ZipBuffer берёт записи по одной прямо из буфера, а ZipFile.stream() отдаёт запись потоком. В maxSize ставишь предел на распакованный размер: архив на пару килобайт, разворачивающийся в гигабайты, дальше лимита не пойдёт. Фича экспериментальная и пока только в 26-й ветке: LTS она станет 28 октября. 🔥 — если было полезно @glo_academy | #js_day

Акция ко Дню рождения Glo Academy стартовала 🎉 Пару дней назад школе исполнилось 10 лет — по этому поводу принимаем поздравл
Акция ко Дню рождения Glo Academy стартовала 🎉 Пару дней назад школе исполнилось 10 лет — по этому поводу принимаем поздравления и проводим большую акцию, которая продлится до 14 сентября В ней участвуют наши основные курсы. На каждый из них можно записаться с большой скидкой В списке появились новые программы: по Vue, React + Next, TypeScript и большой обновленный курс Фуллстек-разработчик Есть рассрочка до 12 месяцев для нескольких стран, а также промокод ДР10, который даст дополнительные приятные условия Все детали — на странице акции: 👉Выбрать курс https://glo.academy/bday/ А в тг-канале проведем отдельный розыгрыш среди участников акции, подключайтесь: https://t.me/+lMQho8IDDTo2ZDZi

🧰 swup Между страницами обычного сайта белая вспышка: браузер выгружает одну и рисует другую с нуля. Ради плавности проекты переезжают на SPA и получают в нагрузку роутер, состояние и гидратацию. swup перехватывает клик по ссылке, подгружает следующую страницу и подменяет только контейнер с контентом, а смену анимирует обычным CSS-переходом. Бэкенд любой: PHP, Django, статика из папки. На ролике слева обычный переход, справа он же через swup. Плагины добавляют прогресс-бар, предзагрузку ссылки под курсором и анимацию отправки форм. Опция native: true переводит анимацию на встроенный в браузер View Transitions API там, где он есть. Версия 4.10 вышла на прошлой неделе. https://github.com/swup/swup 🔥 — если было полезно @glo_academy | #инструменты

Блочная модель на собаках @glo_academy
Блочная модель на собаках @glo_academy

✂️ Поле телефона открывает цифровую клавиатуру Открываешь форму на телефоне, тапаешь в поле для номера — и вылезает буквенная клавиатура. Человек жмёт «123», ищет плюс, промахивается по цифрам. На этом месте формы и бросают.

<label>
  Телефон
  <input type="tel" inputmode="tel">
</label>

<label>
  Индекс
  <input type="text" inputmode="numeric">
</label>
За клавиатуру отвечает отдельный атрибут — inputmode. type говорит браузеру, какие данные в поле и как их проверять, а inputmode — только какую клавиатуру открыть на мобильном. Поэтому их ставят вместе: type="tel" inputmode="tel", и человек видит телефонную клавиатуру вместо букв. Ещё есть numeric, decimal, email, url и search. Для индекса, кода из смс и номера карты бери type="text" с inputmode="numeric": у type="number" сбоку стрелки-спиннеры, а ведущие нули он съедает — индекс 01234 станет 1234. ❤️ — если полезно @glo_academy | #snippets

🎨 Выравниваем текст без <br> Заголовок в макете смотрелся ровно, а в браузере последнее слово повисло одно на второй строке. Обычно это лечат руками: ставят <br> в нужном месте или склеивают два последних слова через &nbsp;. Держится до первой правки текста — копирайтер дописал слово, и перенос встал посреди фразы.

h1 {
  text-wrap: balance;
}
Вместо ручных переносов — одно свойство на заголовок. text-wrap: balance заставляет браузер перебрать точки переноса и выбрать ту раскладку, где строки выходят примерно одной длины. Одинокое слово подтягивается наверх, а разметку трогать не нужно: поменяется текст — браузер пересчитает заново. Рассчитано на короткие блоки: заголовки, подписи к карточкам. Абзац на десять строк браузер балансировать не станет — дорого по отрисовке. Chrome 114+, Firefox 121+, Safari 17.5+; старые браузеры строку проигнорируют. 🔥 — если полезно @glo_academy | #js_day

Нашей школе сегодня аж 10 лет! Кругленькая дата)) Обычно в этот день у нас стартует большая акция ко дню рождения с разными н
Нашей школе сегодня аж 10 лет! Кругленькая дата)) Обычно в этот день у нас стартует большая акция ко дню рождения с разными новыми программами Но сегодня решили отдохнуть, а все активности перенести уже на завтра Столько всего было за это время, вы не представляете: много людей приходило и уходило — и из команды, и из преподавателей, и студентов уже набралось за 10 тысяч человек В разное время к проекту были причастны разные классные люди, для которых, надеюсь, этот опыт остался приятным теплым воспоминанием Вообще пока собираю этот пост, становится и грустно, и весело, и приятно за этот пройденный путь, хах, хорошо, что это не живая речь)) В общем — с днём рождения этот проект! И спасибо каждому из вас, кто читает посты здесь или в других наших каналах, ходите на интенсивы, изучаете с нами языки и инструменты. Спасибо! ❤️ Завтра стартанем традиционну большую акцию по случаю дня рождения школы — и там представим новые блоки и обновления программ под современные треования Как обычно самыми первыми обо всём узнают подписчики закрытого канала, ну а потом уже выкатим на всех Если вы хотите быть среди первых — переходите по ссылке, через пару часов админ всех запустит) 👉Попасть в сверхъсекретный тг-канал Хорошего вам воскресенья 😍

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

Один хук заменяет три useState в форме Знакомый набор: состояние под ответ сервера, под ошибку, под «сейчас отправляется» — плюс try/catch/finally, где забываешь снять флаг и кнопка остаётся серой навсегда.

async function subscribe(prevState, formData) {
  const email = formData.get('email');
  if (!email.includes('@')) return { message: 'Проверь почту' };

  await api.subscribe(email);
  return { message: 'Готово' };
}

const [state, formAction, isPending] = useActionState(subscribe, {});

<form action={formAction}>
  <input name="email" />
  <button disabled={isPending}>Подписаться</button>
</form>
useActionState убирает обвязку. Функции приходит formData, она возвращает новое состояние, а isPending React считает сам — это настоящий статус запроса, не флаг на глазок На всякий случай: если бросишь исключение вместо возврата состояния — React отменит очередь вызовов и уронит ближайший Error Boundary. Лови ошибку внутри действия, возвращай её состоянием 🔥 — если полезно @glo_academy | #js_day

🧰 anime.js Если GSAP кажется тяжеловатым, а на чистом CSS уже не вывозишь — держи anime.js. Четвёртая версия работает через ES-модули, из коробки умеет stagger-анимации (когда элементы анимируются по очереди с задержкой), крутит SVG, CSS-свойства и просто JS-объекты. Апи простой: указал что анимировать, задал easing — готово. https://github.com/juliangarnier/anime 🔥 — если было полезно @glo_academy | #инструменты

👩‍💻👩‍💻 @glo_academy
👩‍💻👩‍💻 @glo_academy

✂️ Переключатель-тумблер из чекбокса без JS Тумблер собирается из обычного чекбокса, без лишних элементов: сам <input> уезжает за экран, а рядом стоит <label>, стилизованный под переключатель. Кружок внутри рисуется через ::after.

.switch {
  position: relative;
  display: inline-block;
  width: 46px; height: 24px;
  background: rgba(0,0,0,.25);
  border-radius: 22px;
  transition: .3s;
}
.switch::after {
  content: '';
  position: absolute;
  inset: 1px auto auto 1px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #fff;
  transition: .3s;
}
:checked + .switch { background: #7983ff; }
:checked + .switch::after { transform: translateX(22px); }
Дальше всё делает селектор :checked — двигает кружок вправо и красит подложку. Состояние хранит сам чекбокс, JS не участвует. Только не прячь его через display: none: элемент выпадет из фокуса, и с тумблером перестанут работать клавиатура и скринридеры. Уводи за экран позиционированием. ❤️ — если понравился пост @glo_academy | #snippets

🎨 Фокус на кнопке подсвечивает всю карточку Кнопка внутри карточки при табе получает свою рамку фокуса — и она едет вместе с кнопкой, если та меняет размер или прыгает по карточке. Выглядит криво.

.bento-card:has(button:focus-visible) {
  outline: 2px solid var(--color-primary);
}

.bento-card button {
  outline: none;
}
Вместо рамки на самой кнопке подсвечиваем родителя через :has. :has() — это селектор наоборот: обычно ты стилизуешь детей по родителю, а тут родитель проверяет, что происходит внутри него. .bento-card:has(button:focus-visible) читается как «карточка, в которой кнопка сейчас в фокусе от клавиатуры» — и рамку рисуем уже на всей карточке, а не на кнопке. :focus-visible, а не :focus — иначе рамка будет появляться и при клике мышкой, а не только при навигации с клавиатуры. Chrome 105+, Firefox 121+, Safari 15.4+. 🔥 — если было полезно @glo_academy | #css_day