[d565449] | 1 | // stylelint-disable scss/dimension-no-non-numeric-values
|
---|
| 2 |
|
---|
| 3 | // SCSS RFS mixin
|
---|
| 4 | //
|
---|
| 5 | // Automated responsive values for font sizes, paddings, margins and much more
|
---|
| 6 | //
|
---|
| 7 | // Licensed under MIT (https://github.com/twbs/rfs/blob/main/LICENSE)
|
---|
| 8 |
|
---|
| 9 | // Configuration
|
---|
| 10 |
|
---|
| 11 | // Base value
|
---|
| 12 | $rfs-base-value: 1.25rem !default;
|
---|
| 13 | $rfs-unit: rem !default;
|
---|
| 14 |
|
---|
| 15 | @if $rfs-unit != rem and $rfs-unit != px {
|
---|
| 16 | @error "`#{$rfs-unit}` is not a valid unit for $rfs-unit. Use `px` or `rem`.";
|
---|
| 17 | }
|
---|
| 18 |
|
---|
| 19 | // Breakpoint at where values start decreasing if screen width is smaller
|
---|
| 20 | $rfs-breakpoint: 1200px !default;
|
---|
| 21 | $rfs-breakpoint-unit: px !default;
|
---|
| 22 |
|
---|
| 23 | @if $rfs-breakpoint-unit != px and $rfs-breakpoint-unit != em and $rfs-breakpoint-unit != rem {
|
---|
| 24 | @error "`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.";
|
---|
| 25 | }
|
---|
| 26 |
|
---|
| 27 | // Resize values based on screen height and width
|
---|
| 28 | $rfs-two-dimensional: false !default;
|
---|
| 29 |
|
---|
| 30 | // Factor of decrease
|
---|
| 31 | $rfs-factor: 10 !default;
|
---|
| 32 |
|
---|
| 33 | @if type-of($rfs-factor) != number or $rfs-factor <= 1 {
|
---|
| 34 | @error "`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.";
|
---|
| 35 | }
|
---|
| 36 |
|
---|
| 37 | // Mode. Possibilities: "min-media-query", "max-media-query"
|
---|
| 38 | $rfs-mode: min-media-query !default;
|
---|
| 39 |
|
---|
| 40 | // Generate enable or disable classes. Possibilities: false, "enable" or "disable"
|
---|
| 41 | $rfs-class: false !default;
|
---|
| 42 |
|
---|
| 43 | // 1 rem = $rfs-rem-value px
|
---|
| 44 | $rfs-rem-value: 16 !default;
|
---|
| 45 |
|
---|
| 46 | // Safari iframe resize bug: https://github.com/twbs/rfs/issues/14
|
---|
| 47 | $rfs-safari-iframe-resize-bug-fix: false !default;
|
---|
| 48 |
|
---|
| 49 | // Disable RFS by setting $enable-rfs to false
|
---|
| 50 | $enable-rfs: true !default;
|
---|
| 51 |
|
---|
| 52 | // Cache $rfs-base-value unit
|
---|
| 53 | $rfs-base-value-unit: unit($rfs-base-value);
|
---|
| 54 |
|
---|
| 55 | @function divide($dividend, $divisor, $precision: 10) {
|
---|
| 56 | $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);
|
---|
| 57 | $dividend: abs($dividend);
|
---|
| 58 | $divisor: abs($divisor);
|
---|
| 59 | @if $dividend == 0 {
|
---|
| 60 | @return 0;
|
---|
| 61 | }
|
---|
| 62 | @if $divisor == 0 {
|
---|
| 63 | @error "Cannot divide by 0";
|
---|
| 64 | }
|
---|
| 65 | $remainder: $dividend;
|
---|
| 66 | $result: 0;
|
---|
| 67 | $factor: 10;
|
---|
| 68 | @while ($remainder > 0 and $precision >= 0) {
|
---|
| 69 | $quotient: 0;
|
---|
| 70 | @while ($remainder >= $divisor) {
|
---|
| 71 | $remainder: $remainder - $divisor;
|
---|
| 72 | $quotient: $quotient + 1;
|
---|
| 73 | }
|
---|
| 74 | $result: $result * 10 + $quotient;
|
---|
| 75 | $factor: $factor * .1;
|
---|
| 76 | $remainder: $remainder * 10;
|
---|
| 77 | $precision: $precision - 1;
|
---|
| 78 | @if ($precision < 0 and $remainder >= $divisor * 5) {
|
---|
| 79 | $result: $result + 1;
|
---|
| 80 | }
|
---|
| 81 | }
|
---|
| 82 | $result: $result * $factor * $sign;
|
---|
| 83 | $dividend-unit: unit($dividend);
|
---|
| 84 | $divisor-unit: unit($divisor);
|
---|
| 85 | $unit-map: (
|
---|
| 86 | "px": 1px,
|
---|
| 87 | "rem": 1rem,
|
---|
| 88 | "em": 1em,
|
---|
| 89 | "%": 1%
|
---|
| 90 | );
|
---|
| 91 | @if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {
|
---|
| 92 | $result: $result * map-get($unit-map, $dividend-unit);
|
---|
| 93 | }
|
---|
| 94 | @return $result;
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | // Remove px-unit from $rfs-base-value for calculations
|
---|
| 98 | @if $rfs-base-value-unit == px {
|
---|
| 99 | $rfs-base-value: divide($rfs-base-value, $rfs-base-value * 0 + 1);
|
---|
| 100 | }
|
---|
| 101 | @else if $rfs-base-value-unit == rem {
|
---|
| 102 | $rfs-base-value: divide($rfs-base-value, divide($rfs-base-value * 0 + 1, $rfs-rem-value));
|
---|
| 103 | }
|
---|
| 104 |
|
---|
| 105 | // Cache $rfs-breakpoint unit to prevent multiple calls
|
---|
| 106 | $rfs-breakpoint-unit-cache: unit($rfs-breakpoint);
|
---|
| 107 |
|
---|
| 108 | // Remove unit from $rfs-breakpoint for calculations
|
---|
| 109 | @if $rfs-breakpoint-unit-cache == px {
|
---|
| 110 | $rfs-breakpoint: divide($rfs-breakpoint, $rfs-breakpoint * 0 + 1);
|
---|
| 111 | }
|
---|
| 112 | @else if $rfs-breakpoint-unit-cache == rem or $rfs-breakpoint-unit-cache == "em" {
|
---|
| 113 | $rfs-breakpoint: divide($rfs-breakpoint, divide($rfs-breakpoint * 0 + 1, $rfs-rem-value));
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 | // Calculate the media query value
|
---|
| 117 | $rfs-mq-value: if($rfs-breakpoint-unit == px, #{$rfs-breakpoint}px, #{divide($rfs-breakpoint, $rfs-rem-value)}#{$rfs-breakpoint-unit});
|
---|
| 118 | $rfs-mq-property-width: if($rfs-mode == max-media-query, max-width, min-width);
|
---|
| 119 | $rfs-mq-property-height: if($rfs-mode == max-media-query, max-height, min-height);
|
---|
| 120 |
|
---|
| 121 | // Internal mixin used to determine which media query needs to be used
|
---|
| 122 | @mixin _rfs-media-query {
|
---|
| 123 | @if $rfs-two-dimensional {
|
---|
| 124 | @if $rfs-mode == max-media-query {
|
---|
| 125 | @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}), (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {
|
---|
| 126 | @content;
|
---|
| 127 | }
|
---|
| 128 | }
|
---|
| 129 | @else {
|
---|
| 130 | @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) and (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {
|
---|
| 131 | @content;
|
---|
| 132 | }
|
---|
| 133 | }
|
---|
| 134 | }
|
---|
| 135 | @else {
|
---|
| 136 | @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) {
|
---|
| 137 | @content;
|
---|
| 138 | }
|
---|
| 139 | }
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | // Internal mixin that adds disable classes to the selector if needed.
|
---|
| 143 | @mixin _rfs-rule {
|
---|
| 144 | @if $rfs-class == disable and $rfs-mode == max-media-query {
|
---|
| 145 | // Adding an extra class increases specificity, which prevents the media query to override the property
|
---|
| 146 | &,
|
---|
| 147 | .disable-rfs &,
|
---|
| 148 | &.disable-rfs {
|
---|
| 149 | @content;
|
---|
| 150 | }
|
---|
| 151 | }
|
---|
| 152 | @else if $rfs-class == enable and $rfs-mode == min-media-query {
|
---|
| 153 | .enable-rfs &,
|
---|
| 154 | &.enable-rfs {
|
---|
| 155 | @content;
|
---|
| 156 | }
|
---|
| 157 | } @else {
|
---|
| 158 | @content;
|
---|
| 159 | }
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | // Internal mixin that adds enable classes to the selector if needed.
|
---|
| 163 | @mixin _rfs-media-query-rule {
|
---|
| 164 |
|
---|
| 165 | @if $rfs-class == enable {
|
---|
| 166 | @if $rfs-mode == min-media-query {
|
---|
| 167 | @content;
|
---|
| 168 | }
|
---|
| 169 |
|
---|
| 170 | @include _rfs-media-query () {
|
---|
| 171 | .enable-rfs &,
|
---|
| 172 | &.enable-rfs {
|
---|
| 173 | @content;
|
---|
| 174 | }
|
---|
| 175 | }
|
---|
| 176 | }
|
---|
| 177 | @else {
|
---|
| 178 | @if $rfs-class == disable and $rfs-mode == min-media-query {
|
---|
| 179 | .disable-rfs &,
|
---|
| 180 | &.disable-rfs {
|
---|
| 181 | @content;
|
---|
| 182 | }
|
---|
| 183 | }
|
---|
| 184 | @include _rfs-media-query () {
|
---|
| 185 | @content;
|
---|
| 186 | }
|
---|
| 187 | }
|
---|
| 188 | }
|
---|
| 189 |
|
---|
| 190 | // Helper function to get the formatted non-responsive value
|
---|
| 191 | @function rfs-value($values) {
|
---|
| 192 | // Convert to list
|
---|
| 193 | $values: if(type-of($values) != list, ($values,), $values);
|
---|
| 194 |
|
---|
| 195 | $val: "";
|
---|
| 196 |
|
---|
| 197 | // Loop over each value and calculate value
|
---|
| 198 | @each $value in $values {
|
---|
| 199 | @if $value == 0 {
|
---|
| 200 | $val: $val + " 0";
|
---|
| 201 | }
|
---|
| 202 | @else {
|
---|
| 203 | // Cache $value unit
|
---|
| 204 | $unit: if(type-of($value) == "number", unit($value), false);
|
---|
| 205 |
|
---|
| 206 | @if $unit == px {
|
---|
| 207 | // Convert to rem if needed
|
---|
| 208 | $val: $val + " " + if($rfs-unit == rem, #{divide($value, $value * 0 + $rfs-rem-value)}rem, $value);
|
---|
| 209 | }
|
---|
| 210 | @else if $unit == rem {
|
---|
| 211 | // Convert to px if needed
|
---|
| 212 | $val: $val + " " + if($rfs-unit == px, #{divide($value, $value * 0 + 1) * $rfs-rem-value}px, $value);
|
---|
| 213 | } @else {
|
---|
| 214 | // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value
|
---|
| 215 | $val: $val + " " + $value;
|
---|
| 216 | }
|
---|
| 217 | }
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 | // Remove first space
|
---|
| 221 | @return unquote(str-slice($val, 2));
|
---|
| 222 | }
|
---|
| 223 |
|
---|
| 224 | // Helper function to get the responsive value calculated by RFS
|
---|
| 225 | @function rfs-fluid-value($values) {
|
---|
| 226 | // Convert to list
|
---|
| 227 | $values: if(type-of($values) != list, ($values,), $values);
|
---|
| 228 |
|
---|
| 229 | $val: "";
|
---|
| 230 |
|
---|
| 231 | // Loop over each value and calculate value
|
---|
| 232 | @each $value in $values {
|
---|
| 233 | @if $value == 0 {
|
---|
| 234 | $val: $val + " 0";
|
---|
| 235 | } @else {
|
---|
| 236 | // Cache $value unit
|
---|
| 237 | $unit: if(type-of($value) == "number", unit($value), false);
|
---|
| 238 |
|
---|
| 239 | // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value
|
---|
| 240 | @if not $unit or $unit != px and $unit != rem {
|
---|
| 241 | $val: $val + " " + $value;
|
---|
| 242 | } @else {
|
---|
| 243 | // Remove unit from $value for calculations
|
---|
| 244 | $value: divide($value, $value * 0 + if($unit == px, 1, divide(1, $rfs-rem-value)));
|
---|
| 245 |
|
---|
| 246 | // Only add the media query if the value is greater than the minimum value
|
---|
| 247 | @if abs($value) <= $rfs-base-value or not $enable-rfs {
|
---|
| 248 | $val: $val + " " + if($rfs-unit == rem, #{divide($value, $rfs-rem-value)}rem, #{$value}px);
|
---|
| 249 | }
|
---|
| 250 | @else {
|
---|
| 251 | // Calculate the minimum value
|
---|
| 252 | $value-min: $rfs-base-value + divide(abs($value) - $rfs-base-value, $rfs-factor);
|
---|
| 253 |
|
---|
| 254 | // Calculate difference between $value and the minimum value
|
---|
| 255 | $value-diff: abs($value) - $value-min;
|
---|
| 256 |
|
---|
| 257 | // Base value formatting
|
---|
| 258 | $min-width: if($rfs-unit == rem, #{divide($value-min, $rfs-rem-value)}rem, #{$value-min}px);
|
---|
| 259 |
|
---|
| 260 | // Use negative value if needed
|
---|
| 261 | $min-width: if($value < 0, -$min-width, $min-width);
|
---|
| 262 |
|
---|
| 263 | // Use `vmin` if two-dimensional is enabled
|
---|
| 264 | $variable-unit: if($rfs-two-dimensional, vmin, vw);
|
---|
| 265 |
|
---|
| 266 | // Calculate the variable width between 0 and $rfs-breakpoint
|
---|
| 267 | $variable-width: #{divide($value-diff * 100, $rfs-breakpoint)}#{$variable-unit};
|
---|
| 268 |
|
---|
| 269 | // Return the calculated value
|
---|
| 270 | $val: $val + " calc(" + $min-width + if($value < 0, " - ", " + ") + $variable-width + ")";
|
---|
| 271 | }
|
---|
| 272 | }
|
---|
| 273 | }
|
---|
| 274 | }
|
---|
| 275 |
|
---|
| 276 | // Remove first space
|
---|
| 277 | @return unquote(str-slice($val, 2));
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | // RFS mixin
|
---|
| 281 | @mixin rfs($values, $property: font-size) {
|
---|
| 282 | @if $values != null {
|
---|
| 283 | $val: rfs-value($values);
|
---|
| 284 | $fluid-val: rfs-fluid-value($values);
|
---|
| 285 |
|
---|
| 286 | // Do not print the media query if responsive & non-responsive values are the same
|
---|
| 287 | @if $val == $fluid-val {
|
---|
| 288 | #{$property}: $val;
|
---|
| 289 | }
|
---|
| 290 | @else {
|
---|
| 291 | @include _rfs-rule () {
|
---|
| 292 | #{$property}: if($rfs-mode == max-media-query, $val, $fluid-val);
|
---|
| 293 |
|
---|
| 294 | // Include safari iframe resize fix if needed
|
---|
| 295 | min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null);
|
---|
| 296 | }
|
---|
| 297 |
|
---|
| 298 | @include _rfs-media-query-rule () {
|
---|
| 299 | #{$property}: if($rfs-mode == max-media-query, $fluid-val, $val);
|
---|
| 300 | }
|
---|
| 301 | }
|
---|
| 302 | }
|
---|
| 303 | }
|
---|
| 304 |
|
---|
| 305 | // Shorthand helper mixins
|
---|
| 306 | @mixin font-size($value) {
|
---|
| 307 | @include rfs($value);
|
---|
| 308 | }
|
---|
| 309 |
|
---|
| 310 | @mixin padding($value) {
|
---|
| 311 | @include rfs($value, padding);
|
---|
| 312 | }
|
---|
| 313 |
|
---|
| 314 | @mixin padding-top($value) {
|
---|
| 315 | @include rfs($value, padding-top);
|
---|
| 316 | }
|
---|
| 317 |
|
---|
| 318 | @mixin padding-right($value) {
|
---|
| 319 | @include rfs($value, padding-right);
|
---|
| 320 | }
|
---|
| 321 |
|
---|
| 322 | @mixin padding-bottom($value) {
|
---|
| 323 | @include rfs($value, padding-bottom);
|
---|
| 324 | }
|
---|
| 325 |
|
---|
| 326 | @mixin padding-left($value) {
|
---|
| 327 | @include rfs($value, padding-left);
|
---|
| 328 | }
|
---|
| 329 |
|
---|
| 330 | @mixin margin($value) {
|
---|
| 331 | @include rfs($value, margin);
|
---|
| 332 | }
|
---|
| 333 |
|
---|
| 334 | @mixin margin-top($value) {
|
---|
| 335 | @include rfs($value, margin-top);
|
---|
| 336 | }
|
---|
| 337 |
|
---|
| 338 | @mixin margin-right($value) {
|
---|
| 339 | @include rfs($value, margin-right);
|
---|
| 340 | }
|
---|
| 341 |
|
---|
| 342 | @mixin margin-bottom($value) {
|
---|
| 343 | @include rfs($value, margin-bottom);
|
---|
| 344 | }
|
---|
| 345 |
|
---|
| 346 | @mixin margin-left($value) {
|
---|
| 347 | @include rfs($value, margin-left);
|
---|
| 348 | }
|
---|