SCSS-міксини й функції

SCSS-міксини й функції

Sass-хелпери для повторного використання — з кодом, прикладами й живим переглядом

Найуживаніший адаптивний міксин Bootstrap. Він тримає брейкпоінти в одному місці й робить великий SCSS компонентів значно читабельнішим.

@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
  $min: breakpoint-min($name, $breakpoints);

  @if $min {
    @media (min-width: $min) {
      @content;
    }
  } @else {
    @content;
  }
}

// Usage
.card-grid {
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;

  @include media-breakpoint-up(lg) {
    grid-template-columns: repeat(3, 1fr);
  }
}
@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints)
  $min: breakpoint-min($name, $breakpoints)

  @if $min
    @media (min-width: $min)
      @content
  @else
    @content

// Usage
.card-grid
  display: grid
  gap: 16px
  grid-template-columns: 1fr

  +media-breakpoint-up(lg)
    grid-template-columns: repeat(3, 1fr)
// Equivalent output
.card-grid {
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;
}

@media (min-width: 992px) {
  .card-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

Стане в пригоді, коли макет потребує правки лише для планшетів, не зачіпаючи малих і великих екранів.

@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints) {
  $min: breakpoint-min($lower, $breakpoints);
  $max: breakpoint-max($upper, $breakpoints);

  @media (min-width: $min) and (max-width: $max) {
    @content;
  }
}

// Usage
.toolbar {
  @include media-breakpoint-between(md, lg) {
    gap: 8px;
    flex-wrap: wrap;
  }
}
@mixin media-breakpoint-between($lower, $upper, $breakpoints: $grid-breakpoints)
  $min: breakpoint-min($lower, $breakpoints)
  $max: breakpoint-max($upper, $breakpoints)

  @media (min-width: $min) and (max-width: $max)
    @content

// Usage
.toolbar
  +media-breakpoint-between(md, lg)
    gap: 8px
    flex-wrap: wrap
@media (min-width: 768px) and (max-width: 991.98px) {
  .toolbar {
    gap: 8px;
    flex-wrap: wrap;
  }
}

Разом із make-row() і make-col-ready() це найшвидший спосіб створити власні примітиви сітки на основі системи Bootstrap.

@mixin make-col($size: false, $columns: $grid-columns) {
  @if $size {
    flex: 0 0 auto;
    width: percentage(divide($size, $columns));
  } @else {
    flex: 1 1 0;
    max-width: 100%;
  }
}

// Usage
.feature-row {
  @include make-row();
}

.feature-card {
  @include make-col-ready();
  @include make-col(6);

  @include media-breakpoint-up(xl) {
    @include make-col(3);
  }
}
@mixin make-col($size: false, $columns: $grid-columns)
  @if $size
    flex: 0 0 auto
    width: percentage(divide($size, $columns))
  @else
    flex: 1 1 0
    max-width: 100%

// Usage
.feature-row
  +make-row()

.feature-card
  +make-col-ready()
  +make-col(6)

  +media-breakpoint-up(xl)
    +make-col(3)
.feature-row {
  display: flex;
  flex-wrap: wrap;
  margin-inline: -12px;
}

.feature-card {
  width: 50%;
  padding-inline: 12px;
}

@media (min-width: 1200px) {
  .feature-card {
    width: 25%;
  }
}
6/12
6/12
3/12
3/12

Один із небагатьох хелперів доступності, які варто мати в кожній бібліотеці компонентів.

@mixin visually-hidden() {
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

// Usage
.icon-button__label {
  @include visually-hidden();
}
@mixin visually-hidden()
  width: 1px !important
  height: 1px !important
  padding: 0 !important
  margin: -1px !important
  overflow: hidden !important
  clip: rect(0, 0, 0, 0) !important
  white-space: nowrap !important
  border: 0 !important

// Usage
.icon-button__label
  +visually-hidden()
.icon-button__label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

Підпис для скринрідерів лишається доступним, а видимий інтерфейс — лише з іконкою.

Практична утиліта, коли бейдж, точку, маркер чи оверлей треба закріпити точно у візуальному центрі.

@mixin centerer($horizontal: true, $vertical: true) {
  position: absolute;

  @if ($horizontal and $vertical) {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
}

// Usage
.badge-dot {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: $accent;

  @include centerer;
}
@mixin centerer($horizontal: true, $vertical: true)
  position: absolute

  @if ($horizontal and $vertical)
    top: 50%
    left: 50%
    transform: translate(-50%, -50%)

// Usage
.badge-dot
  width: 18px
  height: 18px
  border-radius: 50%
  background: $accent
  +centerer
.badge-dot {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

Добре підходить для декоративних слів у головному блоці, постерної типографіки й підписів із прозорою заливкою.

@mixin text-outline($color: black, $stroke: 1px) {
  color: transparent;
  -webkit-text-stroke-width: $stroke;
  -webkit-text-stroke-color: $color;
}

// Usage
.hero-word {
  font-size: 72px;
  font-weight: 700;

  @include text-outline(#111827, 1.5px);
}
@mixin text-outline($color: black, $stroke: 1px)
  color: transparent
  -webkit-text-stroke-width: $stroke
  -webkit-text-stroke-color: $color

// Usage
.hero-word
  font-size: 72px
  font-weight: 700
  +text-outline(#111827, 1.5px)
.hero-word {
  font-size: 72px;
  font-weight: 700;
  color: transparent;
  -webkit-text-stroke: 1.5px #111827;
}
КОНТУР

Стане в пригоді для смуг завантаження, станів прогресу, акцентів у скелетонах чи будь-якої поверхні, якій потрібен рух без зображень.

@mixin stripes($opacity, $color, $size: 25px, $deg: -135deg) {
  background-size: $size $size;
  background-image: linear-gradient($deg, rgba($color, $opacity) 25%, transparent 25%, transparent 50%, rgba($color, $opacity) 50%, rgba($color, $opacity) 75%, transparent 75%, transparent);
}

// Usage
.loader-track {
  @include stripes(.22, #2563eb, 20px, -45deg);
}
@mixin stripes($opacity, $color, $size: 25px, $deg: -135deg)
  background-size: $size $size
  background-image: linear-gradient($deg, rgba($color, $opacity) 25%, transparent 25%, transparent 50%, rgba($color, $opacity) 50%, rgba($color, $opacity) 75%, transparent 75%, transparent)

// Usage
.loader-track
  +stripes(.22, #2563eb, 20px, -45deg)
.loader-track {
  background-size: 20px 20px;
  background-image: linear-gradient(
    -45deg,
    rgba(37, 99, 235, .22) 25%,
    transparent 25%,
    transparent 50%,
    rgba(37, 99, 235, .22) 50%,
    rgba(37, 99, 235, .22) 75%,
    transparent 75%,
    transparent
  );
}

Один із найсильніших власних хелперів. Завдяки маскуванню він особливо корисний для преміум-карток і виносок.

@mixin border-gradient($color1: #fff, $color2: #fff, $deg: 45deg, $border: 1px) {
  background: linear-gradient($deg, $color1, $color2) border-box;
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  border: $border solid transparent;
}

// Usage
.promo-card {
  border-radius: 16px;
  @include border-gradient(#22c55e, #0ea5e9, 135deg, 1px);
}
@mixin border-gradient($color1: #fff, $color2: #fff, $deg: 45deg, $border: 1px)
  background: linear-gradient($deg, $color1, $color2) border-box
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)
  -webkit-mask-composite: xor
  mask-composite: exclude
  border: $border solid transparent

// Usage
.promo-card
  border-radius: 16px
  +border-gradient(#22c55e, #0ea5e9, 135deg, 1px)
.promo-card {
  border: 1px solid transparent;
  border-radius: 16px;
  background:
    linear-gradient(135deg, #22c55e, #0ea5e9) border-box;
  -webkit-mask:
    linear-gradient(#fff 0 0) padding-box,
    linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
}
Рамка преміум-картки

Один із найуніверсальніших хелперів. Йому місце серед перших у будь-якій внутрішній Sass-бібліотеці.

Згенеруйте власну версію з усіма потрібними параметрами: Генератор clamp()

@function to-clamp($min-size, $max-size, $min-screen: 375px, $max-screen: 1920px) {
  $min-size-rem: math.div($min-size, 16px) * 1rem;
  $max-size-rem: math.div($max-size, 16px) * 1rem;
  // preferred(vw) = (min-size - slope * min-screen) + slope * 100vw
  $slope: math.div($max-size - $min-size, $max-screen - $min-screen);
  $intercept-rem: math.div($min-size - $slope * $min-screen, 16px) * 1rem;
  $slope-vw: $slope * 100 * 1vw;
  @return clamp(#{$min-size-rem}, calc(#{$intercept-rem} + #{$slope-vw}), #{$max-size-rem});
}

// Usage
.headline {
  font-size: to-clamp(28px, 72px);
  margin-bottom: to-rem(24px);
}
@function to-clamp($min-size, $max-size, $min-screen: 375px, $max-screen: 1920px)
  $min-size-rem: math.div($min-size, 16px) * 1rem
  $max-size-rem: math.div($max-size, 16px) * 1rem
  // preferred(vw) = (min-size - slope * min-screen) + slope * 100vw
  $slope: math.div($max-size - $min-size, $max-screen - $min-screen)
  $intercept-rem: math.div($min-size - $slope * $min-screen, 16px) * 1rem
  $slope-vw: $slope * 100 * 1vw
  @return clamp(#{$min-size-rem}, calc(#{$intercept-rem} + #{$slope-vw}), #{$max-size-rem})

// Usage
.headline
  font-size: to-clamp(28px, 72px)
  margin-bottom: to-rem(24px)
.headline {
  font-size: clamp(1.75rem, calc(1.0825rem + 2.85vw), 4.5rem);
  margin-bottom: 1.5rem;
}
Плинний заголовок

Міксин повзунка для повторного використання: збирає в одному місці стилі доріжки, бігунка, hover, focus і disabled, а кожен розділ продукту може підлаштувати кольори й розміри.

@mixin c-range-styles(
  $track-height: 4px,
  $track-color: $dark-200,
  $thumb-size: 14px,
  $thumb-color: $primary,
  $thumb-border-width: 0,
  $thumb-border-color: transparent,
  $thumb-shadow: none,
  $hover-scale: 1.15,
  $focus-shadow: 0 2px 6px rgba(0, 0, 0, 0.18)
) {
  appearance: none;
  background: transparent;
  cursor: pointer;

  &::-webkit-slider-runnable-track {
    height: $track-height;
    background: $track-color;
    border-radius: 999px;
  }

  &::-webkit-slider-thumb {
    appearance: none;
    width: $thumb-size;
    height: $thumb-size;
    margin-top: math.div($track-height - $thumb-size, 2);
    border-radius: 50%;
    background: $thumb-color;
    border: $thumb-border-width solid $thumb-border-color;
    box-shadow: $thumb-shadow;
  }
}

// Usage
.c-range {
  @include c-range-styles(
    $track-height: 6px,
    $track-color: #dbe3ef,
    $thumb-size: 18px,
    $thumb-color: #111827
  );
}
@mixin c-range-styles(
  $track-height: 4px,
  $track-color: $dark-200,
  $thumb-size: 14px,
  $thumb-color: $primary,
  $thumb-border-width: 0,
  $thumb-border-color: transparent,
  $thumb-shadow: none,
  $hover-scale: 1.15,
  $focus-shadow: 0 2px 6px rgba(0, 0, 0, 0.18)
)
  appearance: none
  background: transparent
  cursor: pointer

  &::-webkit-slider-runnable-track
    height: $track-height
    background: $track-color
    border-radius: 999px

  &::-webkit-slider-thumb
    appearance: none
    width: $thumb-size
    height: $thumb-size
    margin-top: math.div($track-height - $thumb-size, 2)
    border-radius: 50%
    background: $thumb-color
    border: $thumb-border-width solid $thumb-border-color
    box-shadow: $thumb-shadow

// Usage
.c-range
  +c-range-styles(
    $track-height: 6px,
    $track-color: #dbe3ef,
    $thumb-size: 18px,
    $thumb-color: #111827
  )
.c-range {
  appearance: none;
  background: transparent;
}

.c-range::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 999px;
  background: #dbe3ef;
}

.c-range::-webkit-slider-thumb {
  width: 18px;
  height: 18px;
  margin-top: -6px;
  border-radius: 50%;
  background: #111827;
}

Один міксин задає положення і по головній, і по поперечній осі — замість розкиданих по компонентах оголошень вирівнювання.

@mixin flex-align($align: center, $justify: center) {
  align-items: $align;
  justify-content: $justify;
}
// $align: flex-start | center | flex-end | stretch | baseline
// $justify: flex-start | center | flex-end | space-between | space-around | space-evenly

// Usage
.toolbar {
  display: flex;
  @include flex-align(center, space-between);
}
@mixin flex-align($align: center, $justify: center)
  align-items: $align
  justify-content: $justify
// $align: flex-start | center | flex-end | stretch | baseline
// $justify: flex-start | center | flex-end | space-between | space-around | space-evenly

// Usage
.toolbar
  display: flex
  +flex-align(center, space-between)
.toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
Фільтр Сортування Експорт Поділитися

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

@mixin svg($color, $width) {
  svg {
    path,
    line,
    polyline {
      stroke: $color;
      stroke-width: $width;
    }

    path {
      fill: transparent;
    }
  }
}

// Usage
.icon-wrap {
  @include svg(#111827, 1.5px);
}
@mixin svg($color, $width)
  svg
    path,
    line,
    polyline
      stroke: $color
      stroke-width: $width

    path
      fill: transparent

// Usage
.icon-wrap
  +svg(#111827, 1.5px)
.icon-wrap svg path,
.icon-wrap svg line,
.icon-wrap svg polyline {
  stroke: #111827;
  stroke-width: 1.5px;
}

Практичний хелпер для проєктів, які досі віддають растрові фони в кількох роздільностях.

@mixin background-image($img, $type: png, $x: 0, $y: 0, $repeat: no-repeat) {
  background-image: url("../images/#{$img}.#{$type}");
  background-image: image-set(
    url("../images/#{$img}.#{$type}") 1x,
    url("../images/#{$img}@2x.#{$type}") 2x,
    url("../images/#{$img}@3x.#{$type}") 3x
  );
  background-repeat: #{$repeat};
  background-position: #{$x}px #{$y}px;
}

// Usage
.hero {
  @include background-image("noise", png, 0, 0, repeat);
}
@mixin background-image($img, $type: png, $x: 0, $y: 0, $repeat: no-repeat)
  background-image: url("../images/#{$img}.#{$type}")
  background-image: image-set(url("../images/#{$img}.#{$type}") 1x, url("../images/#{$img}@2x.#{$type}") 2x, url("../images/#{$img}@3x.#{$type}") 3x)
  background-repeat: #{$repeat}
  background-position: #{$x}px #{$y}px

// Usage
.hero
  +background-image("noise", png, 0, 0, repeat)
.hero {
  background-image: url("../images/noise.png");
  background-repeat: repeat;
  background-position: 0 0;
}
Фон, готовий до Retina

Досі корисна, коли не хочеться тягнути вендорні селектори placeholder у компоненти форм.

@mixin placeholder {
  &::-webkit-input-placeholder { @content; }
  &:-moz-placeholder { @content; }
  &::-moz-placeholder { @content; }
  &:-ms-input-placeholder { @content; }
}

// Usage
.input {
  @include placeholder {
    color: rgba($text, .45);
  }
}
@mixin placeholder
  &::-webkit-input-placeholder
    @content
  &:-moz-placeholder
    @content
  &::-moz-placeholder
    @content
  &:-ms-input-placeholder
    @content

// Usage
.input
  +placeholder
    color: rgba($text, .45)
.input::placeholder {
  color: rgba(17, 24, 39, .45);
}

Стане в пригоді для утилітних наборів анімацій, коли треба передати кілька фрагментів анімації як аргументи.

@mixin anim($animate...) {
  $max: length($animate);
  $animations: "";
  @for $i from 1 through $max {
    $animations: #{$animations + nth($animate, $i)};
  }
  animation: $animations;
}

// Usage
.pulse-chip {
  @include anim(fade-in .25s ease, float 2s linear infinite);
}
@mixin anim($animate...)
  $max: length($animate)
  $animations: ""
  @for $i from 1 through $max
    $animations: #{$animations + nth($animate, $i)}
  animation: $animations

// Usage
.pulse-chip
  +anim(fade-in .25s ease, float 2s linear infinite)
.pulse-chip {
  animation: fade-in .25s ease, float 2s linear infinite;
}
Живий статус

Стане в пригоді, коли звичайна лінійна рамка здається надто пласкою і хочеться світіння, зосередженого в кутах.

@mixin border-radial-gradient($color1: #fff, $color2: #fff, $percent: 25%) {
  background: radial-gradient(circle at 0% 0%, $color1 0%, $color2 $percent) border-box, radial-gradient(circle at 100% 100%, $color1 0%, $color2 $percent) border-box;
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  border: 1px solid transparent;
}

@mixin border-radial-gradient-x2($color1: #fff, $color2: #fff, $percent: 25%) {
  background: radial-gradient(circle at 0% 0%, $color1 0%, $color2 $percent) border-box, radial-gradient(circle at 100% 0%, $color1 0%, $color2 $percent) border-box, radial-gradient(circle at 0% 100%, $color1 0%, $color2 $percent) border-box, radial-gradient(circle at 100% 100%, $color1 0%, $color2 $percent) border-box;
  border: 1px solid transparent;
}

// Usage
.panel {
  @include border-radial-gradient-x2(#7c3aed, #06b6d4, 32%);
}
@mixin border-radial-gradient($color1: #fff, $color2: #fff, $percent: 25%)
  background: radial-gradient(circle at 0% 0%, $color1 0%, $color2 $percent) border-box, radial-gradient(circle at 100% 100%, $color1 0%, $color2 $percent) border-box
  -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0)
  -webkit-mask-composite: xor
  border: 1px solid transparent

@mixin border-radial-gradient-x2($color1: #fff, $color2: #fff, $percent: 25%)
  background: radial-gradient(circle at 0% 0%, $color1 0%, $color2 $percent) border-box, radial-gradient(circle at 100% 0%, $color1 0%, $color2 $percent) border-box, radial-gradient(circle at 0% 100%, $color1 0%, $color2 $percent) border-box, radial-gradient(circle at 100% 100%, $color1 0%, $color2 $percent) border-box
  border: 1px solid transparent

// Usage
.panel
  +border-radial-gradient-x2(#7c3aed, #06b6d4, 32%)
.panel {
  border: 1px solid transparent;
  background: radial-gradient(circle at 0% 0%, #7c3aed 0%, #06b6d4 32%) border-box;
}
Рамка преміум-картки

Проєктний варіант, що вже використовується в різних частинах UI; його варто тримати поруч з іншими хелперами поверхонь.

@mixin scrollbars($size, $foreground-color, $background-color: mix($foreground-color, white, 50%)) {
  &::-webkit-scrollbar { width: $size; height: $size; }
  &::-webkit-scrollbar-thumb { background: $foreground-color; }
  &::-webkit-scrollbar-track { background: $background-color; }
}

// Usage
.panel {
  @include scrollbars(10px, #111827, #e5e7eb);
}
@mixin scrollbars($size, $foreground-color, $background-color: mix($foreground-color, white, 50%))
  &::-webkit-scrollbar
    width: $size
    height: $size
  &::-webkit-scrollbar-thumb
    background: $foreground-color
  &::-webkit-scrollbar-track
    background: $background-color

// Usage
.panel
  +scrollbars(10px, #111827, #e5e7eb)
.panel::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

.panel::-webkit-scrollbar-thumb {
  background: #111827;
}

.panel::-webkit-scrollbar-track {
  background: #e5e7eb;
}
Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання.

Проєктний варіант налаштування font-face. Зручно тримати один include на кожну родину файлів.

@mixin font($fname, $fstyle, $fweight, $furl, $fstrech: normal) {
  @font-face {
    font-family: $fname;
    font-style: $fstyle;
    font-weight: $fweight;
    font-stretch: $fstrech;
    src: url($furl) format("woff2");
    font-display: swap;
  }
}

// Usage
@include font("Manrope", normal, 500, "/fonts/manrope-500.woff2");
@mixin font($fname, $fstyle, $fweight, $furl, $fstrech: normal)
  @font-face
    font-family: $fname
    font-style: $fstyle
    font-weight: $fweight
    font-stretch: $fstrech
    src: url($furl) format("woff2")
    font-display: swap

// Usage
+font("Manrope", normal, 500, "/fonts/manrope-500.woff2")
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 500;
  src: url("/fonts/manrope-500.woff2") format("woff2");
  font-display: swap;
}
Aa
Заголовок для перегляду шрифту

Зручно, коли потрібні стилізовані нумеровані списки без повторення шаблонного коду лічильника в кожному компоненті.

@mixin counter($var, $sep) {
  counter-reset: list + $var;
  > li {
    &:before {
      content: counter(list + $var) $sep;
      counter-increment: list + $var;
    }
  }
}

// Usage
.steps {
  @include counter(step, ". ");
}
@mixin counter($var, $sep)
  counter-reset: list + $var
  > li
    &:before
      content: counter(list + $var) $sep
      counter-increment: list + $var

// Usage
.steps
  +counter(step, ". ")
.steps {
  counter-reset: step;
}

.steps > li::before {
  counter-increment: step;
  content: counter(step) ". ";
}
  1. Зібрати вихідні файли
  2. Уніфікувати назви
  3. Експортувати фінальні ресурси

Декоративний хелпер для об'ємних заголовків і стилізованого брендового тексту.

@mixin text3d($primary, $depth: 5, $shadowsteps: 5, $shadowincrementer: 3px, $shadowopacity: 0.4, $primaryshadowcolour: #000, $lighting: $LIGHTING_CEIL) {
  $predefinedShadows: (
    0 0 5px rgba($primaryshadowcolour, 0.05),
    0 -1px 3px rgba($primaryshadowcolour, 0.2),
    0 3px 5px rgba($primaryshadowcolour, 0.2)
  );
  $value: ();

  @for $i from 1 through $depth {
    $num: $i + px;
    $num1: $i * 1.3 + px;
    $hueadjust: $i * 0.2;

    @if ($lighting == $LIGHTING_FLOOR) {
      $hueadjust: ($i * 2 - $depth - 5) * 1%;
    } @else if ($lighting == $LIGHTING_CEIL) {
      $hueadjust: -($i * 2 + $depth - 10) * 1%;
    } @else if ($lighting == $LIGHTING_FLAT) {
      $hueadjust: -$depth * 1%;
    }

    $colour: adjust-color($primary, $lightness: $hueadjust);
    $theShadow: $num1 $num 1px $colour;
    $value: append($value, $theShadow, comma);
  }

  @for $i from 1 through $shadowsteps {
    @if ($i >= length($predefinedShadows)) {
      $dist: $i * $shadowincrementer;
      $value: append($value, 0 $dist $dist rgba($primaryshadowcolour, $shadowopacity));
    } @else {
      $value: append($value, nth($predefinedShadows, $i));
    }
  }

  text-shadow: $value;
}

// Usage
.logo-mark {
  @include text3d(#f97316, 6, 4, 3px, .18);
}
@mixin text3d($primary, $depth: 5, $shadowsteps: 5, $shadowincrementer: 3px, $shadowopacity: 0.4, $primaryshadowcolour: #000, $lighting: $LIGHTING_CEIL)
  $predefinedShadows: (0 0 5px rgba($primaryshadowcolour, 0.05), 0 -1px 3px rgba($primaryshadowcolour, 0.2), 0 3px 5px rgba($primaryshadowcolour, 0.2))
  $value: ()

  @for $i from 1 through $depth
    $num: $i + px
    $num1: $i * 1.3 + px
    $hueadjust: $i * 0.2

    @if ($lighting == $LIGHTING_FLOOR)
      $hueadjust: ($i * 2 - $depth - 5) * 1%
    @else if ($lighting == $LIGHTING_CEIL)
      $hueadjust: -($i * 2 + $depth - 10) * 1%
    @else if ($lighting == $LIGHTING_FLAT)
      $hueadjust: -$depth * 1%

    $colour: adjust-color($primary, $lightness: $hueadjust)
    $theShadow: $num1 $num 1px $colour
    $value: append($value, $theShadow, comma)

  @for $i from 1 through $shadowsteps
    @if ($i >= length($predefinedShadows))
      $dist: $i * $shadowincrementer
      $value: append($value, 0 $dist $dist rgba($primaryshadowcolour, $shadowopacity))
    @else
      $value: append($value, nth($predefinedShadows, $i))

  text-shadow: $value

// Usage
.logo-mark
  +text3d(#f97316, 6, 4, 3px, .18)
.logo-mark {
  text-shadow: 1px 1px 0 #f59e0b, 2px 2px 0 #ea580c, 0 8px 16px rgba(0,0,0,.12);
}
JINERO

Пара до text3d() для блоків: стане в пригоді для чипів, кнопок і грайливих піднятих UI-блоків.

@mixin box3d($primary, $depth: 5, $shadowsteps: 5, $shadowincrementer: 3px, $shadowopacity: 0.4, $primaryshadowcolour: #000, $lighting: $LIGHTING_CEIL) {
  $predefinedShadows: (
    0 0 5px rgba($primaryshadowcolour, 0.05),
    0 -1px 3px rgba($primaryshadowcolour, 0.2),
    0 3px 5px rgba($primaryshadowcolour, 0.2)
  );
  $value: ();

  @for $i from 1 through $depth {
    $num: $i + px;
    $num1: $i * 1.5 + px;
    $hueadjust: $i * 0.2;

    @if ($lighting == $LIGHTING_FLOOR) {
      $hueadjust: ($i * 2 - $depth - 5) * 1%;
    } @else if ($lighting == $LIGHTING_CEIL) {
      $hueadjust: -($i * 2 + $depth - 10) * 1%;
    } @else if ($lighting == $LIGHTING_FLAT) {
      $hueadjust: -$depth * 1%;
    }

    $colour: adjust-color($primary, $lightness: $hueadjust);
    $theShadow: $num1 $num 1px $colour;
    $value: append($value, $theShadow, comma);
  }

  @for $i from 1 through $shadowsteps {
    @if ($i >= length($predefinedShadows)) {
      $dist: $i * $shadowincrementer;
      $value: append($value, 0 $dist $dist rgba($primaryshadowcolour, $shadowopacity));
    } @else {
      $value: append($value, nth($predefinedShadows, $i));
    }
  }

  box-shadow: $value;
}

// Usage
.chip {
  @include box3d(#8b5cf6, 5, 3, 3px, .16);
}
@mixin box3d($primary, $depth: 5, $shadowsteps: 5, $shadowincrementer: 3px, $shadowopacity: 0.4, $primaryshadowcolour: #000, $lighting: $LIGHTING_CEIL)
  $predefinedShadows: (0 0 5px rgba($primaryshadowcolour, 0.05), 0 -1px 3px rgba($primaryshadowcolour, 0.2), 0 3px 5px rgba($primaryshadowcolour, 0.2))
  $value: ()

  @for $i from 1 through $depth
    $num: $i + px
    $num1: $i * 1.5 + px
    $hueadjust: $i * 0.2

    @if ($lighting == $LIGHTING_FLOOR)
      $hueadjust: ($i * 2 - $depth - 5) * 1%
    @else if ($lighting == $LIGHTING_CEIL)
      $hueadjust: -($i * 2 + $depth - 10) * 1%
    @else if ($lighting == $LIGHTING_FLAT)
      $hueadjust: -$depth * 1%

    $colour: adjust-color($primary, $lightness: $hueadjust)
    $theShadow: $num1 $num 1px $colour
    $value: append($value, $theShadow, comma)

  @for $i from 1 through $shadowsteps
    @if ($i >= length($predefinedShadows))
      $dist: $i * $shadowincrementer
      $value: append($value, 0 $dist $dist rgba($primaryshadowcolour, $shadowopacity))
    @else
      $value: append($value, nth($predefinedShadows, $i))

  box-shadow: $value

// Usage
.chip
  +box3d(#8b5cf6, 5, 3, 3px, .16)
.chip {
  box-shadow: 1px 1px 0 #a78bfa, 2px 2px 0 #8b5cf6, 0 10px 18px rgba(0,0,0,.12);
}
Об'ємний чип

Крихітна, але її варто задокументувати, бо вона стає частиною мови дизайн-системи.

@function to-rem($pixels) {
  @return math.div($pixels, 16px) * 1rem;
}

// Usage
.card {
  padding: to-rem(24px);
}
@function to-rem($pixels)
  @return math.div($pixels, 16px) * 1rem

// Usage
.card
  padding: to-rem(24px)
.card {
  padding: 1.5rem;
}
Плинний заголовок

Стане в пригоді для адаптивних відступів чи розмірів у діапазоні ширини екрана без ручного запису формули calc() для кожної властивості.

@function stripUnit($value) {
  @return $value / ($value * 0 + 1);
}

@mixin fluid(
  $min-value,
  $max-value,
  $min-vw,
  $max-vw,
  $properties...
) {
  @each $property in $properties {
    #{$property}: $min-value;
  }

  @media only screen and (min-width: #{$min-vw}) {
    @each $property in $properties {
      #{$property}: calc(#{$min-value} + #{stripUnit($max-value - $min-value)} * (100vw - #{$min-vw}) / #{stripUnit($max-vw - $min-vw)});
    }
  }

  @media only screen and (min-width: #{$max-vw}) {
    @each $property in $properties {
      #{$property}: $max-value;
    }
  }
}

// Usage
.example {
  @include fluid(20px, 40px, 320px, 1200px, padding-top, margin-bottom);
}
@function stripUnit($value)
  @return $value / ($value * 0 + 1)

@mixin fluid(
  $min-value,
  $max-value,
  $min-vw,
  $max-vw,
  $properties...
)
  @each $property in $properties
    #{$property}: $min-value

  @media only screen and (min-width: #{$min-vw})
    @each $property in $properties
      #{$property}: calc(#{$min-value} + #{stripUnit($max-value - $min-value)} * (100vw - #{$min-vw}) / #{stripUnit($max-vw - $min-vw)})

  @media only screen and (min-width: #{$max-vw})
    @each $property in $properties
      #{$property}: $max-value

// Usage
.example
  +fluid(20px, 40px, 320px, 1200px, padding-top, margin-bottom)
.example {
  padding-top: 20px;
  margin-bottom: 20px;
}

@media only screen and (min-width: 320px) {
  .example {
    padding-top: calc(20px + 20 * (100vw - 320px) / 880);
    margin-bottom: calc(20px + 20 * (100vw - 320px) / 880);
  }
}

@media only screen and (min-width: 1200px) {
  .example {
    padding-top: 40px;
    margin-bottom: 40px;
  }
}
Перегляд плинних відступів

Досі один із найчастіше копійованих мікроміксинів у бібліотеках компонентів і дашбордах.

@mixin ellipsis($width: 100%) {
  display: inline-block;
  max-width: $width;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

// Usage
.card-title {
  @include ellipsis(240px);
}
@mixin ellipsis($width: 100%)
  display: inline-block
  max-width: $width
  overflow: hidden
  text-overflow: ellipsis
  white-space: nowrap

// Usage
.card-title
  +ellipsis(240px)
.card-title {
  display: inline-block;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
Це дуже довгий заголовок картки, який має акуратно обрізатися трикрапкою

Поширений утилітний патерн, бо селектори кастомних скролбарів громіздкі й повторюються в різних застосунках.

@mixin scrollbars($size, $thumb, $track: rgba($thumb, .2)) {
  &::-webkit-scrollbar {
    width: $size;
    height: $size;
  }

  &::-webkit-scrollbar-thumb {
    background: $thumb;
  }

  &::-webkit-scrollbar-track {
    background: $track;
  }
}

// Usage
.panel {
  @include scrollbars(10px, #111827, #e5e7eb);
}
@mixin scrollbars($size, $thumb, $track: rgba($thumb, .2))
  &::-webkit-scrollbar
    width: $size
    height: $size

  &::-webkit-scrollbar-thumb
    background: $thumb

  &::-webkit-scrollbar-track
    background: $track

// Usage
.panel
  +scrollbars(10px, #111827, #e5e7eb)
.panel::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

.panel::-webkit-scrollbar-thumb {
  background: #111827;
}

.panel::-webkit-scrollbar-track {
  background: #e5e7eb;
}
Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання. Спільні стилі скролбара роблять довгі панелі зручними для читання.

Використовуйте цей плейсхолдер для підписів чи допоміжного UI, де виділення тексту перетягуванням небажане.

%noselect {
  user-select: none;
}

// Usage
.drag-label {
  @extend %noselect;
}
%noselect
  user-select: none

// Usage
.drag-label
  @extend %noselect
.drag-label {
  user-select: none;
}
Цей текст не можна виділити

Один спільний плейсхолдер для тексту з градієнтною заливкою замість дублювання оголошень clip і fill.

%text-gradient {
  background-clip: text;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

// Usage
.brand-word {
  @extend %text-gradient;
  background-image: linear-gradient(135deg, #0ea5e9, #8b5cf6);
}
%text-gradient
  background-clip: text
  -webkit-background-clip: text
  -webkit-text-fill-color: transparent

// Usage
.brand-word
  @extend %text-gradient
  background-image: linear-gradient(135deg, #0ea5e9, #8b5cf6)
.brand-word {
  background-image: linear-gradient(135deg, #0ea5e9, #8b5cf6);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
Градієнтний текст

Про бібліотеку SCSS-міксинів

Переглядайте Sass-міксини й функції для повторного використання з кодом, прикладами та живим переглядом. Копіюйте будь-який хелпер у свій проєкт.

Згруповано за призначенням

Міксини й функції розкладено за логічними групами — макет, типографіка, адаптивність, колір, анімація тощо. Потрібний хелпер знаходиться миттєво.

Живий перегляд

Дивіться, що генерує кожен міксин, ще до копіювання. Панелі живого перегляду показують відрендерений результат, тож ви одразу бачите, як це виглядає.

Код і використання

Кожен запис містить оголошення SCSS і приклади використання. Копіюйте код міксина або лише рядок include — як зручніше у вашому робочому процесі.

Копіювання в один клік

Копіюйте оголошення будь-якого міксина чи приклад використання одним кліком. Вставте у свої SCSS-файли й користуйтеся одразу, без ручного переписування.

Часті запитання

Міксин — це блок SCSS-коду для повторного використання: ви оголошуєте його один раз і підключаєте в різні селектори через @include. Міксини приймають параметри, тож ними зручно генерувати повторювані CSS-патерни: медіазапити, flexbox-макети чи анімації.

Міксин виводить CSS-оголошення й підключається через @include. Функція обчислює й повертає значення (наприклад, колір чи число) і викликається просто у виразі. Міксини — для блоків CSS, функції — для обчислених значень.

Так. Кожен міксин самодостатній, і його можна скопіювати в будь-який SCSS-проєкт. Деякі міксини посилаються на змінні — просто замініть їх своїми значеннями або оголосіть у своєму файлі змінних.

Міксини дублюють свій вивід усюди, де їх підключено, тож за надмірного використання файл росте. Для спільних оголошень краще застосувати @extend із placeholder-селекторами. Міксини найкращі для параметричних патернів, що відрізняються від виклику до виклику.