Reputation: 5179
I have a nested scss
list with some font definitions:
$font-cinzel: "Cinzel", serif;
$font-poppins: "Poppins", sans-serif;
$fonts: (
"title": (
"all": (
font-family: $font-cinzel
),
"sm": (
font-size: 26px,
line-height: 1.4
),
"lg": (
font-size: 80px,
line-height: 1.6
)
),
"text": (
"all": (
font-family: $font-poppins
),
"sm": (
font-size: 16px,
line-height: 1.2
),
"lg": (
font-size: 36px,
line-height: 1.4
)
)
);
Now I would like to write a mixin
and a function
to generate a styling block when passing the font name for each viewport. So all styles defined in all
should be rendered outside of a media query
(rule for all viewports to avoid redundancy). All other style which are unique for the viewport are defined in the viewport block e.g. all small styles are in sm
, all large styles with different values in lg
etc. I also have a breakpoint mixin, where I map the min-width
by using the viewport name form the font list:
breakpoint mixin:
$breakpoints: (
sm: 768px,
md: 1024px,
lg: 1280px,
xl: 1400px
);
@function get-bp($bp) {
@if $bp {
$bp: map-get($breakpoints, $bp);
} @else {
@error "Parameter #{$vp} is unknown or empty.";
}
@return $bp;
}
This is how I try to loop the stuff:
@function get-value($definition, $vp) {
@each $prop, $val in map-get($definition, $vp) {
@return $val;
}
}
@mixin font($name) {
$definition: map-get($fonts, $name);
@each $key, $value in $definition {
@if $key == "all" {
$prop: get-value($definition, $key);
} @else {
@media (min-width: get-bp($key)) {
$prop: get-value($definition, $key);
}
}
}
}
h1 {
@include font('title');
}
p {
@include font('text');
}
Expected output:
h1 {
font-family: 'Cinzel', serif;
@media (min-width: 768px) {
font-size: 26px;
line-height: 1.4;
}
@media (min-width: 1280px) {
font-size: 80px;
line-height: 1.6;
}
}
p {
font-family: 'Poppins', sans-serif;
@media (min-width: 768px) {
font-size: 16px;
line-height: 1.2;
}
@media (min-width: 1280px) {
font-size: 36px;
line-height: 1.4;
}
}
I don't get any errors, but the styles are not available for the elements.
Here is the codepen: https://codepen.io/STWebtastic/pen/QWdOZPN
Upvotes: 0
Views: 1301
Reputation: 3084
As to your update in your question:
Here is a mixin example to your updated requirements.
The example is done in a more general way for multiple use and a good readability.
So font-sizing-settings works as well as margins or even (nearly) every other property setting you would like ot adapt to the breakpoints. And once more with map-element selector
it works on simple elements (tags), on classes, id's and more complex selectors as well.
Additional to the $rules
map it is based on a breakpoint map. That make sure it works on all breakpoints adviced to the project. But rules to the breakpoints are only added to an element if noted in the rule map ... so you can say: it's an universal swiss knife.
EXAMPLE:
//###### SASS
// this example assumes:
// breakpoints allways min-width ...
$breakpoints: (
sm: 768px,
md: 1024px,
lg: 1280px,
xl: 1400px
);
$rules: (
title: (
selector: 'h1',
all: (
font-family: 'Arial',
font-size: 26px,
line-height: 1.4,
),
sm: (
font-size: 26px,
line-height: 1.4,
),
lg: (
font-size: 80px,
line-height: 1.6,
),
xl: (
font-size: 100px,
),
),
text: (
selector: 'p',
all: (
font-family: 'Courier New',
font-size: 26px,
line-height: 1.4,
),
sm: (
font-size: 16px,
line-height: 1.2,
),
lg: (
font-size: 36px,
line-height: 1.4,
)
),
) !default;
@mixin fontSizing($rule-map, $breakpoints: $breakpoints){
@each $element, $settings-map in $rule-map {
$selector: map-get($settings-map, selector);
// write generel rules
#{$selector} {
@each $property, $value in map-get($settings-map, all){
#{$property}: $value;
}
}
// rules for every breakpoint
@each $breakpoint, $breakpoint-setting in $breakpoints {
// only if breakpoint values set for element
@if map-has-key( $settings-map, $breakpoint ){
// write breakpoints rule
@media ( min-width: #{$breakpoint-setting} ){
#{$selector} {
@each $property, $value in map-get($settings-map, $breakpoint){
#{$property}: $value;
}
}
}
}//if
}//each
}//each
}//mixin
//##### CALL MIXIN
@include fontSizing($rules);
//##### COMPILES TO...
h1 {
font-family: "Arial";
font-size: 26px;
line-height: 1.4;
}
@media (min-width: 768px) {
h1 {
font-size: 26px;
line-height: 1.4;
}
}
@media (min-width: 1280px) {
h1 {
font-size: 80px;
line-height: 1.6;
}
}
@media (min-width: 1400px) {
h1 {
font-size: 100px;
}
}
p {
font-family: "Courier New";
font-size: 26px;
line-height: 1.4;
}
@media (min-width: 768px) {
p {
font-size: 16px;
line-height: 1.2;
}
}
@media (min-width: 1280px) {
p {
font-size: 36px;
line-height: 1.4;
}
}
ADDITIONAL HINT/IMPULSE:
It is possible to compress the rule map. In that case the code working is less, - but more specialised to single tags only and the pre-defined font-sizing settings ... and it is less readable. The general construction of the mixin would be the same but code writing would change a little bit as you work with predefined properties and nested lists instead of nested maps. Feel free to adapt the code. Here is an example for the possible compression to the rule-map:
$rule: (
h1: (
fontFamily: 'Arial',
all: (10px, 1.2),
sm: (12px, 1.4),
lg: (24px, 1.6),
),
p: (
fontFamily: 'Courier New',
all: (8px, 1.2),
sm: (10px, 1.4),
lg: (20px, 1.6),
xl: (24px, 1.8),
),
) !default;
Upvotes: 1
Reputation: 3084
There are some huddles in your code ...
The mixin uses an @each
loop ... which is not needed but malicious as you don't want to write all classes at once but to use it to include only a special group of properties to a single class/selector (in your example you try to advice to h1 {...}
only.
The error:
map-get($fonts, $name)' which in your example is inner map
title`.= fontFamily, sm, lg
) it should do something ...fontFamily
fontFamily
...map-get
and realize fontFamily
you named as map for the function is not a map ...... and you are in trouble.
What you may do:
Code could look like this:
// ### SASS
$fonts: (
title: (
fontFamily: 'Arial',
sm: 12px,
lg: 24px,
),
text: (
fontFamily: 'Courier New',
sm: 10px,
lg: 20px,
),
) !default;
@mixin font( $item, $size ) {
font-family: map-get(map-get($fonts, $item), fontFamily );
font-size: map-get(map-get($fonts, $item), $size );
line-height: 1.2em;
}
h1 {
@include font( title, sm);
}
// ### Compiles to CSS
h1 {
font-family: "Arial";
font-size: 12px;
line-height: 1.2em;
}
Upvotes: 1