Зачем нужен scss если есть javascript?
Я посмотрел в интернете, что scss дает расширенные возможности и добавляет такие вещи как: переменные, вложенность и миксины. Зачем нужен scss если многие вещи можно реализовать через js? Хотел бы побольше услышать о возможностях и применениях scss, помимо ответа на свой вопрос.
P.s. да, вопрос может показаться глупым, но в интернете я нормального ответа не нашел, а нейронка выдает частями и сильно размыто, поэтому спрашиваю у людей, которые уже работали в с такими вещами во фронтенд разработке.
Ответы (2 шт):
По сути это надстройки для упрощения стилизации страниц.
Его преимущества: Скорость - мощный инструмент, позволяет значительно упростить процесс написания CSS. Организация кода - лучше читаемость CSS и проще его поддерживать. Производительность - меньше кода, меньше нагрузка на интерпретатор. Повторяемость - позволяет избежать дублирования и упрощает изменения. Удобство - SCSS аналогичен CSS что делает его легким для освоения.
Если вы его не используйте, значит оно вам не надо. Порой написать все руками без излишеств гораздо проще и быстрее. Так как обычный CSS полностью валидный для SCSS, а вот SCSS требует компиляции через Sass (в котором тоже еще разобраться надо), чтобы его начали понимать браузеры. Лично я обожаю JS, но если сотню строк можно заменить всего парой, и по сути обойтись только описанием стилей, то это уже очень технологично. Например, раньше для анимации кнопок приходилось писать минимум три функции с биндингом для каждого элемента, а теперь всего одна строка в стилевых таблицах, одна, и все работает, без скриптов вообще. Это ли не славно?
Я перефразировал бы вопрос на такой: зачем нужен SCSS, если есть CSS?
SCSS (или Sass) — это препроцессор CSS. Он добавляет синтаксический сахар и абстракции: переменные, функции, миксины, вложенность, наследование стилей и др., компилируется в обычный CSS.
Я считаю, что SCSS устарел концептуально.
SCSS появился в 2009 году и на тот момент он был мастхэв решением, так как позволял делать многие вещи, которых у CSS не было и в помине.
На текущий момент всё что давал SCSS - CSS позволяет решать куда более удобно, гибко и динамически, без необходимости компиляции каждый раз
Вложенность
Вложенность, которую давал SCSS и появилась в стандартном CSS не является супер фичей, это просто возможность по другому писать код. На моей практике, код в больших приложениях на SCSS/SASS/less всегда был хуже понимаем, чем стандартный CSS, так как позволял обратиться к одному и тому же элементу 5-10 разными способами - а потом разбирайся в этих кишках вложенности, кто к кому обратился, чтобы удалить лишние куски кода.
Работа с цветами
SCSS позволял при компиляции делать вычисления. CSS же это позволяет делать в рантайме.
color-mix, contrast-color, lab, lch, oklab, oklch, color
статья chrome blog, как работать с относительными цветами в CSS
Переменные
CSS переменные уже давно стали лучше SCSS переменных(в контексте того, для чего они были обычно нужны):
CSS умеет в рантайме менять темы
CSS умеет анимировать значения переменных
CSS умеет наследовать значения переменных в DOM
CSS умеет изменять значения CSS переменных через JS
Если взглянуть, например, на стилизацию гитхаба(да и того же stackoverflow) - у них уже всё давно сделано именно через CSS переменные. Но и не только. Github использует и @layer
Модули
Раньше SCSS модули решали архитектуру через препроцессор. Сегодня архитектура решается нативным CSS: через переменные, наследование, @layer, @scope и динамику.
Статья Chrome на тему работы с @scope
Миксины
SCSS вычислялся один раз при компиляции и для этого нужны были миксины - чтобы каждый раз создавать один и тот же код(дублировать его из функций). Сейчас в этом отпала необходимость, так как появились CSS переменные, которые изменяются в рантайме, динамически
Кроме того, появились пользовательские CSS функции, которые позволяют инкапуслировать логику CSS вычислений.
Пример пользовательской функции для создания градиента кнопке:
html {
color-scheme: light dark;
}
/* Кастомная CSS-функция для генерации градиента */
@function --gradient-from-color(--base-color) {
/* Делаем градиент от цвета к его более светлому оттенку */
result: linear-gradient(
135deg,
var(--base-color),
color-mix(in srgb, var(--base-color) 20%, white)
);
}
/* Пример использования */
.button {
background-image: --gradient-from-color(var(--btn-color));
border: none;
padding: 12px 24px;
font-size: 16px;
border-radius: 8px;
cursor: pointer;
}
<button class="button" type="button" style="--btn-color: red">Кнопка</button>
<button class="button" type="button" style="--btn-color: blue">Кнопка</button>
<button class="button" type="button" style="--btn-color: green">Кнопка</button>
<button class="button" type="button" style="--btn-color: orange">Кнопка</button>
Пример пользовательской CSS функции, которая меняет расстановку грид сетки, если экран больше 500px размера
* {
box-sizing: border-box;
}
@function --layout-sidebar(--sidebar-width: 256px) {
result: var(--sidebar-width) var(--sidebar-width);
@media (max-width: 500px) {
result: var(--sidebar-width);
}
}
.layout {
display: grid;
grid-template-columns: --layout-sidebar(256px);
gap: 1rem;
}
.sidebar {
background: lightgray;
padding: 1rem;
border-radius: 8px;
}
.content {
background: lightblue;
padding: 1rem;
border-radius: 8px;
}
<div class="layout">
<aside class="sidebar">
<h2>Меню</h2>
<ul>
<li><a href="#">Ссылка 1</a></li>
<li><a href="#">Ссылка 2</a></li>
<li><a href="#">Ссылка 3</a></li>
</ul>
</aside>
<main class="content">
<h1>Заголовок</h1>
<p>
Это основной контент. Потяни за правую границу блока, чтобы увидеть,
как макет переключается между одной и двумя колонками.
</p>
</main>
</div>
Работа с числами
в CSS уже можно нативно считать:
min
max
clamp - динамическое CSS значение с краями min и max
@property - типизированные CSS переменные для keyfames анимаций
abs - модуль числа
mod - получение остатка от деления числа
sign - знак числа, положительный или отрицательный
sqrt - корень числа
round - округление числа
sin
cos
tan
Условные выражения
Пример работы, например, с CSS переменными для анимации карточки. В обычном подходе SCSS при анимации использовалось бы ручное изменение конкретных стилей. В данном же случае вся логика стилизации инкапсулируется только в CSS, а задача JS - изменить CSS переменные, пример разделения ответственности, который дал нативный CSS
const htmlNode = document.documentElement;
const defaultMouseX = parseFloat(getComputedStyle(htmlNode).getPropertyValue('--mouse-x'));
const defaultMouseY = parseFloat(getComputedStyle(htmlNode).getPropertyValue('--mouse-y'));
let currentX = 0;
let currentY = 0;
let lastCurrentX = 0;
let lastCurrentY = 0;
let targetX = 0;
let targetY = 0;
let targetXPercent = 0;
let targetYPercent = 0;
const ease = 0.05; // значение, определяющее скорость анимации или изменения координат
window.addEventListener('pointermove', onMove);
window.addEventListener('resize', onResize);
function onResize(e) {
targetX = targetXPercent * window.innerWidth;
targetY = targetYPercent * window.innerHeight;
}
function onMove(e) {
targetX = e.clientX;
targetY = e.clientY;
// это сделано, чтобы не выходить за пределы экрана браузера
targetX = Math.min(Math.max(targetX, 0), window.innerWidth);
targetY = Math.min(Math.max(targetY, 0), window.innerHeight);
// это сделано, чтобы не выходить за пределы экрана браузера
targetXPercent = targetX / window.innerWidth;
targetYPercent = targetY / window.innerHeight;
}
function initPosition() {
targetX = defaultMouseX * window.innerWidth;
targetY = defaultMouseY * window.innerHeight;
targetXPercent = targetX / window.innerWidth;
targetYPercent = targetY / window.innerHeight;
currentX = targetX;
currentY = targetY;
}
function animate() {
currentX = currentX + ((targetX - currentX) * ease);
currentY = currentY + ((targetY - currentY) * ease);
// преобразуем координаты в диапазон от 0 до 1
let x = currentX / window.innerWidth;
let y = currentY / window.innerHeight;
// округляем до 3 чисел после запятой
x = parseFloat(x.toFixed(3));
y = parseFloat(y.toFixed(3));
// устанавливаем соответствующие CSS-переменные
if (x !== lastCurrentX) {
document.documentElement.style.setProperty('--mouse-x', x);
lastCurrentX = x;
}
if (y !== lastCurrentY) {
document.documentElement.style.setProperty('--mouse-y', y);
lastCurrentY = y;
}
requestAnimationFrame(animate);
}
initPosition();
animate();
*,
*::before,
*::after {
box-sizing: border-box;
}
:root {
/* от 0 до 1 эти CSS переменные */
--mouse-x: 0.2;
--mouse-y: 0.7;
}
html,
body {
height: 100%;
}
body {
margin: 0;
font-family: 'Arial', sans-serif;
display: flex;
justify-content: center;
align-items: center;
background-color: rgb(0, 26, 52);
color: white;
overflow: hidden;
touch-action: none;
}
.rectangle {
--borderRadius: 20px;
--background: linear-gradient(to right top, #29a329, #71da71);
/* от -1 до 1 эти CSS переменные */
--x: calc(var(--mouse-x) * 2 - 1);
--y: calc(var(--mouse-y) * 2 - 1);
/* от -1 до 1 эти CSS переменные */
--skewDeg: calc(-18deg * var(--x) * var(--y));
--maxTranslateX: 20px;
--maxTranslateY: 20px;
transform: skew(0, var(--skewDeg));
font-size: 2em;
font-weight: bold;
line-height: 1.3;
max-width: 180px;
position: relative;
text-align: center;
}
.rectangle__body-shadow {
--maxTranslateXShadow: calc(var(--maxTranslateX) * 2.5);
--maxTranslateYShadow: calc(var(--maxTranslateY) * 2.5);
--translateX: calc(var(--maxTranslateXShadow) * var(--x) * -1);
--translateY: calc(var(--maxTranslateYShadow) * var(--y) * -1);
transform: translate3d(var(--translateX), var(--translateY), 0);
background: var(--background);
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0.3;
border-radius: var(--borderRadius);
}
.rectangle__body {
padding: 35px 25px;
background: var(--background);
border-radius: var(--borderRadius);
position: relative;
z-index: 2;
}
.rectangle__body-pos-relative {
position: relative;
}
.rectangle__body-text-shadow {
position: absolute;
top: 0;
left: 0;
z-index: 1;
filter: blur(4px);
color: rgb(0, 0, 0);
user-select: none;
cursor: default;
}
.rectangle__body-text {
--translateX: calc(var(--maxTranslateX) * var(--x));
--translateY: calc(var(--maxTranslateY) * var(--y));
position: relative;
z-index: 2;
transform: translate3d(var(--translateX), var(--translateY), 0);
}
<div class="rectangle">
<div class="rectangle__body-shadow"></div>
<div class="rectangle__body">
<div class="rectangle__body-pos-relative">
<div class="rectangle__body-text-shadow"> Воу, 3D карточка!<br> </div>
<div class="rectangle__body-text"> Воу, 3D карточка!<br> </div>
</div>
</div>
</div>
Пример keyframes анимации через @property
*, *::before, *::after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
color-scheme: dark;
font-family: sans-serif;
}
html,
body {
margin: 0;
height: 100%;
background: #111;
display: flex;
justify-content: center;
align-items: center;
}
.container {
position: relative;
width: 100vw;
height: 100vh;
border: 2px solid #333;
background: #1e1e1e;
}
/* Ось X */
.x-axis {
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 1px;
background: #666;
}
/* Ось Y */
.y-axis {
position: absolute;
left: 50%;
top: 0;
width: 1px;
height: 100%;
background: #666;
}
@property --progress {
syntax: '<number>';
inherits: false;
initial-value: 0;
}
.dot {
--progress: 0;
--size: 2.5rem;
width: var(--size);
aspect-ratio: 1 / 1;
/* Центрирование */
transform: translate(-50%, -50%);
position: absolute;
/* Движение ограничено размерами шаров (от 0.05 до 0.95 примерно) */
--x: calc(5% + var(--progress) * 90%);
left: var(--x);
border-radius: 50%;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
}
/* Синус */
.dot-sin {
--y: calc(50% - sin(var(--progress) * 360deg) * 40vh);
top: var(--y);
background: deepskyblue;
}
/* Косинус */
.dot-cos {
--y: calc(50% - cos(var(--progress) * 360deg) * 40vh);
top: var(--y);
background: limegreen;
}
.animate {
animation: move 4s linear infinite alternate;
}
@keyframes move {
0% {
--progress: 0;
}
100% {
--progress: 1;
}
}
<div class="container">
<div class="x-axis"></div>
<div class="y-axis"></div>
<div class="dot dot-sin animate">sin</div>
<div class="dot dot-cos animate">cos</div>
</div>