Математика в CSS:
что и зачем считать
для стилей
Никита Дубко, Яндекс Контест
Никита Дубко
- 16 лет в профессии
- амбассадор CSS
- третий раз на CodeFest
- подкастер
- беларус
Часть 1
Базовые возможности
calc(
1 / var(--count) * 1turn
)
@property --scrollbar {
syntax: "<length>";
inherits: true;
initial-value: 0px;
}
html {
container-type: inline-size;
}
body {
--scrollbar: calc(100vw - 100cqw);
}
Get the scrollbar width using only CSS
h1 {
font-size: clamp(3rem, 5vw + 1.5rem, 7rem);
}
h2 {
font-size: clamp(2rem, 4vw + 1.5rem, 5rem);
}
Часть 2
Логические функции
--not: calc(1 - var(--a))
AND
| A |
B |
A && B |
| 0 |
0 |
0 |
| 1 |
0 |
0 |
| 0 |
1 |
0 |
| 1 |
1 |
1 |
--and: calc(var(--a) * var(--b))
OR
| A |
B |
A || B |
| 0 |
0 |
0 |
| 1 |
0 |
1 |
| 0 |
1 |
1 |
| 1 |
1 |
1 |
not (A or B)
=
(not A) and (not B)
--or: calc(1 - (1 - var(--a)) * (1 - var(--b)))
XOR
| A |
B |
A ^ B |
| 0 |
0 |
0 |
| 1 |
0 |
1 |
| 0 |
1 |
1 |
| 1 |
1 |
0 |
--xor: calc((var(--a) - var(--b)) * (var(--a) - var(--b)))
.effects {
--i: var(--disabled, 0);
filter:
contrast(calc(1 - var(--i) * .1))
opacity(calc(1 - var(--i) * .3));
[id='toggle']:checked ~ & {
--disabled: 1;
}
}
Logical Operations with CSS Variables
.effects {
filter: if(
style(--disabled: 1):
contrast(.9) opacity(.7);
else:
none);
}
Chrome 137
😬😬📖📖
Физика!
Можно рисовать правильные фигуры
round(var(--w), 50px)
↓
Math.round()
round(up, var(--w), 50px)
↓
Math.ceil()
round(down, var(--w), 50px)
↓
Math.floor()
round(to-zero, var(--w), 50px)
↓
Math.trunc()
h1 {
font-size: clamp(3rem, 5vw + 1.5rem, 7rem);
}
h2 {
font-size: clamp(2rem, 4vw + 1.5rem, 5rem);
}
h1 {
font-size: round(down, clamp(3rem, 5vw + 1.5rem, 7rem), 1px);
}
h2 {
font-size: round(down, clamp(2rem, 4vw + 1.5rem, 5rem), 1px);
}
Домашнее задание
Разобраться, чем отличаются
rem(-21, 4) и mod(-21, 4)
Экспоненциальные функции
pow(5, 2) = 25
sqrt(9) = 3
hypot(3em, 4em) = 5em
log(8, 2) = 3
exp(1) = e
Функции знака
abs(-20px) = 20px
sign(-20px) = -1
Тригонометрические функции
sin()
cos()
tan()
asin()
acos()
atan()
atan2()
Можно рисовать правильные фигуры
.mercury {
--size: 1vmin;
--radius: calc(0.4 * var(--ae));
--speed: calc(88 / 365);
--x: calc(cos(var(--angle)) * var(--radius) - var(--size) / 2);
--y: calc(sin(var(--angle)) * var(--radius) - var(--size) / 2);
translate: calc(var(--x)) calc(var(--y));
animation: spin linear calc(var(--speed) * 100s) infinite;
}
@keyframes spin {
from { --angle: 0turn; }
to { --angle: 1turn; }
}
const ball = document.querySelector('.ball');
function setPositions(e) {
const viewportOffset = ball.getBoundingClientRect();
ball.style = `
--cx: ${viewportOffset.left + viewportOffset.width / 2};
--cy: ${viewportOffset.top + viewportOffset.height / 2};
--cursor-x: ${e.pageX};
--cursor-y: ${e.pageY}
`;
}
addEventListener('mousemove', setPositions, false);
.iris {
--angle: atan2(
var(--cursor-x) - var(--cx),
var(--cursor-y) - var(--cy)
);
--x: calc((var(--cursor-x) - var(--cx)) / var(--cx));
--y: calc((var(--cursor-y) - var(--cy)) / var(--cy));
--scale: calc(
0.2 * hypot(
var(--cursor-y) - var(--cy),
var(--cursor-x) - var(--cx)
) / hypot(var(--cy), var(--cx))
);
scale: 1 calc(1 - var(--scale));
translate: calc(var(--x) * 50%) calc(var(--y) * 50%);
rotate: calc(-1 * var(--angle));
}
* {
--packed-int: 781; /* 11_00_001_101 */
--bits-9-10: mod(
round(down, calc(var(--packed-int) / 256)), 4
);
--bits-7-8: mod(
round(down, calc(var(--packed-int) / 64)), 4
);
--bits-4-6: mod(
round(down, calc(var(--packed-int) / 8)), 8
);
--bits-0-3: mod(var(--packed-int), 8);
}
.very-top {
/* ❌ */
z-index: 9999999;
/* ✅ */
z-index: calc(Infinity);
}
.rounded {
/* ❌ */
border-radius: 100%;
border-radius: 9999px;
/* ✅ */
border-radius: calc(Infinity * 1px);
}
❌ На странице нет динамики
❌ SVG или JS весят меньше