IT-инфраструктура для бизнеса и творчества
Разработка
Daniil Lebedev

Динамическое масштабирование элементов в CSS

Перед началом статьи хочу сказать, что еще больше полезной и нужной информации вы найдете в нашем Telegram-канале. Подпишитесь, мне будет очень приятно.

Оригинал данной статьи на английском: Austin Smith, “Dynamic Element Scaling in CSS”

CSS

Сегодня я поделюсь с вами одним трюком, с которым я столкнулся, работая с CSS. С его помощью можно динамически масштабировать любой HTML-элемент, а заодно устранить многие причины, по которым раньше приходилось писать медиа-запросы.

Прежде всего, я хотел бы отдать должное Майку Ритмюллеру за то, что он изначально придумал эту функцию, и Джеффу Грэму из CSS-Tricks за расширение её функциональности. Я ни в коем случае не ставлю себе в заслугу создание этой функции. Я только хочу пропеть ей хвалу.

Итак, давайте приступим к применению стилей.

Формула

В CSS по умолчанию присутствует возможность применять базовые математические операции с помощью функции calc(). Благодаря ей мы можем решить любое простое математическое уравнение и установить полученный результат в качестве свойства CSS, которому требуется численное значение. calc() может применяться везде: от font-size до width и box-shadow… .

В CSS также есть средство измерения, которое вычисляет viewport height и viewport width окна браузера:vh и vw соответственно. 100vh обозначает всю высоту окна браузера, а 100vw — всю ширину. Разница между 100% и 100vh/100vw в том, что 100% устанавливается относительно селектора, внутри которого происходит определение, в то время как значение 100vh/100vw — абсолютное для окна браузера. Это различие важно.

Объяснив этот момент с calc() и 100vh/100vw, пропущу несколько шагов и перейду прямо к формуле.

Она позволяет динамически масштабировать любое свойство с числовым значением, основанным на ширине или высоте браузера:

calc([min size]px + ([max size] — [min size]) * ((100vw — [min vw width]px) / ([max vw width] — [min vw width])))

Хорошо… Давайте разбираться.

Во-первых, взглянем на правую часть уравнения:

[min size]px

Нам нужно установить минимальный размер для элемента element, так, чтобы любой element, который мы хотим масштабировать, не был равен 0px. Если мы хотим, чтобы элемент был размером не менее 25px, то можем подставить это значение в первую часть calc():

[min size]px = 25px

Нам важна левая часть:

([max size] — [min size]) * ((100vw — [min vw width]px) / ([max vw width] — [min vw width])))

Разберёмся с ней:

([max size] — [min size])

Здесь мы устанавливаем диапазон через минимальный и максимальный размер, который хочется видеть у элемента, и эта разность будет действовать как множитель. Если нужно, чтобы размер элемента находился в пределах между 25px и 50px, мы можем подставить сюда такие значения:

([max size] — [min size]) = (50 - 25)

Третья часть сложнее всего:

((100vw — [min vw width]px) / ([max vw width] — [min vw width]))

Здесь мы можем задать диапазон через минимальное и максимальное ожидаемое разрешение браузера. На десктопе я всегда, исходя из опыта, беру 1920px (горизонтальное разрешение для 1080p) и 500px (самое маленькое разрешение, до которого возможно масштабировать в Chrome без инструментов разработчика).

Подставим эти значения, и крайняя слева часть уравнения примет следующий вид:

((100vw — [min vw width]px) / ([max vw width] — [min vw width])) = ((100vw — 500px) / (1920–500)))

Это создаёт соотношение, основанное на величине значения свойства viewport (окна просмотра) браузера. Всё, что выходит за пределы диапазона между 500px и 1920px, будет масштабироваться вверх или вниз, но с линейной скоростью. Мы также можем написать медиа-запрос для мобильных устройств или сверхшироких мониторов или записать эти исключения в саму функцию calc().

Давайте начнём упрощать: подставим в функцию некоторые числа и посмотрим на неё в действии. Мы можем заменить 100vw любым разрешением, чтобы увидеть соотношение, которое устанавливаем для размера нашего element:

((1920px — 500px) / (1920–500)) = 1 ((1565px — 500px) / (1920–500)) = 0.75 ((1210px — 500px) / (1920–500)) = 0.5 ((855px — 500px) / (1920–500)) = 0.25 ((500px — 500px) / (1920–500)) = 0

Если затем взять множитель размера элемента, заданный ранее, и умножить на это соотношение, то в итоге получится динамическое значение размера нашего элемента, основанное на размере viewport:

(50–25) * ((1920px — 500px) / (1920–500)) = 25px (50–25) * ((1565px — 500px) / (1920–500)) = 18.75px (50–25) * ((1210px — 500px) / (1920–500)) = 12.5px (50–25) * ((855px — 500px) / (1920–500)) = 6.25px (50–25) * ((500px — 500px) / (1920–500)) = 0px

Наконец, если мы затем добавим минимальный размер элемента к этому множителю, то получим окончательный размер элемента:

25 + (50–25) * ((1920px — 500px) / (1920–500)) = 50px 25 + (50–25) * ((1565px — 500px) / (1920–500)) = 43.75px 25 + (50–25) * ((1210px — 500px) / (1920–500)) = 37.5px 25 + (50–25) * ((855px — 500px) / (1920–500)) = 31.25px 25 + (50–25) * ((500px — 500px) / (1920–500)) = 25px

Итак, если мы хотим, чтобы элемент был равен 25px, когда ширина браузера равна 500px, и 50px, когда ширина браузера равна 1920px, вся функция будет выглядеть следующим образом:

calc(25px + (50–25) * ((100vw — 500px) / (1920–500)))

Запутанно? Ещё как.

Полезно? Очень.

Что получаем в итоге:

calc([min size]px + ([max size] — [min size]) * ((100vw — [min vw width]px) / ([max vw width] — [min vw width])))

Теперь перейдём к примерам.

Предварительная настройка

У меня есть очень простая настройка “скелета” HTML с импортом CSS-файла:

В index.html:

<!DOCTYPE html> <html lang='en'> <head> <title>Dynamic Scaling Example</title> <meta charset='UTF-8'> <meta name='viewport' content='width=device-width, initial-scale=1.0'> <meta http-equiv='X-UA=Compatible' content='ie=edge'> <link rel="stylesheet" href="css/main.css"> </head> <body> <h1>Here Be Headline</h1> <div class="square"> <h2> Here Be Subheader </h2> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> <div class="small_square"> <h3> Here Be Subsubheader </h3> <p> Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, eaque ipsa quae ab illo inventore veritatis. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. </p> </div> </div> </body> </html>

В main.css:

body { align-content: center; align-items: center; background: white; display: flex; flex-direction: column; flex-wrap: nowrap; height: 100vh; justify-content: flex-start; margin: 0px; width: 100vw; } h1 { background: lightgrey; } .square { background: darkgreen; } .square h2 { background: green; } .square p { background: lightgreen; } .small_square { background: blue; } .small_square h3 { background: lightblue; } .small_square p { background: cyan; }

В хроме это выглядит так:

Ширина

Давайте для начала поиграем с шириной width у square и small_square с помощью нашей новой причудливой функции масштабирования.

Допустим, нам нужно, чтобы ширина square была равна максимум 1500px и минимум 250px.

В main.css:

body { align-content: center; align-items: center; background: white; display: flex; flex-direction: column; flex-wrap: nowrap; height: 100vh; justify-content: flex-start; margin: 0px; width: 100vw; } h1 { background: lightgrey; } .square { background: darkgreen; width: calc(250px + (1500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square h2 { background: green; } .square p { background: lightgreen; } .small_square { background: blue; } .small_square h3 { background: lightblue; } .small_square p { background: cyan; }

В хроме:

Также настроим small_square на ширину максимум 1000px и минимум 100px.

В main.css:

body { align-content: center; align-items: center; background: white; display: flex; flex-direction: column; flex-wrap: nowrap; height: 100vh; justify-content: flex-start; margin: 0px; width: 100vw; } h1 { background: lightgrey; } .square { background: darkgreen; width: calc(250px + (1500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square h2 { background: green; } .square p { background: lightgreen; } .small_square { background: blue; margin: 0 auto; width: calc(100px + (1000 - 100) * ((100vw - 500px) / (1920 - 500))); } .small_square h3 { background: lightblue; } .small_square p { background: cyan; }

В хроме:

Шапка

В main.css:

body { align-content: center; align-items: center; background: white; display: flex; flex-direction: column; flex-wrap: nowrap; height: 100vh; justify-content: flex-start; margin: 0px; text-align: center; width: 100vw; } h1 { background: lightgrey; margin: 0 auto; width: calc(250px + (500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square { background: darkgreen; width: calc(250px + (1500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square h2 { background: green; margin: 0 auto; width: calc(200px + (400 - 200) * ((100vw - 500px) / (1920 - 500))); } .square p { background: lightgreen; } .small_square { background: blue; margin: 0 auto; width: calc(100px + (1000 - 100) * ((100vw - 500px) / (1920 - 500))); } .small_square h3 { background: lightblue; margin: 0 auto; width: calc(50px + (100 - 50) * ((100vw - 500px) / (1920 - 500))); } .small_square p { background: cyan; }

В хроме:

Поля/отступы

В main.css:

body { align-content: center; align-items: center; background: white; display: flex; flex-direction: column; flex-wrap: nowrap; height: 100vh; justify-content: flex-start; margin: 0px; text-align: center; width: 100vw; } h1 { background: lightgrey; margin: calc(5px + (30 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(250px + (500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square { background: darkgreen; width: calc(250px + (1500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square h2 { background: green; margin: calc(5px + (20 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(200px + (400 - 200) * ((100vw - 500px) / (1920 - 500))); } .square p { background: lightgreen; margin: calc(5px + (20 - 5) * ((100vw - 500px) / (1920 - 500))); padding: calc(4px + (16 - 4) * ((100vw - 500px) / (1920 - 500))); } .small_square { background: blue; margin: 0 auto; width: calc(100px + (1000 - 100) * ((100vw - 500px) / (1920 - 500))); } .small_square h3 { background: lightblue; margin: calc(5px + (10 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(50px + (100 - 50) * ((100vw - 500px) / (1920 - 500))); } .small_square p { background: cyan; margin: calc(3px + (18 - 3) * ((100vw - 500px) / (1920 - 500))); padding: calc(1px + (8 - 1) * ((100vw - 500px) / (1920 - 500))); }

Размер шрифта

В main.css:

body { align-content: center; align-items: center; background: white; display: flex; flex-direction: column; flex-wrap: nowrap; height: 100vh; justify-content: flex-start; margin: 0px; text-align: center; width: 100vw; } h1 { background: lightgrey; font-size: calc(15px + (30 - 15) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (30 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(250px + (500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square { background: darkgreen; width: calc(250px + (1500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square h2 { background: green; font-size: calc(10px + (20 - 10) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (20 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(200px + (400 - 200) * ((100vw - 500px) / (1920 - 500))); } .square p { background: lightgreen; font-size: calc(12px + (24 - 12) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (20 - 5) * ((100vw - 500px) / (1920 - 500))); padding: calc(4px + (16 - 4) * ((100vw - 500px) / (1920 - 500))); } .small_square { background: blue; margin: 0 auto; width: calc(100px + (1000 - 100) * ((100vw - 500px) / (1920 - 500))); } .small_square h3 { background: lightblue; font-size: calc(5px + (15 - 5) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (10 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(50px + (100 - 50) * ((100vw - 500px) / (1920 - 500))); } .small_square p { background: cyan; font-size: calc(8px + (16 - 8) * ((100vw - 500px) / (1920 - 500))); margin: calc(3px + (18 - 3) * ((100vw - 500px) / (1920 - 500))); padding: calc(1px + (8 - 1) * ((100vw - 500px) / (1920 - 500))); }

В хроме:

Радиус границы

В main.css:

body { align-content: center; align-items: center; background: white; display: flex; flex-direction: column; flex-wrap: nowrap; height: 100vh; justify-content: flex-start; margin: 0px; text-align: center; width: 100vw; } h1 { background: lightgrey; font-size: calc(15px + (30 - 15) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (30 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(250px + (500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square { background: darkgreen; border-radius: calc(1px + (50 - 1) * ((100vw - 500px) / (1920 - 500))); width: calc(250px + (1500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square h2 { background: green; font-size: calc(10px + (20 - 10) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (20 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(200px + (400 - 200) * ((100vw - 500px) / (1920 - 500))); } .square p { background: lightgreen; border-radius: calc(5px + (50 - 5) * ((100vw - 500px) / (1920 - 500))); font-size: calc(12px + (24 - 12) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (20 - 5) * ((100vw - 500px) / (1920 - 500))); padding: calc(4px + (16 - 4) * ((100vw - 500px) / (1920 - 500))); } .small_square { background: blue; border-radius: calc(10px + (50 - 10) * ((100vw - 500px) / (1920 - 500))) calc(5px + (40 - 5) * ((100vw - 500px) / (1920 - 500))); margin: 0 auto; width: calc(100px + (1000 - 100) * ((100vw - 500px) / (1920 - 500))); } .small_square h3 { background: lightblue; font-size: calc(5px + (15 - 5) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (10 - 5) * ((100vw - 500px) / (1920 - 500))) auto; width: calc(50px + (100 - 50) * ((100vw - 500px) / (1920 - 500))); } .small_square p { background: cyan; font-size: calc(8px + (16 - 8) * ((100vw - 500px) / (1920 - 500))); margin: calc(3px + (18 - 3) * ((100vw - 500px) / (1920 - 500))); padding: calc(1px + (8 - 1) * ((100vw - 500px) / (1920 - 500))); }

В Хроме:

Тень элемента/текста

В main.css:

body { align-content: center; align-items: center; background: white; display: flex; flex-direction: column; flex-wrap: nowrap; height: 100vh; justify-content: flex-start; margin: 0px; text-align: center; width: 100vw; } h1 { background: lightgrey; box-shadow: 0 0 calc(8px + (35 - 8) * ((100vw - 500px) / (1920 - 500))) 0px rgba(0, 0, 0, 1); font-size: calc(15px + (30 - 15) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (30 - 5) * ((100vw - 500px) / (1920 - 500))) auto; text-shadow: 1px 1px calc(3px + (12 - 3) * ((100vw - 500px) / (1920 - 500))) red; width: calc(250px + (500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square { background: darkgreen; border-radius: calc(1px + (50 - 1) * ((100vw - 500px) / (1920 - 500))); box-shadow: 0 0 calc(10px + (40 - 10) * ((100vw - 500px) / (1920 - 500))) 0px rgba(0, 0, 0, 1); width: calc(250px + (1500 - 250) * ((100vw - 500px) / (1920 - 500))); } .square h2 { background: green; box-shadow: 0 0 calc(10px + (45 - 10) * ((100vw - 500px) / (1920 - 500))) 0px rgba(0, 0, 0, 1); font-size: calc(10px + (20 - 10) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (20 - 5) * ((100vw - 500px) / (1920 - 500))) auto; text-shadow: 1px 1px calc(5px + (8 - 5) * ((100vw - 500px) / (1920 - 500))) cyan; width: calc(100px + (400 - 100) * ((100vw - 500px) / (1920 - 500))); } .square p { background: lightgreen; border-radius: calc(5px + (50 - 5) * ((100vw - 500px) / (1920 - 500))); box-shadow: 0 0 calc(10px + (45 - 10) * ((100vw - 500px) / (1920 - 500))) 0px rgba(0, 0, 0, 1); font-size: calc(12px + (24 - 12) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (20 - 5) * ((100vw - 500px) / (1920 - 500))) auto; padding: calc(4px + (16 - 4) * ((100vw - 500px) / (1920 - 500))); width: calc(100px + (1200 - 100) * ((100vw - 500px) / (1920 - 500))); } .small_square { background: blue; border-radius: calc(10px + (50 - 10) * ((100vw - 500px) / (1920 - 500))) calc(5px + (40 - 5) * ((100vw - 500px) / (1920 - 500))); box-shadow: 0 0 calc(8px + (32 - 8) * ((100vw - 500px) / (1920 - 500))) 0px rgba(0, 0, 0, 1); margin: 0 auto; width: calc(100px + (1000 - 100) * ((100vw - 500px) / (1920 - 500))); } .small_square h3 { background: lightblue; font-size: calc(5px + (15 - 5) * ((100vw - 500px) / (1920 - 500))); margin: calc(5px + (10 - 5) * ((100vw - 500px) / (1920 - 500))) auto; text-shadow: 1px 1px calc(5px + (15 - 5) * ((100vw - 500px) / (1920 - 500))) yellow; width: calc(50px + (100 - 50) * ((100vw - 500px) / (1920 - 500))); } .small_square p { background: cyan; font-size: calc(8px + (16 - 8) * ((100vw - 500px) / (1920 - 500))); margin: calc(3px + (18 - 3) * ((100vw - 500px) / (1920 - 500))) auto; padding: calc(1px + (8 - 1) * ((100vw - 500px) / (1920 - 500))); width: calc(100px + (800 - 100) * ((100vw - 500px) / (1920 - 500))); }

В хроме:

Вы можете применить функцию calc() практически где угодно с любыми из селекторов, для которых требуется числовое значение. Она очень гибкая.

Миссия завершена

Функция calc() изменила мою жизнь, и мне показалось, что она достойна статьи. После знакомства с ней мне пришлось полностью переосмыслить подход к адаптивному веб-дизайну, и эффект оказался ещё более мощным, как только я начал погружаться в SCSS (но это уже совсем другая история).

Всё зашло настолько далеко, что я начал применять эту функцию везде и сделал весь свой проект Spacebar Smasher полностью отзывчивым без единого медиа-запроса. С помощью этой calc() мне удалось также добавить специфичную разметку для мобильных устройств. Я даже не знаю, как её теперь называть.

В одном из более поздних проектов я дал этой функции имя calcSize, так что, быть может, такое название подойдёт лучше.

Как бы там ни было, надеюсь, вы извлекли отсюда некоторую полезную информацию. Пусть все ваши функции вернут true, а все ваши запросы вернутся с кодом 200.

{ "author_name": "Daniil Lebedev", "author_type": "self", "tags": ["webdevelopment","css"], "comments": 0, "likes": 2, "favorites": 16, "is_advertisement": false, "subsite_label": "dev", "id": 178033, "is_wide": true, "is_ugc": true, "date": "Tue, 17 Nov 2020 20:43:20 +0300", "is_special": false }
(function () { let cdnUrl = `https://specialsf378ef5-a.akamaihd.net/SelectelBranding/images/` let previousArticleNumber = null let currentArticleNumber = 0 let platform = 'Desktop' let articles = [ // { // name: 'camera', // url: `${cdnUrl}CameraCat`, // text: 'умную камеру для\u00A0наблюдения за\u00A0котиками', // link: '1', // }, { name: 'chill', url: `${cdnUrl}ChillCat`, text: 'трекер, который подскажет, когда пора отдохнуть', link: 'https://vc.ru/promo/288561-eye-tracker', }, // { // name: 'cloud', // url: `${cdnUrl}CloudCat`, // text: 'котика: даёшь ему «пять», а\u00A0он делает бэкап в облако', // link: '3', // } ] let buttonCycle = document.querySelector('.button--cycle') let textField = document.querySelector('.selectel-footer-subtitle') let imageAgent = document.querySelector('.image--agent') let banner = document.querySelector('.selectel-footer') buttonCycle.addEventListener('click', cycleClick) let media = window.matchMedia("(max-width: 570px)") media.addEventListener('change', matchMedia) function matchMedia() { if (media.matches) { platform = 'Mobile' } else { platform = 'Desktop' } update() } matchMedia() function cycleClick(event) { if (event) { event.preventDefault() event.stopPropagation() } window.open('https://vc.ru/tag/selectelDIY', '_blank') //cycle(event) } function cycle(event) { // incrementArticleNumber() textField.innerHTML = generatedText() imageAgent.src = articles[currentArticleNumber].url + platform + '.svg?5' imageAgent.setAttribute("class", "") imageAgent.classList.add('image--agent', articles[currentArticleNumber].name) banner.href = articles[currentArticleNumber].link } function update() { banner.href = articles[currentArticleNumber].link imageAgent.src = articles[currentArticleNumber].url + platform + '.svg?5' textField.innerHTML = generatedText() } function incrementArticleNumber() { previousArticleNumber = currentArticleNumber if (currentArticleNumber >= articles.length - 1) { currentArticleNumber = 0 } else { currentArticleNumber++ } } function generatedText() { let defaultText if (platform === 'Desktop') { defaultText = `Мы тут собрали %text%. Хотите почитать?` } else { defaultText = `Мы тут собрали %text%.` } return defaultText.replace('%text%', articles[currentArticleNumber].text) } function getRandom(min, max) { min = Math.ceil(min) max = Math.floor(max) return Math.floor(Math.random() * (max - min + 1)) + min } (function create() { currentArticleNumber = getRandom(0, articles.length - 1) cycle() let page = document.querySelector('.page--entry') if (page) { function insertAfter() { let parents = page.querySelectorAll('[data-id="7"]') let referenceNode = parents[0] referenceNode.parentNode.insertBefore(banner, referenceNode.nextSibling); loaded() } setTimeout(() => insertAfter(), 0) } }()) function loaded() { banner.classList.add('loaded') } loadImages([ `${cdnUrl}CameraCatDesktop.svg`, `${cdnUrl}ChillCatDesktop.svg`, `${cdnUrl}CloudCatDesktop.svg`, `${cdnUrl}CameraCatMobile.svg`, `${cdnUrl}ChillCatMobile.svg`, `${cdnUrl}CloudCatMobile.svg`, ]) function loadImages(urls) { return Promise.all(urls.map(function (url) { return new Promise(function (resolve) { var img = document.createElement('img'); img.onload = resolve; img.onerror = resolve; img.src = url; }); })); } }())
0
0 комментариев
Популярные
По порядку
Читать все 0 комментариев
Как мы проводили командную ретроспективу в Minecraft

Рассказываем историю, на что стоит обратить внимание при проведении командного мероприятия в игре Minecraft, какие грабли могут быть, как организовать онлайн- и офлайн-участие.

Эксперты Httpool выступят на конференции по глобальному маркетингу Globalize! 2021

Событие соберет самых продвинутых специалистов в сфере трафика и аналитики из Google, Httpool, AppsFlyer, Aitarget, Angle Connect, TikTok, OWOX, SHAREit, Tribuna, SportQuake, LCFC, FBS.

«Любая клавиатура может быть эргономичной — просто купите вторую» Статьи редакции

Программист Disney Джефф Пэрриш несколько лет страдал от боли в спине при наборе текста— смена клавиатуры и упражнения не помогали. Решение оказалось изящнее.

Сергей Галицкий о деньгах: «В гробу карманов нет. Два завтрака съесть не сможешь» Статьи редакции

В сентябре бизнесмен сообщил о своей болезни, которая мешает ему работать.

Во-первых, следующее поколение должно иметь мотивацию. Во-вторых, в гробу карманов нет. Ты заработал в этом обществе. И ты должен отдавать этому обществу. Два завтрака ты съесть не сможешь, как ни пытайся. Второй завтрак уже будет полдником.

Ты не можешь употребить больше двух-трёх тысяч калорий в день. Тебе не нужно столько. Не нужно двадцать машин, тридцать самолетов.

Сергей Галицкий
цитата по Sports.ru
Нужны ли в России сити-фермы

И появятся ли грядки на крышах пятиэтажек.

re-thinkingthefuture.com
Как традиционному малому бизнесу превратиться в стартап: план действий

Сейчас в России предприниматели переходят из традиционного малого бизнеса в стартапы очень редко — меньше чем в 0,02% случаев. Это не больше 1 000 стартапов из около 6 млн предприятий малого бизнеса. Поговорим о том, что мешает предпринимателям и как действовать, если есть желание создать стартап.

Нью-Йорк первым из крупных городов США принял указы об условиях труда курьеров: разрешил отказываться от заказа и другое Статьи редакции

Сервисы должны будут сообщать, куда везти заказ, до его принятия и платить не реже раза в неделю.

Готовы выбрать победителя премии «Экспортер года eBay — 2021»?
Сергей Галицкий сообщил о болезни, которая «мешает работе» — из-за неё он не может посещать матчи команды Статьи редакции

Галицкий не раскрыл детали, но рассказал, что меняет структуру футбольного клуба — чтобы он не зависел от одного человека.

Сергей Галицкий «КомментШоу»
Управление репутацией на Otzovik.Как убить негативный рейтинг/удалить негативные отзывы и выдвинуть хорошие отзывы вверх
Кейс: как мы арендовали гостиницу на юге и ничего не заработали

На днях я поговорила с предпринимателями, которые в 2018 году арендовали гостевой дом в Геленджике, и выяснила, почему нужно чётко фиксировать все принципиальные вопросы в договоре заранее и что важно просчитать на старте, чтобы бизнес был прибыльным.

Набережная Геленджика  Фото является объектом авторского права
null