Compare commits

...

8 Commits

Author SHA1 Message Date
Julien Déramond 5e69406ee0 Merge branch 'main' into pr/34102 2023-03-27 18:49:50 +02:00
Mark Otto 3583de8bd2 interpolate instead 2023-03-24 23:07:12 -05:00
Mark Otto 850bd19d0e Document how to disable generated classes and still use the util() mixin 2023-03-24 23:03:07 -05:00
Mark Otto b6e4cf45de Add ability to use multiple classes in a single mixin call 2023-03-24 22:43:46 -05:00
Mark Otto 87edeeff13 Fix up docs 2023-03-24 22:20:38 -05:00
Mark Otto 4a8a38bb30 Document the util() mixin 2023-03-24 22:10:47 -05:00
Mark Otto b4cbc54143 Update utility mixin and wrap utility classes in $enable- variable
- Rearrange Sass files to simplify things
- Rename `utl()` to `util()`
- Add new `$enable-utility-classes` variable for disabling the default generation of our utilities (useful if you want to only use utilities via mixin)
2023-03-24 22:10:12 -05:00
Andreas Hennings 0354fd27e4 Issue #33861: New utl() mixin
Co-Authored-By: Andreas Hennings <andreas@dqxtech.net>
2023-03-24 22:10:12 -05:00
8 changed files with 357 additions and 31 deletions
+3
View File
@@ -73,6 +73,9 @@
@return $result;
}
// Some functions are defined in separate files.
@import "functions/utilities-map";
// Internal Bootstrap function to turn maps into its negative variant.
// It prefixes the keys with `n` and makes the value negative.
@function negativify-map($map) {
+1
View File
@@ -382,6 +382,7 @@ $enable-rfs: true !default;
$enable-validation-icons: true !default;
$enable-negative-margins: false !default;
$enable-deprecation-messages: true !default;
$enable-utility-classes: true !default;
$enable-important-utilities: true !default;
$enable-dark-mode: true !default;
+89
View File
@@ -0,0 +1,89 @@
// Builds a map of utility classes.
@function build-utilities-map($grid-breakpoints: $grid-breakpoints, $utilities: $utilities) {
// Build a breakpoint map that does not include the zero breakpoint.
$breakpoints-map: ();
@each $name, $min in $grid-breakpoints {
@if $min != 0 {
$breakpoints-map: map-merge(
$breakpoints-map,
(
"-#{$name}": $name,
)
);
}
}
$utilities-map: () !default;
@each $key, $utility in $utilities {
@if type-of($utility) == "map" {
$properties: map-get($utility, property);
// Some utilities set the value on more than one property.
@if type-of($properties) == "string" {
$properties: append((), $properties);
}
// Use custom class if present
$shortname: if(
map-has-key($utility, class),
map-get($utility, class),
nth($properties, 1)
);
$shortname: if($shortname == null, "", $shortname);
// Shortname with prepended dash, or empty string if empty.
$dashname: if($shortname == "", "", "-" + $shortname);
$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 {
// Value key with prepended dash, or empty string if value key is null.
$dashkey: if($k, "-" + $k, "");
$property-value-map: ();
@each $property in $properties {
$property-value-map: map-merge(
$property-value-map,
(
$property: $value,
)
);
}
$dashclass: $dashname + $dashkey;
$class: str-slice($dashclass, 2);
$utilities-map: map-merge(
$utilities-map,
(
// Create a normalized version of the utility definition.
$class: (
breakpoint: null,
properties: $properties,
value: $value,
),
)
);
@if map-get($utility, responsive) {
@each $dashpoint, $breakpoint in $breakpoints-map {
$class: str-slice($dashname + $dashpoint + $dashkey, 2);
$utilities-map: map-merge(
$utilities-map,
(
$class: (
breakpoint: $breakpoint,
properties: $properties,
value: $value,
)
)
);
}
}
}
}
}
@return $utilities-map;
}
+35
View File
@@ -0,0 +1,35 @@
@import "../../functions";
@import "../../variables";
@import "../../variables-dark";
@import "../../maps";
@import "../../mixins";
@import "../../utilities";
@import "../../utilities/api";
@include describe("utility mixin") {
@include it("generate property-value pairs for given utility") {
@include assert() {
@include output() {
.test {
@include util(d-block);
@include util(p-3);
}
.test2 {
@include util(d-block, p-3);
}
}
@include expect() {
.test {
display: block;
padding: 1rem;
}
.test2 {
display: block;
padding: 1rem;
}
}
}
}
}
+60 -31
View File
@@ -1,47 +1,76 @@
// Loop over each breakpoint
@each $breakpoint in map-keys($grid-breakpoints) {
// Generate media query if needed
@include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
// Loop over each utility property
@each $key, $utility in $utilities {
// The utility can be disabled with `false`, thus check if the utility is a map first
// Only proceed if responsive media queries are enabled or if it's the base media query
@if type-of($utility) == "map" and (map-get($utility, responsive) or $infix == "") {
@include generate-utility($utility, $infix);
}
}
}
}
// RFS rescaling
@media (min-width: $rfs-mq-value) {
@if $enable-utility-classes {
// Loop over each breakpoint
@each $breakpoint in map-keys($grid-breakpoints) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
// Generate media query if needed
@include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
@if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) {
// Loop over each utility property
@each $key, $utility in $utilities {
// The utility can be disabled with `false`, thus check if the utility is a map first
// Only proceed if responsive media queries are enabled or if it's the base media query
@if type-of($utility) == "map" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == "") {
@include generate-utility($utility, $infix, true);
@if type-of($utility) == "map" and (map-get($utility, responsive) or $infix == "") {
@include generate-utility($utility, $infix);
}
}
}
}
// RFS rescaling
@media (min-width: $rfs-mq-value) {
@each $breakpoint in map-keys($grid-breakpoints) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
@if (map-get($grid-breakpoints, $breakpoint) < $rfs-breakpoint) {
// Loop over each utility property
@each $key, $utility in $utilities {
// The utility can be disabled with `false`, thus check if the utility is a map first
// Only proceed if responsive media queries are enabled or if it's the base media query
@if type-of($utility) == "map" and map-get($utility, rfs) and (map-get($utility, responsive) or $infix == "") {
@include generate-utility($utility, $infix, true);
}
}
}
}
}
// Print utilities
@media print {
@each $key, $utility in $utilities {
// The utility can be disabled with `false`, thus check if the utility is a map first
// Then check if the utility needs print styles
@if type-of($utility) == "map" and map-get($utility, print) == true {
@include generate-utility($utility, "-print");
}
}
}
}
// Generate utility placeholders
// Print utilities
@media print {
@each $key, $utility in $utilities {
// The utility can be disabled with `false`, thus check if the utility is a map first
// Then check if the utility needs print styles
@if type-of($utility) == "map" and map-get($utility, print) == true {
@include generate-utility($utility, "-print");
$utilities-map: build-utilities-map(); // stylelint-disable-line scss/dollar-variable-default
@mixin util($classes...) {
@each $class in $classes {
@if map-has-key($utilities-map, $class) {
$definition: map-get($utilities-map, $class);
$breakpoint: map-get($definition, breakpoint);
@if $breakpoint != null {
@include media-breakpoint-up($breakpoint) {
@each $property in map-get($definition, properties) {
#{$property}: map-get($definition, value);
}
}
}
@else {
@each $property in map-get($definition, properties) {
#{$property}: map-get($definition, value);
}
}
}
@else {
@debug "Unknown utility class " + $class;
}
}
}
@@ -27,6 +27,7 @@ You can find and customize these variables for key global options in Bootstrap's
| `$enable-validation-icons` | `true` (default) or `false` | Enables `background-image` icons within textual inputs and some custom forms for validation states. |
| `$enable-negative-margins` | `true` or `false` (default) | Enables the generation of [negative margin utilities]({{< docsref "/utilities/spacing#negative-margin" >}}). |
| `$enable-deprecation-messages` | `true` (default) or `false` | Set to `false` to hide warnings when using any of the deprecated mixins and functions that are planned to be removed in `v6`. |
| `$enable-utility-classes` | `true` (default) or `false` | Enables the generation of utility classes. |
| `$enable-important-utilities` | `true` (default) or `false` | Enables the `!important` suffix in utility classes. |
| `$enable-smooth-scroll` | `true` (default) or `false` | Applies `scroll-behavior: smooth` globally, except for users asking for reduced motion through [`prefers-reduced-motion` media query]({{< docsref "/getting-started/accessibility#reduced-motion" >}}) |
{{< /bs-table >}}
+167
View File
@@ -0,0 +1,167 @@
---
layout: docs
title: Utility mixin
description: Don't want to use utility classes directly in your HTML? Use the `util()` mixin to compose custom component styles in your source Sass files.
group: utilities
aliases: "/docs/5.1/utilities/mixin/"
toc: true
---
## How it works
Bootstrap generates hundreds of utilities to quickly and easily style elements through the addition of classes in your HTML. Now with Bootstrap v5.3.0, you can add utilities in your custom Sass with the `util()` mixin. Using the [utility API]({{< docsref "/utilities/api" >}}), we generate placeholders for every utility that can then be included via Sass mixin. Use the mixin in your own Sass files to include quick styles, override defaults, or compose entire components.
```html
<!-- Using utility classes -->
<div class="d-inline-flex p-4 mb-md-3">...</div>
<!-- Using a custom class and the utility mixin -->
<div class="test">...</div>
```
```scss
// Using the utility mixin in your custom Sass
.test {
// Individual utilities
@include util(d-inline-flex);
@include util(p-4);
@include util(mb-md-3);
// Multiple utilities
@include util(d-inline-flex, p-4, mb-md-3);
}
```
## Motivation
There are two major motivations for creating the utility mixin:
1. **Bootstrap should be as approachable and useful to as many people as possible.** This is why we provide compiled ready-to-go distribution files alongside source Sass and JavaScript. Similarly, not every situation calls for only pre-built components or all utilities.
2. **Working with utilities is systems-based development.** No matter where you apply your styles, you can now think and develop with the same powerful system of responsive property-value pairings, in your HTML or in your Sass.
The `util()` mixin takes something familiar and brings it into a new context, allowing you to style with a utility-based approach no matter where you apply your styles without ever changing how you think about CSS. It makes Bootstrap even more flexible and powerful, without forcing your hand into a particular development methodology.
## Example
Consider this `.test` example. We've set the display, added padding, included a responsive margin, and a custom color.
```scss
.test {
@include util(d-inline-flex);
@include util(p-4);
@include util(mb-md-3);
color: purple;
}
```
Which outputs to the following:
```css
.test {
display: inline-flex;
padding: 1.5rem;
color: purple;
}
@media (min-width: 768px) {
.test {
margin-bottom: 1rem;
}
}
```
## Working responsively
While you can include responsive utilities individually, Sass will generate multiple media queries each time you include a responsive utility with the `util()` mixin. When you have multiple responsive values, use media queries to group them.
```scss
// Avoid this
.test {
@include util(p-3);
@include util(p-md-5);
@include util(mb-3);
@include util(mb-md-5);
}
// Do this
.test {
@include util(p-3);
@include util(mb-3);
@include media-breakpoint-up(md) {
@include util(p-5);
@include util(mb-5);
}
}
```
This way, you only output one media query in your compiled CSS and no further optimization or action is needed to clean up the output.
## Multiple utilities
Include multiple utilities in a single `util()` mixin call with a comma separated list. Once again, for multiple responsive utilities, use a media query to group them under a single compiled media query.
```scss
.test {
@include util(d-inline-flex, p-4, mb-3);
color: purple;
@include media-breakpoint-up(md) {
@include util(p-5, mb-5);
}
}
```
## Usage
Want to use only the utilities and mixin in your own project? In your project's Sass file, include the following:
```scss
// Required Bootstrap imports
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/root";
// Import the utilities maps and mixins
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/utilities/api";
// Write your own styles
.test {
@include util(d-inline-flex, p-4, mb-md-3);
color: purple;
}
```
### Disable generated utilities
The `util()` mixin is powered by Sass placeholders, not the generated classes in the compiled CSS. This means you can disable the generated classes and still use the mixin in your custom Sass.
To do this, disable the `$enable-utility-classes` global option, import the required Sass files, and start using the new mixin.
```scss
// Disable the generated utility classes
$enable-utility-classes: false;
// Required Bootstrap imports
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/variables-dark";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/root";
// Import the utilities maps and mixins
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/utilities/api";
// Write your own styles
.test {
@include util(d-inline-flex, p-4, mb-md-3);
color: purple;
}
```
+1
View File
@@ -119,6 +119,7 @@
icon_color: red
pages:
- title: API
- title: Mixin
- title: Background
- title: Borders
- title: Colors