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 | }
|
---|