Математика в CSS: что и зачем считать для стилей

Никита Дубко, Яндекс Контест

Математика в CSS:
что и зачем считать
для стилей

Никита Дубко, Яндекс Контест

Никита Дубко

Есть 2 категории людей

Diamond Spin by Ryan Mulligan
Watching you

Часть 1
Базовые возможности

🕹️ calc()

calc(1vh + 20px)

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

min(90px, 5vw, 5%)

max(90px, 5vw, 5%)

clamp(from, fn, to)

Резиновая типографика: магия clamp()
h1 {
    font-size: clamp(3rem, 5vw + 1.5rem, 7rem);
}

h2 {
    font-size: clamp(2rem, 4vw + 1.5rem, 5rem);
}

Часть 2
Логические функции

.effects {
    /* Фантазируем */
    @if (disabled) {
        filter:
            contrast(.9)
            opacity(.7);
    }

    [id='toggle']:checked ~ & {
        disabled: true;
    }
}
Logical Operations with CSS Variables

AND
OR
XOR
NOT

NOT

A !A
0 1
1 0
            --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

Часть 3
Тригонометрия

sin ( α ) = a c
cos ( α ) = b c
tg ( α ) = sin ( α ) cos ( α ) = a b
ctg ( α ) = cos ( α ) sin ( α ) = b a

Зачем мне это в вебе?

😬😬📖📖

Физика!

y = A cos ( ω 0 t + φ ) = A sin ( ω 0 t + φ 1 )

Ряды Фурье

f ( x ) = a 0 2 + k = 1 + A k cos ( k 2 π τ x + θ k )

R 2 = x 2 + y 2
1 2 = c o s 2 + s i n 2

Можно рисовать правильные фигуры

Часть 4
Спецификации

Как считали древние?

Ряд Тейлора

sin ( x ) = x x 3 3 ! + x 5 5 ! x 7 7 ! + x 9 9 ! ...
@function sin($angle) {
    $sin: 0;
    $angle: rad($angle);
    // Iterate a bunch of times.
    @for $i from 0 through 20 {
        $sin:
            $sin + pow(-1, $i)
            * pow($angle, (2 * $i + 1))
            / fact(2 * $i + 1);
    }
    @return $sin;
}
Use trigonometric functions to draw curves and display animations in CSS
@function cos($angle) {
    $cos: 0;
    $angle: rad($angle);
    // Iterate a bunch of times.
    @for $i from 0 through 20 {
        $cos:
            $cos + pow(-1, $i)
            * pow($angle, 2 * $i)
            / fact(2 * $i);
    }
    @return $cos;
}
Use trigonometric functions to draw curves and display animations in CSS
[css-values] Trigonometric functions
[css-values] Trigonometric functions
CSS Values and Units Module Level 4
CSS Math Functions
Interop 2023
atan2

Функции шага

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(7, 2)

7 % 2 = 1

mod(7, 2)

7 % 2 = 1

Домашнее задание

Разобраться, чем отличаются
rem(-21, 4) и mod(-21, 4)

Экспоненциальные функции

hypot(3em, 4em, 5em)

Функции знака

Тригонометрические функции

atan(pi / 2)

atan2(4px, 3px)

Часть 5
Практика

:root {
    /* ❌ */
    --px-width: calc(100vw / 1px);

    /* ✅ */
    --px-width: tan(atan2(100vw, 1px));
}
CSS Type Casting to Numeric: tan(atan2()) Scalars

Можно рисовать правильные фигуры

clip-path: polygon()

Improving CSS Shapes with Trigonometric Functions

mask: radial-gradient()

Creating Flower Shapes using CSS Mask & Trigonometric Functions
Creating Flower Shapes using CSS Mask & Trigonometric Functions
Solar System
.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; }
}
Spinner
Building a no-JS radial menu with CSS trigonometry, popover, and anchor positioning
Watching you
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));
}
Watching you
CSS trig functions: Practical applications
* {
    --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);
}
Minimal CSS-only blurry image placeholders

Infinity

.very-top {
    /* ❌ */
    z-index: 9999999;

    /* ✅ */
    z-index: calc(Infinity);
}
.rounded {
    /* ❌ */
    border-radius: 100%;
    border-radius: 9999px;

    /* ✅ */
    border-radius: calc(Infinity * 1px);
}

Когда это всё не нужно?

❌ На странице нет динамики

❌ IE11

❌ SVG или JS весят меньше

«Веб-стандарты»

Всем добра!

Слайды
mefody.dev/talks/math-css/

Подписаться
@dark_mefody
t.me/mefody_dev QR-код со ссылкой на голосование