Summer Sale! All accounts are 50% off this week.

devhoussam123's avatar

SCSS/SASS: How to Change the Breakpoint infix(sm,md,lg…) direction when Utilities has option called variants for pseudo-classes(hover,focus…)?

For some time now I've been working on an academic project using SCSS I reached an advanced level with my project, but I had some problems.

I made a Pseudo-Class Variants creator using SCSS it make me to add pseudo-classes such as (hover, focus) in Utilities.

if want to know more about Pseudo-Class Variants and How it work take a look to tailwind css Docs.

and don't forget to look also in Bootstrap Utilities API and How it work.

Let's move now to my problem and what I want to solve in my code.

first try to test my code in sassmeister to know more about what I want to change.

My Full Code :

$enable-important-utilities: true !default;
$breakpoint-inset-namespace: "\:" !default;

@mixin _assert-ascending($map, $map-name) {
  $prev-key: null;
  $prev-num: null;
  @each $key, $num in $map {
    @if $prev-num == null or unit($num) == "%" or unit($prev-num) == "%" {
    } @else if not comparable($prev-num, $num) {
      @warn "Potentially invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} whose unit makes it incomparable to #{$prev-num}, the value of the previous key '#{$prev-key}' !";
    } @else if $prev-num >= $num {
      @warn "Invalid value for #{$map-name}: This map must be in ascending order, but key '#{$key}' has value #{$num} which isn't greater than #{$prev-num}, the value of the previous key '#{$prev-key}' !";
    }
    $prev-key: $key;
    $prev-num: $num;
  }
}

@mixin _assert-starts-at-zero($map, $map-name: "$grid-breakpoints") {
  @if length($map) > 0 {
    $values: map-values($map);
    $first-value: nth($values, 1);
    @if $first-value != 0 {
      @warn "First breakpoint in #{$map-name} must start at 0, but starts at #{$first-value}.";
    }
  }
}

@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {
  $n: index($breakpoint-names, $name);
  @if not $n {
    @error "breakpoint `#{$name}` not found in `#{$breakpoints}`";
  }
  @return if($n < length($breakpoint-names), nth($breakpoint-names, $n + 1), null);
}

@function breakpoint-min($name, $breakpoints: $grid-breakpoints) {
  $min: map-get($breakpoints, $name);
  @return if($min != 0, $min, null);
}

@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {
  $max: map-get($breakpoints, $name);
  @return if($max and $max > 0, $max - .02, null);
}

@function breakpoint-infix($name, $breakpoints: $grid-breakpoints) {
    @return if(breakpoint-min($name, $breakpoints) == null, "", "#{$name}#{$breakpoint-inset-namespace}");
}

@mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
  $min: breakpoint-min($name, $breakpoints);
  @if $min {
    @media (min-width: $min) {
      @content;
    }
  } @else {
    @content;
  }
}

@mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
  $max: breakpoint-max($name, $breakpoints);
  @if $max {
    @media (max-width: $max) {
      @content;
    }
  } @else {
    @content;
  }
}

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

  @if $min != null and $max != null {
    @media (min-width: $min) and (max-width: $max) {
      @content;
    }
  } @else if $max == null {
    @include media-breakpoint-up($lower, $breakpoints) {
      @content;
    }
  } @else if $min == null {
    @include media-breakpoint-down($upper, $breakpoints) {
      @content;
    }
  }
}

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

  @if $min != null and $max != null {
    @media (min-width: $min) and (max-width: $max) {
      @content;
    }
  } @else if $max == null {
    @include media-breakpoint-up($name, $breakpoints) {
      @content;
    }
  } @else if $min == null {
    @include media-breakpoint-down($next, $breakpoints) {
      @content;
    }
  }
}

$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px,
  xxl: 1400px
) !default;

@include _assert-ascending($grid-breakpoints, "$grid-breakpoints");
@include _assert-starts-at-zero($grid-breakpoints, "$grid-breakpoints");

@function _remove($list, $value) {
  $result: ();

  @for $i from 1 through length($list) {
    @if nth($list, $i) != $value {
      $result: append($result, nth($list, $i));
    }
  }

  @return $result;
}

@function _selector($variant: null, $breakpoint: null) {
  $selectors: ();

  @each $selector in & {
    $selector: nth($selector, 1);
    $selector: str-slice($selector, 2);

    @if $variant {
      $selector: '#{$variant}\:#{$selector}';
    }
    
    @if $breakpoint {
      $selector: '#{$breakpoint}\:#{$selector}';
    }

    $selectors: append($selectors, '.#{$selector}', comma);
  }

  @return $selectors;
}

@function _pseudo-class($key, $variant: null) {
  @if $variant {
    $key: '#{$key}:#{$variant}';
  }

  @return $key;
}

@mixin _options($options, $variant: null, $breakpoint: null) {
  $pseudo-class: $variant;
  $group: null;

  @if type-of($variant) == 'string' and str-index($variant, 'group-') {
    $pseudo-class: str-slice($pseudo-class, 7);
    $group: '.group';
  }

  $selector: _selector($variant, $breakpoint);

  @if $options {
    @each $key, $value in $options {
      @if $group {
        #{_pseudo-class($group, $pseudo-class)} #{$selector} {
            @content($key, $value);
        }
      } @else {
        #{$selector} {
          @content(_pseudo-class($key, $pseudo-class), $value);
        }
      }
    }
  } @else {
    @if $group {
      #{_pseudo-class($group, $pseudo-class)} #{$selector} {
        @content;
      }
    } @else {
      #{_pseudo-class($selector, $pseudo-class)} {
        @content;
      }
    }
  }
}

@mixin _variants($options, $variants, $breakpoint: null) {
  @include _options($options, null, $breakpoint) using ($data...) {
    @content($data...);
  }

  @each $variant in $variants {
    @include _options($options, $variant, $breakpoint) using ($data...) {
      @content($data...);
    }
  }
}

@mixin variants($options...) {
    $variants: nth($options, -1);
    $options: nth($options, 1);

    @if (type-of($variants) == "map") {
        $variants: null;
    }

    @if (type-of($options) != "map") {
        $options: null;
    }

    $responsive: index($variants, "responsive");

    @if $responsive {
        $variants: _remove($variants, "responsive");
    }

    @at-root {
        @include _variants($options, $variants) using ($data...) {
            @content ($data...);
        }

        @if $responsive {
            $grid-breakpoints: map-remove($grid-breakpoints, "xs");
            @each $breakpoint in map-keys($grid-breakpoints) {
                @include media-breakpoint-up($breakpoint) {
                    @include _variants($options, $variants, $breakpoint) using ($data...) {
                        @content($data...);
                    }
                }
            }
        }
    }
}

// .example {
//  @include variants('responsive' 'hover' 'focus') {
//     color: #FFE135;
//  }
// }

$opacity: (
  0: 0
) !default;

$utilities: () !default;
$utilities: map-merge(
  (
    "border-opacity": (
        variants: ('hover'),
        responsive: true,
        property: --border-opacity,
        class: border-opacity,
        values: $opacity
    ),
  ),
  $utilities
);

@mixin generate-utility($utility, $infix) {
    $values: map-get($utility, values);

    @if type-of($values) == "string" or type-of(nth($values, 1)) != "list" {
        $values: zip($values, $values);
    }

    @each $key, $value in $values {
        $variants: map-get($utility, variants);

        @if type-of($variants) == "string" {
            $variants: append((), $variants);
        }

        $properties: map-get($utility, property);

        @if type-of($properties) == "string" {
            $properties: append((), $properties);
        }

        $property-class: if(
            map-has-key($utility, class),
            map-get($utility, class),
            nth($properties, 1)
        );

        $property-class: if($property-class == null, "", $property-class);

        $infix: if(
            $property-class == "" and str-slice($infix, 1, 1) == "-",
            str-slice($infix, 2),
            $infix
        );

        $property-class-modifier: if(
            $key,
            if($property-class == "" and $infix == "", "", "-") + $key,
            ""
        );

        @if $value != null {
            .#{$infix + $property-class + $property-class-modifier} {
                @each $property in $properties {
                    @if $variants {
                        @include variants($variants) {
                            #{$property}: $value if($enable-important-utilities, !important, null);
                        }  
                    } @else {
                        #{$property}: $value if($enable-important-utilities, !important, null);
                    }
                }
            }
        }
    }
}

@each $breakpoint in map-keys($grid-breakpoints) {
  @include media-breakpoint-up($breakpoint) {
    $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
    @each $key, $utility in $utilities {
      @if type-of($utility) == "map" and (map-get($utility, responsive) or $infix == "") {
        @include generate-utility($utility, $infix);
      }
    }
  }
}

so after you test my code in sassmeister you will get this result. until now everything is good.

.border-opacity-0 {
  --border-opacity: 0 !important;
}

.hover\:border-opacity-0:hover {
  --border-opacity: 0 !important;
}

@media (min-width: 576px) {
  .sm\:border-opacity-0 {
    --border-opacity: 0 !important;
  }

  .hover\:sm\:border-opacity-0:hover {
    --border-opacity: 0 !important;
  }
}
@media (min-width: 768px) {
  .md\:border-opacity-0 {
    --border-opacity: 0 !important;
  }

  .hover\:md\:border-opacity-0:hover {
    --border-opacity: 0 !important;
  }
}
@media (min-width: 992px) {
  .lg\:border-opacity-0 {
    --border-opacity: 0 !important;
  }

  .hover\:lg\:border-opacity-0:hover {
    --border-opacity: 0 !important;
  }
}
@media (min-width: 1200px) {
  .xl\:border-opacity-0 {
    --border-opacity: 0 !important;
  }

  .hover\:xl\:border-opacity-0:hover {
    --border-opacity: 0 !important;
  }
}
@media (min-width: 1400px) {
  .xxl\:border-opacity-0 {
    --border-opacity: 0 !important;
  }

  .hover\:xxl\:border-opacity-0:hover {
    --border-opacity: 0 !important;
  }
}

What I want to change is : when Utilities map has an Utility with variants option contain a Pseudo-Class such as (hover, focus ...) move the Breakpoint infix(sm,md,lg...) direction to be the first like this :

  .sm\:hover\:border-opacity-0:hover {
    --border-opacity: 0;
  } 
0 likes
0 replies

Please or to participate in this conversation.