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;
}
Please or to participate in this conversation.