Using CSS Logical Properties TODAY

Itzik Pop
12 min readJan 6, 2019

Intro

I read Elad Shechter’s article on the new CSS Logical Properties (LP) and found it useful and thought to start using them today.

I’ve checked which browser doesn’t support these new LP and found that IE and EDGE don’t support them.

I thought, what can I do to start using all of those new properties this today and came up with an idea in collaboration with Eli Philosoph to write a mixin to give the user the ability to use LP along with a fall back to old version and to avoid writing the entire property name every time (they are so long to write).

I set down and started to write the mixin, and found that there are some issues to write a single mixin for this purpose and two mixins ere needed.
One for border, margin, padding, height, width, float and text-align properties, and one for position properties.

In this post I will not write about the difference between each property. You can find this information on Elad’s article.

The writing mode (WM) property

I’ve added 1 value for writing mode and with this variable, we can change the direction of the element

The position mixin:

@mixin position($t, $b-start: null, $i-start: null, $b-end: null, $i-end: null) {
@if ($t == 'absolute') {
position: absolute;
} @else if ($t == 'relative') {
position: relative;
};
top: $b-start;
right: $i-start;
bottom: $b-end;
left: $i-end;
inset-block-start: $b-start;
inset-block-end: $b-end;
inset-inline-start: $i-start;
inset-inline-end: $i-end;
}

Let’s break it apart:
- We have 4 values
- Each value gets “null” as a default value
- Each value will set to the correct LP with the fall back

For all of these variables, I would have built a table more organized and prevents repetitions:

$b-start — Will set to top (fall back) and inset-block-start
$i-start — Will set to right (fall back) and inset-inline-start
$b-end — Will set to bottom (fall back) and inset-block-end
$i-end — Will set to left (fall back) and inset-inline-end

Calling the mixin as follows:

@include position('absolute', 10px);

will result on the generated CSS file as:

position: absolute;
top: 10px;
inset-block-start: 10px;

Calling the mixin as follows:

@include position('relative', 10px, 25px);

will result on the generated CSS file as:

position: relative;
top: 10px;
right: 25px;
inset-block-start: 10px;
inset-inline-end: 25px;

I’ve kept using the “old” box-model “compass” for easy use:

top, right, bottom, left

The second mixin — and the bigger one — is to support the border, margin, padding, height, width, float and text-align properties

@mixin lp($t, $values...) {
[Code will goes here]
}

These are the properties of the mixin:

  • $t- Type of the property we want to create - border, margin, padding, height, width, float and text-align
  • $values… - List of the values we want to give for each property

Margin and Padding properties

@if ($t == 'margin' or $t == 'padding') {
@if (length($values) == 1) {
#{$t}: $values;
#{$t}-block-start: $values;
#{$t}-inline-start: $values;
#{$t}-block-end: $values;
#{$t}-inline-end: $values;
} @else if (length($values) == 2) {
@if ($wm == 'ver-l') {
#{$t}: nth($values, 2) nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}: nth($values, 2) nth($values, 1);
} @else {
#{$t}: nth($values, 1) nth($values, 2);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 2);
#{$t}-block-end: nth($values, 1);
#{$t}-inline-end: nth($values, 2);
} @else if (length($values) == 3) {
@if ($wm == 'ver-l') {
#{$t}: nth($values, 2) nth($values, 3) nth($values, 2) nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}: nth($values, 2) nth($values, 1) nth($values, 2) nth($values, 3);
} @else {
#{$t}: nth($values, 1) nth($values, 2) nth($values, 3);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 2);
#{$t}-block-end: nth($values, 3);
#{$t}-inline-end: nth($values, 2);
} @else if (length($values) == 4) {
@if ($wm == 'ver-l') {
#{$t}: nth($values, 4) nth($values, 2) nth($values, 3) nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}: nth($values, 4) nth($values, 2) nth($values, 3) nth($values, 1);
} @else {
#{$t}: nth($values, 1) nth($values, 2) nth($values, 3) nth($values, 4);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 4);
#{$t}-block-end: nth($values, 2);
#{$t}-inline-end: nth($values, 3);
}
}

Margin and Padding properties getting the same properties so they are together on the mixin

Calling the mixin as follow:

@include lp('margin', '', 2rem);

will result on the generated CSS file as:

margin: 2rem;
margin-block-start: 2rem;
margin-inline-start: 2rem;
margin-block-end: 2rem;
margin-inline-end: 2rem;

But if’ll pass the WM property and set it to “ver-l” as follow:

@include lp('padding', 'ver-l', 1rem, 2rem, 3rem, 4rem);

This will result on the generated CSS file as:

padding: 4rem 2rem 3rem 1rem;
padding-block-start: 1rem;
padding-inline-start: 4rem;
padding-block-end: 2rem;
padding-inline-end: 3rem;

As you can see, the order of the properties changed and the padding-top getting the 4rem even it is the last one

Border property

@else if ($t == 'border') {
@if (length($values) == 1) {
#{$t}: $values;
#{$t}-block-start: $values;
#{$t}-inline-start: $values;
#{$t}-block-end: $values;
#{$t}-inline-end: $values;
} @else if (length($values) == 2) {
@if ($wm == 'ver-l') {
#{$t}-top: nth($values, 2);
#{$t}-right: nth($values, 1);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}-top: nth($values, 2);
#{$t}-right: nth($values, 1);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 1);
} @else {
#{$t}-top: nth($values, 1);
#{$t}-right: nth($values, 2);
#{$t}-bottom: nth($values, 1);
#{$t}-left: nth($values, 2);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 2);
#{$t}-block-end: nth($values, 1);
#{$t}-inline-end: nth($values, 2);
} @else if (length($values) == 3) {
@if ($wm == 'ver-l') {
#{$t}-top: nth($values, 2);
#{$t}-right: nth($values, 3);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}-top: nth($values, 2);
#{$t}-right: nth($values, 1);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 3);
} @else {
#{$t}-top: nth($values, 1);
#{$t}-right: nth($values, 2);
#{$t}-bottom: nth($values, 3);
#{$t}-left: nth($values, 2);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 2);
#{$t}-block-end: nth($values, 3);
#{$t}-inline-end: nth($values, 2);
} @else if (length($values) == 4) {
@if ($wm == 'ver-l') {
#{$t}-top: nth($values, 4);
#{$t}-right: nth($values, 3);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}-top: nth($values, 4);
#{$t}-right: nth($values, 1);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 3);
} @else {
#{$t}-top: nth($values, 1);
#{$t}-right: nth($values, 2);
#{$t}-bottom: nth($values, 3);
#{$t}-left: nth($values, 4);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 4);
#{$t}-block-end: nth($values, 3);
#{$t}-inline-end: nth($values, 2);
}
}

For border property we can pass the WM property and each 1 to 4 borders properties

Calling the mixin as follow:

@include lp('border', '', solid 1px black);

will result on the generated CSS file as:

border: solid 1px black;
border-block-start: solid 1px black;
border-inline-start: solid 1px black;
border-block-end: solid 1px black;
border-inline-end: solid 1px black;

But if we’ll change the WM variable to ‘ver-r’ the calling the mixin as follow:

@include lp('border', solid 1px $black, solid 2px $black);

will result on the generated CSS file as:

border-top: solid 2px black;
border-right: solid 1px black;
border-bottom: solid 2px black;
border-left: solid 1px black;
border-block-start: solid 1px black;
border-inline-start: solid 2px black;
border-block-end: solid 1px black;
border-inline-end: solid 2px black;

As you can see, the top and bottom border getting the 2px width and this is because of the of the WM from horizontal to vertical

Height, Width properties

@else if ($t == 'height') {
@if (length($values) == 1) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
width: $values;
inline-size: $values;
} @else {
#{$t}: $values;
block-size: $values;
}
} @if (length($values) == 2) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
width: nth($values, 1);
inline-size: nth($values, 1);
min-width: nth($values, 2);
min-inline-size: nth($values, 2);
} @else {
#{$t}: nth($values, 1);
block-size: nth($values, 1);
min-#{$t}: nth($values, 2);
min-block-size: nth($values, 2);
}
} @if (length($values) == 3) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
width: nth($values, 1);
inline-size: nth($values, 1);
min-width: nth($values, 2);
min-inline-size: nth($values, 2);
max-width: nth($values, 3);
max-inline-size: nth($values, 3);
} @else {
#{$t}: nth($values, 1);
block-size: nth($values, 1);
min-#{$t}: nth($values, 2);
min-block-size: nth($values, 2);
max-#{$t}: nth($values, 3);
max-block-size: nth($values, 3);
}
}
}
@else if ($t == 'width') {
@if (length($values) == 1) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
height: $values;
block-size: $values;
} @else {
#{$t}: $values;
inline-size: $values;
}
} @if (length($values) == 2) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
height: nth($values, 1);
block-size: nth($values, 1);
min-height: nth($values, 2);
min-block-size: nth($values, 2);
} @else {
#{$t}: nth($values, 1);
inline-size: nth($values, 1);
min-#{$t}: nth($values, 2);
min-inline-size: nth($values, 2);
}
} @if (length($values) == 3) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
height: nth($values, 1);
block-size: nth($values, 1);
min-height: nth($values, 2);
min-block-size: nth($values, 2);
max-height: nth($values, 3);
max-block-size: nth($values, 3);
} @else {
#{$t}: nth($values, 1);
inline-size: nth($values, 1);
min-#{$t}: nth($values, 2);
min-inline-size: nth($values, 2);
max-#{$t}: nth($values, 3);
max-inline-size: nth($values, 3);
}
}
}

First, we need to check the number of properties that passed and than we need to check the WM. If the WM is set to “vertical-lr” or “vertical-rl” then we set the values for each property.

Calling the mixin as follow:

@include lp('height', '', 300px, 150px, 450px);

will result on the generated CSS file as:

height: 300px;
block-size: 300px;
min-height: 150px;
min-block-size: 150px;
max-height: 450px;
max-block-size: 450px;

Same as for width

Calling the mixin as follow:

@include lp('width', 'ver-l', 300px, 150px, 450px);

will result on the generated CSS file as:

width: 300px;
inline-size: 300px;
min-width: 150px;
min-inline-size: 150px;
max-width: 450px;
max-inline-size: 450px;

Setting the WM property, will change the property from height to width and from block to inline (Block — top to bottom, Inline — left to right — read more on Elad’s article)

Float property

@else if ($t == 'float') {
@if (nth($values, 1) == 'left') {
#{$t}: $values;
#{$t}: inline-start;
} @else if(nth($values, 1) == 'right') {
#{$t}: $values;
#{$t}: inline-end;
}
}

Float property will not get the WM property so we don’t need to check it (but we must pass an empty value), we are checking only the direction of floating

Calling the mixin as follow:

@include lp('float', '', left);

will result on the generated CSS file as:

float: left;
float: inline-start;

Text align property

@else if ($t == 'text-align') {
@if (nth($values, 1) == 'left') {
#{$t}: $values;
#{$t}: start;
} @else if(nth($values, 1) == 'right') {
#{$t}: $values;
#{$t}: end;
}
}

Text align property will not get the WM property like the float, so we don’t need to check it (but we must pass an empty value), we are checking only the direction of text-align

Calling the mixin as follow:

@include lp('text-align', '', left);

will result on the generated CSS file as:

text-align: left;
text-align: start;

Let’s see the entire mixin together

@mixin lp($t, $values...) {
@if ($t == 'margin' or $t == 'padding') {
@if (length($values) == 1) {
#{$t}: $values;
#{$t}-block-start: $values;
#{$t}-inline-start: $values;
#{$t}-block-end: $values;
#{$t}-inline-end: $values;
} @else if (length($values) == 2) {
@if ($wm == 'ver-l') {
#{$t}: nth($values, 2) nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}: nth($values, 2) nth($values, 1);
} @else {
#{$t}: nth($values, 1) nth($values, 2);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 2);
#{$t}-block-end: nth($values, 1);
#{$t}-inline-end: nth($values, 2);
} @else if (length($values) == 3) {
@if ($wm == 'ver-l') {
#{$t}: nth($values, 2) nth($values, 3) nth($values, 2) nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}: nth($values, 2) nth($values, 1) nth($values, 2) nth($values, 3);
} @else {
#{$t}: nth($values, 1) nth($values, 2) nth($values, 3);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 2);
#{$t}-block-end: nth($values, 3);
#{$t}-inline-end: nth($values, 2);
} @else if (length($values) == 4) {
@if ($wm == 'ver-l') {
#{$t}: nth($values, 4) nth($values, 2) nth($values, 3) nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}: nth($values, 4) nth($values, 2) nth($values, 3) nth($values, 1);
} @else {
#{$t}: nth($values, 1) nth($values, 2) nth($values, 3) nth($values, 4);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 4);
#{$t}-block-end: nth($values, 2);
#{$t}-inline-end: nth($values, 3);
}
} @else if ($t == 'border') {
@if (length($values) == 1) {
#{$t}: $values;
#{$t}-block-start: $values;
#{$t}-inline-start: $values;
#{$t}-block-end: $values;
#{$t}-inline-end: $values;
} @else if (length($values) == 2) {
@if ($wm == 'ver-l') {
#{$t}-top: nth($values, 2);
#{$t}-right: nth($values, 1);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}-top: nth($values, 2);
#{$t}-right: nth($values, 1);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 1);
} @else {
#{$t}-top: nth($values, 1);
#{$t}-right: nth($values, 2);
#{$t}-bottom: nth($values, 1);
#{$t}-left: nth($values, 2);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 2);
#{$t}-block-end: nth($values, 1);
#{$t}-inline-end: nth($values, 2);
} @else if (length($values) == 3) {
@if ($wm == 'ver-l') {
#{$t}-top: nth($values, 2);
#{$t}-right: nth($values, 3);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}-top: nth($values, 2);
#{$t}-right: nth($values, 1);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 3);
} @else {
#{$t}-top: nth($values, 1);
#{$t}-right: nth($values, 2);
#{$t}-bottom: nth($values, 3);
#{$t}-left: nth($values, 2);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 2);
#{$t}-block-end: nth($values, 3);
#{$t}-inline-end: nth($values, 2);
} @else if (length($values) == 4) {
@if ($wm == 'ver-l') {
#{$t}-top: nth($values, 4);
#{$t}-right: nth($values, 3);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 1);
} @else if ($wm == 'ver-r') {
#{$t}-top: nth($values, 4);
#{$t}-right: nth($values, 1);
#{$t}-bottom: nth($values, 2);
#{$t}-left: nth($values, 3);
} @else {
#{$t}-top: nth($values, 1);
#{$t}-right: nth($values, 2);
#{$t}-bottom: nth($values, 3);
#{$t}-left: nth($values, 4);
}
#{$t}-block-start: nth($values, 1);
#{$t}-inline-start: nth($values, 4);
#{$t}-block-end: nth($values, 3);
#{$t}-inline-end: nth($values, 2);
}
} @else if ($t == 'height') {
@if (length($values) == 1) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
width: $values;
inline-size: $values;
} @else {
#{$t}: $values;
block-size: $values;
}
} @if (length($values) == 2) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
width: nth($values, 1);
inline-size: nth($values, 1);
min-width: nth($values, 2);
min-inline-size: nth($values, 2);
} @else {
#{$t}: nth($values, 1);
block-size: nth($values, 1);
min-#{$t}: nth($values, 2);
min-block-size: nth($values, 2);
}
} @if (length($values) == 3) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
width: nth($values, 1);
inline-size: nth($values, 1);
min-width: nth($values, 2);
min-inline-size: nth($values, 2);
max-width: nth($values, 3);
max-inline-size: nth($values, 3);
} @else {
#{$t}: nth($values, 1);
block-size: nth($values, 1);
min-#{$t}: nth($values, 2);
min-block-size: nth($values, 2);
max-#{$t}: nth($values, 3);
max-block-size: nth($values, 3);
}
}
} @else if ($t == 'width') {
@if (length($values) == 1) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
height: $values;
block-size: $values;
} @else {
#{$t}: $values;
inline-size: $values;
}
} @if (length($values) == 2) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
height: nth($values, 1);
block-size: nth($values, 1);
min-height: nth($values, 2);
min-block-size: nth($values, 2);
} @else {
#{$t}: nth($values, 1);
inline-size: nth($values, 1);
min-#{$t}: nth($values, 2);
min-inline-size: nth($values, 2);
}
} @if (length($values) == 3) {
@if ($wm == 'ver-l') or ($wm == 'ver-r') {
height: nth($values, 1);
block-size: nth($values, 1);
min-height: nth($values, 2);
min-block-size: nth($values, 2);
max-height: nth($values, 3);
max-block-size: nth($values, 3);
} @else {
#{$t}: nth($values, 1);
inline-size: nth($values, 1);
min-#{$t}: nth($values, 2);
min-inline-size: nth($values, 2);
max-#{$t}: nth($values, 3);
max-inline-size: nth($values, 3);
}
}
} @else if ($t == 'float') {
@if (nth($values, 1) == 'left') {
#{$t}: $values;
#{$t}: inline-start;
} @else if(nth($values, 1) == 'right') {
#{$t}: $values;
#{$t}: inline-end;
}
} @else if ($t == 'text-align') {
@if (nth($values, 1) == 'left') {
#{$t}: $values;
#{$t}: start;
} @else if(nth($values, 1) == 'right') {
#{$t}: $values;
#{$t}: end;
}
}
}

As you can see there are a lot of usage of @IF / @ELSE in the mixin, they are used to simplify / break apart the values and set them to their corresponding property of the new LP

Now that I’ve walked you through my created mixin, you can start using CSS logic properties on every browser and you can start today :-)

You can see all of the examples above in this codepen that I’ve created and you can download the mixin from github

Thanks to Oshrat Choen for her help with the writing of this article

--

--