Glo Academy
Ir al canal en Telegram
Бесплатные уроки по HTML/CSS/JavaScript, лайфхаки для верстальщика и бесплатные дизайн-макеты для портфолио. По рекламе — @yankovsky_ads Купить рекламу напрямую https://telega.in/c/glo_academy
Mostrar más3 754
Suscriptores
-124 horas
-37 días
-930 días
Archivo de publicaciones
3 755
📐 Лендинг ресторанного комплекса
Макет средней сложности на русском языке без адаптива
@glo_academy | #макеты
3 755
🧰 React Bits
Нужен эффектный первый экран: заголовок вылетает по буквам, кнопка переливается, по клику летят искры. Писать такое с нуля — вечер с GSAP. В React Bits больше 215 готовых анимированных компонентов, их копируют в проект и настраивают пропсами.
На ролике пять компонентов библиотеки без правок в их коде: GradientText переливает надпись сверху, SplitText выкладывает заголовок по буквам, DecryptedText расшифровывает подзаголовок, ShinyText пускает блик по кнопке, ClickSpark рассыпает искры на клик.
У каждого компонента на сайте живое демо с ползунками: крутишь скорость и цвета и забираешь код. Версий четыре: JS или TypeScript, обычный CSS или Tailwind. Ставится одной командой через shadcn, например npx shadcn@latest add @react-bits/SplitText-TS-TW, или копируется из вкладки Code.
Бесплатно, лицензия MIT с оговоркой Commons Clause: в свои и клиентские проекты можно, продавать сами компоненты нельзя.
https://github.com/DavidHDev/react-bits
🔥 — если было полезно
@glo_academy | #инструменты
3 755
⚡ Правильный подсчёт символов в полях с эмодзи
Ставишь лимит в 30 символов и считаешь через text.length. Человек добавляет в отзыв 👨🍳, видит одну картинку, а счётчик отнимает пять, и кнопка «Отправить» гаснет раньше времени. Считать надо то, что видит глаз:
const seg = new Intl.Segmenter('ru', { granularity: 'grapheme' });
const count = (text) => [...seg.segment(text)].length;
count('👨🍳'); // 1, а '👨🍳'.length равен 5
length считает не буквы, а ячейки строки в памяти. Буква занимает одну ячейку, 👍 две, флаг 🇷🇺 четыре, а повар склеен из двух эмодзи и занимает пять. Intl.Segmenter с granularity: 'grapheme' делит строку так, как её видит человек: одна картинка — один символ.
На гифке в отзыв добавляются эмодзи. Счётчик на length прыгает и упирается в лимит, счётчик на Segmenter прибавляет по одному.
Так же текст обрезают «до 100 символов», не разрезая эмодзи пополам. Работает в Chrome 87+, Firefox 125+ и Safari 14.1+.
🔥 — если было полезно
@glo_academy | #js_day3 755
✂️ Код из СМС вводится одним тапом
Пришла СМС с кодом, человек уходит из браузера в сообщения, запоминает цифры и возвращается в форму. По дороге путает 4071 с 4017. Одна строка в поле убирает этот поход:
<input
type="text"
inputmode="numeric"
autocomplete="one-time-code"
>
autocomplete="one-time-code" говорит браузеру, что в поле одноразовый код. Safari на айфоне находит свежую СМС и показывает код над клавиатурой: тап, и цифры уже в поле. inputmode="numeric" открывает клавиатуру с цифрами, а type="text" не съест ведущий ноль, как это делает type="number".
На гифке на оба телефона приходит одна СМС. Слева поле без атрибута: клавиатура есть, кода нет, вспоминай. Справа код висит над клавиатурой и вставляется одним тапом.
Браузеры, которые атрибут не знают, просто покажут обычное поле, так что ставить его можно уже сейчас.
❤️ — если было полезно
@glo_academy | #snippets3 755
🎨 CSS-свойство, которое убирает прыжки текста при загрузке шрифта
Подключил шрифт с font-display: swap, и пока он грузится, текст рисуется запасным Arial. Потом приходит настоящий, буквы у него шире, абзац переносится на лишнюю строку, и всё ниже прыгает. Чтобы подмена прошла незаметно, растяни запасной шрифт под размер основного:
@font-face {
font-family: "Brand Fallback";
src: local("Arial");
size-adjust: 113%;
}
body {
font-family: "Brand", "Brand Fallback", sans-serif;
}
Второй @font-face не качает файл: local("Arial") берёт шрифт, который уже есть в системе, а size-adjust растягивает буквы на 13%. Запасной шрифт занимает в строке столько же места, сколько основной, переносы не меняются.
На гифке сверху Arial без правок: шрифт загрузился, абзац стал на строку длиннее, кнопка уехала вниз. Снизу с size-adjust кнопка стоит на месте.
Процент свой для каждого шрифта, в Next.js его считает next/font. Работает в Chrome 92+, Firefox 92+ и Safari 17+.
🔥 — если было полезно
@glo_academy | #css_day3 755
Чеклист: 50 ошибок в портфолио дизайнеров
Основано на разборе 1000+ портфолио.
Формфактор собрали ошибки, которые:
— портят первое впечатление от портфолио
— вызывают отторжение у лида
— убивают шансы на оффер
Актуально для рынка 2026.
Чеклист в закрепе @formfactor
Реклама.
3 755
⚡ Кнопка блокируется сама, пока идёт запрос
Человек жмёт «Оформить заказ» второй раз, пока запрос идёт, и уходят два заказа. Обычно заводят isLoading и выключают его в finally. В React 19 за этим следит useTransition:
const [isPending, startTransition] = useTransition();
const submit = () => startTransition(async () => {
await sendOrder(cart);
});
<button onClick={submit} disabled={isPending}>
{isPending ? 'Отправляем…' : 'Оформить заказ'}
</button>
isPending равен true, пока асинхронная функция внутри startTransition не закончится. Кнопка блокируется на время запроса и пишет «Отправляем…», свой стейт для загрузки не нужен.
На гифке по каждой кнопке кликаем дважды. Слева приходят два заказа, справа второй клик упирается в блокировку.
Ошибки лови внутри функции через try/catch, иначе они уйдут в ближайший Error Boundary.
🔥 — если было полезно
@glo_academy | #js_day3 755
✂️ Кнопка «Скопировать» на сайте за три строки JS
Ссылку-приглашение, номер заказа или реквизиты удобнее копировать кнопкой. Человеку не нужно аккуратно выделять текст мышкой, а на телефоне пальцем. Кнопка «Скопировать» пишется в три строки:
button.addEventListener('click', async () => {
await navigator.clipboard.writeText(link.textContent);
button.textContent = 'Скопировано ✓';
});
navigator.clipboard.writeText кладёт текст в буфер обмена, туда же, куда он попадает после Ctrl+C. Дальше человек вставляет его куда угодно. Надпись на кнопке после копирования меняем, чтобы было видно, что нажатие сработало.
На гифке жмём «Скопировать» у ссылки, потом вставляем её в сообщение через ⌘V.
Работает на сайтах с HTTPS и на localhost и только по клику человека. Если браузер не дал скопировать, writeText упадёт с ошибкой, поэтому оберни вызов в try/catch.
❤️ — если было полезно
@glo_academy | #snippets3 755
🎨 Стили компонента перебиваются одним классом, без !important
Пишешь класс, чтобы перекрасить кнопку из библиотеки, а она остаётся красной. У селектора
.card .button два класса, у твоего один, и вес решает раньше порядка файлов. В ход идёт !important, а через месяц поверх него ставят второй. Обёртка :where() снимает спор:
/* стили компонента */
:where(.card .button) {
background: crimson;
}
/* перебивается одним классом */
.button-buy {
background: teal;
}
Всё, что стоит внутри :where(), не добавляет селектору веса. Такой селектор весит ноль, и любой обычный класс его перебивает, в каком бы файле он ни стоял.
На гифке на обе кнопки вешается один и тот же класс. Сверху селектор библиотеки тяжелее, и кнопка не меняется. Снизу он обёрнут в :where(), и кнопка перекрашивается.
Так пишут сбросы стилей и общие компоненты. Работает во всех браузерах.
🔥 — если было полезно
@glo_academy | #css_day3 755
📐 Интернет-магазин сантехники
Макет средней сложности на русском языке без адаптива
@glo_academy | #макеты
3 755
🧰 Pretty TS Errors
Ошибки TypeScript в VS Code приходят одной строкой. Пока типы простые, это терпимо. Но передал в функцию объект с одним неверным полем, и в подсказке весь тип целиком: скобки, точки с запятой, кавычки, а где именно ошибка, искать глазами.
Pretty TS Errors переписывает это окно. Тип из ошибки разворачивается по строкам и подсвечивается цветами твоей темы, а названия типов и полей стоят отдельными плашками. На ролике одна и та же ошибка: слева как её показывает VS Code, справа с расширением. Сразу видно, что не так с полем zip в address.
Рядом с ошибкой расширение ставит ссылки на её разбор на typescript.tv и на перевод на простой английский. Работает в .ts и .tsx, а ещё в Vue, Svelte и Astro, если там включён TypeScript. Ставится из маркетплейса VS Code поиском по pretty-ts-errors, в Cursor тоже есть.
https://github.com/yoavbls/pretty-ts-errors
🔥 — если было полезно
@glo_academy | #инструменты
3 755
⚡ Группировка массива без reduce
Разложить заказы по статусам, например для канбан-доски, обычно пишут через reduce: заводят пустой объект, проверяют, есть ли уже массив под ключ, и пушат в него. Несколько строк, в которых легко ошибиться. Теперь это один вызов:
const orders = [
{ id: 1, status: 'new' },
{ id: 2, status: 'paid' },
{ id: 3, status: 'new' },
];
const byStatus = Object.groupBy(orders, (o) => o.status);
// { new: [заказы 1 и 3], paid: [заказ 2] }
Object.groupBy проходит по массиву и для каждого элемента вызывает функцию. Что она вернёт, то и станет ключом, а элемент попадёт в массив под этим ключом. Группировать можно по чему угодно: по статусу, по первой букве имени, по цене «до 1000» и «дороже».
На гифке заказы из одного списка разъезжаются по колонкам статусов.
Элементы в группах не копируются, это те же объекты, что в исходном массиве. Работает в Chrome 117+, Firefox 119+, Safari 17.4+.
🔥 — если было полезно
@glo_academy | #js_day3 755
✂️ +7 в поле телефона, который нельзя стереть
+7 перед номером часто пишут прямо в value поля. Пользователь стирает номер, и плюс семёрка уходит вместе с ним. А если он вставит номер из буфера, получится +7+7. Надёжнее держать префикс вне ввода: рамку рисует обёртка, а input внутри без рамки:
<label class="field">
<span>+7</span>
<input type="tel">
</label>
.field { display: flex; border: 1px solid #ccc; }
.field:focus-within { border-color: #4f5bd5; }
.field input { border: 0; outline: 0; flex: 1; }
label делает префикс частью поля: клик по +7 ставит курсор в ввод. Псевдокласс :focus-within срабатывает у обёртки, когда фокус внутри, поэтому рамка подсвечивается целиком, как у обычного поля. На сервер уходит только input, без +7.
На гифке сверху +7 записан в value и стирается вместе с номером. Снизу он в обёртке, и стереть его нельзя.
Так же ставят ₽ после суммы или https:// перед адресом. Работает во всех браузерах.
❤️ — если было полезно
@glo_academy | #snippets3 755
🎨 Поле для отзыва растёт по тексту без JS
Поле для отзыва обычно растягивают скриптом: на каждый ввод сбрасывают высоту и ставят новую по scrollHeight. Без скрипта textarea остаётся в две строки, и длинный текст уезжает под скролл. Теперь хватает одного свойства:
textarea {
field-sizing: content;
min-height: 3lh;
max-height: 10lh;
}
field-sizing: content говорит браузеру считать размер поля по тексту внутри. min-height не даёт пустому полю схлопнуться, max-height останавливает рост, дальше появляется скролл. Единица lh равна высоте строки, 3lh значит три строки.
На гифке в оба поля печатается один и тот же отзыв. Сверху обычная textarea, текст прячется под скролл. Снизу поле растёт вместе с текстом.
Где свойства нет, поле останется обычным, так что ставить его можно уже сейчас. Работает в Chrome 123+, Firefox 152+, Safari 26.2+.
🔥 — если было полезно
@glo_academy | #css_day3 755
📐 Интернет-магазин ретро-электрики
Сложный макет на русском языке без адаптива
@glo_academy | #макеты
3 755
🧰 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 | #инструменты
