question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Generate %placeholder utilities to @extend from

See original GitHub issue

Motivation: !important in utiliites

My colleague likes to use SASS @extend with bootstrap utility classes, e.g.

header {
  @extend .py-1;
}

I personally tend to disagree, but I guess it is a valid technique.

One problem when doing this is we also inherit the !important part, which is reasonable on actual utility classes, but not in most of the @extend use cases.

Proposed solution: %placeholder.

Generate placeholder utilities in addition to the regular utilities, using SASS placeholder syntax. https://sass-lang.com/documentation/style-rules/placeholder-selectors They could then be extended like so:

header {
  @extend %py-1;
}

This would have the same effect, but without the !important.

Implementation

In the generate-utility(..) mixin it would look like this:

@mixin generate-utility(..) {
  [..]
      // Generate the real utility.
      .#{$property-class + $infix + $property-class-modifier} {
        @each $property in $properties {
          #{$property}: $value if($enable-important-utilities, !important, null);
        }
      }
      // Generate placeholder utility.
      %#{$property-class + $infix + $property-class-modifier} {
        @each $property in $properties {
          #{$property}: $value;
        }
      }

I applied this as a quick hack, and it actually worked! I was able to @extend the generated placeholder utilities.

Userland workaround?

I was going to do this in custom code, but this would require to copy the entire generate-utility() mixin.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:27 (12 by maintainers)

github_iconTop GitHub Comments

2reactions
mdocommented, May 22, 2021

The set of mixin you"re now prodiving will also bloat final file size if not used sparingly, I don’t see the difference.

The file size is caused by repetition.

From my perspective, we will never use the utilities in our own source Sass.

Utilities are not meant to be extended but applied in the markup. Most of utilities values are available as variables too, like $spacers. If one wants to apply a spacer, the way is to use map-get($spacers, "1") or similar.

Considering this, our discussion feels like we’re talking about a way to consume our utility API internally instead of simple outputting classes.

Building on the first quoted comment here, this hits the same way for me. This isn’t for us. This is for people who want utility, but without the HTML pollution. This is what Tailwind does with @apply—building components out of atomic property-value pairs. This way you’re still using the approach and methodology of Bootstrap, but you’re still building components like us.


I think there’s a very valid use case here of consuming utilities in a way that meets developers and designers where they are. Some are in the markup, some are still wanting to write component CSS without all the frills. Instead of creating your own $component-* variables that reassign existing Bootstrap values, you could use the utilities that are built on our own system wide rules (margin, padding, text, font, etc are all great examples of this).

<div class="d-flex align-items-center justify-content-center p-3 mb-4 bg-light rounded-3">...</div>

Or a hybrid approach of something like this…

.custom-component {
  display: flex;
  align-items: center
  justify-content: center;
  @include utl(p-3);
  @include utl(mb-4);
  @include utl(bg-light);
  @include utl(rounded-3);
}
1reaction
donquixotecommented, May 17, 2021

Actually my first draft above is broken, because it does not support the case where the same short key is used in multiple utilities (e.g. margins and negative margins). I already have a different version in my own project:

$utilities-by-class: ();

@each $key, $utility in $utilities {
  $properties: map-get($utility, property);
  // Use custom class if present
  $property-class: if(map-has-key($utility, class), map-get($utility, class), nth($properties, 1));
  $property-class: if($property-class == null, "", $property-class);
  $group: map-get($utilities-by-class, $property-class);
  @if ($group) {
    $group: map-merge($group, ($key: $utility));
  }
  @else {
    $group: ($key: $utility);
  }
  $utilities-by-class: map-merge($utilities-by-class, ($property-class: $group));
}

@mixin u($class, $value-name, $breakpoint: null) {
  @debug 'u()', $class, $value-name, $breakpoint;
  // Find utilities that match the shortcut key.
  $group: map-get($utilities-by-class, $class);
  @each $key, $utility in $group {
    $values: map-get($utility, values);
    // If the values are a list or string, convert it into a map
    @if type-of($values) == "string" or type-of(nth($values, 1)) != "list" {
      $values: zip($values, $values);
    }
    // $values could be a map or a list. @each covers both.
    @each $k, $value in $values {
      @if $k == $value-name {
        @include _utility($utility, $value, $breakpoint);
      }
    }
  }
}

// Mixin to use instead of extending utility classes.
@mixin uv($class, $value, $breakpoint: null) {
  @debug 'uv()', $class, $value, $breakpoint;
  // Find utilities that match the shortcut key.
  $group: map-get($utilities-by-class, $class);
  // Use a trick because SASS does not have 'break' in loops.
  // See https://github.com/sass/sass/issues/378.
  $firstgroup: true;
  @each $key, $utility in $group {
    @if ($firstgroup) {
      @include _utility($utility, $value, $breakpoint);
    }
    $firstgroup: false;
  }
}

// Mixin to use instead of extending utility classes.
@mixin _utility($utility, $value, $breakpoint: null) {

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

  // Regular (responsive) CSS.
  @include media-breakpoint-up($breakpoint) {
    @each $property in $properties {
      @debug '_utility', $property, $value, $breakpoint;
      #{$property}: $value;
    }
  }

  // RFS rescaling.
  // @todo This might not work at all.
  @if type-of($utility) == "map"
    and map-get($utility, rfs)
    and ($breakpoint == null or map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint)
    and (map-get($utility, responsive)
      or breakpoint-min($breakpoint, $breakpoints) == null)
  {
    @media (min-width: $rfs-mq-value) {
      @each $property in $properties {
        #{$property}: $value;
      }
    }
  }
}

This does not address your points, I m just posting it here because it works better than the other version.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Is there any way to @extend sass placeholder properties into ...
I tried using mixins but I don't want to make a mixin for every utility class, because I'm using maps and I generate...
Read more >
Creating a Placeholder
Create a utility or EJB project and add the Portal Web Application Services facet to the project. For general instructions, see Creating a...
Read more >
Bootstrap Placeholders - examples & tutorial
Responsive Placeholder built with the latest Bootstrap 5. Use, and customize our alternate layout system built on CSS Grid. Change its width, color,...
Read more >
Placeholders · Bootstrap v5.1
Placeholders can be used to enhance the experience of your application. They're built only with HTML and CSS, meaning you don't need any...
Read more >
Class Utilities | Apps Script - Google Developers
base64Encode(data, charset), String, Generates a base-64 encoded string from the given string in a specific character set.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found