Зачем нужен scss если есть javascript?

Я посмотрел в интернете, что scss дает расширенные возможности и добавляет такие вещи как: переменные, вложенность и миксины. Зачем нужен scss если многие вещи можно реализовать через js? Хотел бы побольше услышать о возможностях и применениях scss, помимо ответа на свой вопрос.

P.s. да, вопрос может показаться глупым, но в интернете я нормального ответа не нашел, а нейронка выдает частями и сильно размыто, поэтому спрашиваю у людей, которые уже работали в с такими вещами во фронтенд разработке.


Ответы (2 шт):

Автор решения: Lordar

По сути это надстройки для упрощения стилизации страниц.

Его преимущества: Скорость - мощный инструмент, позволяет значительно упростить процесс написания 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

Условные выражения

:where, if, :has


Пример работы, например, с 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>

→ Ссылка