Physical properties to logical properties
In the last months, I have been diving into the beautiful world of logical properties. The CSS spec has one sentence that I think describes what logical properties are in a nice way:
[Logical] properties are writing-mode relative equivalents of their corresponding physical properties.
Physical properties are simple to reason about, since they represent physical directions. Top, right, bottom and left are absolute directions. Left is always left—simple as that. Logical properties, on the other hand, are relative—the physical direction they correspond to varies.
As mentioned, the absolute nature of physical properties makes them simple to reason about. However, the absolute nature is also a limitation.
The problem with physical properties
Let's say we want to display a qoute with a red border to the left together with some padding:
<blockquote>Hello</blockquote>
blockquote {
border-left: 2px solid red;
padding-left: 1rem;
}
Result:
Hello
Cool!
But what happens if we translate the paragraph to Arabic? Arabic has a right-to-left text direction, so let's specify that with dir="rtl"
, and also provide mark that Arabic is used in the paragraph with lang="ar"
.
But what happens if this is done in a language with a right-to-left text direction? Let's try updating the text direction while keeping the same CSS:
<blockquote dir="rtl" lang="ar">أهلا</blockquote>
Result:
أهلا
That's not what we want... 😕
When the text direction is changed, we want the margin to be updated with it. But left is still left, even when the text direction is changed.
Enter logical properties
Let's try using logical properties instead!
<blockquote>Hello</blockquote>
blockquote {
border-inline-start: 1px solid red;
padding-inline-start: 2rem;
}
Result:
Hello
Let's see what happens now when translating to Arabic.
Let's try to change the text direction again while keeping the same CSS.
<blockquote dir="rtl" lang="ar">أهلا</blockquote>
Result:
أهلا
Yeah! 🎉
I scratched my head a lot when I started converting physical properties to their logical equivalents. During the process, I made some conversion tables for future reference.
Converting to logical properties
In the following conversion tables, the baseline text direction is left-to-right, top-to-bottom, as regular English.
Padding
Here's how physical padding properties are converted to logical:
Physical property | Logical property |
---|---|
padding-top | padding-block-start |
padding-right | padding-inline-end |
padding-bottom | padding-block-end |
padding-right | padding-inline-start |
Shorthands
The padding
property is a shorthand that enables setting padding in multiple directions in one declaration. padding: 1rem;
sets 1rem
padding in all four directions. Since it sets the same padding in all direction, there's no need to convert to logical—it would still have the same result.
But consider this padding declaration:
p {
padding: 1rem 2rem;
}
That's a shorthand for these declarations:
p {
padding-top: 1rem;
padding-bottom: 1rem;
padding-left: 2rem;
padding-right: 2rem;
}
In logical property land, this is the equivalent:
p {
padding-block: 1rem;
padding-inline: 2rem;
}
In physical property land, setting top and bottom padding is commonly done with the shorthand padding: 1rem 0;
. However, this sets left and right padding to the default value 0
as a "side effect". The logical padding-block: 1rem;
is not truly equivalent, as left and right padding is not set in that case.
Setting default values when there's no need for unsetting, just to be able to use the shorthands, might be considered bad practice. This is another reason why padding-block
and padding-inline
are great replacements for the padding
shorthand in many cases!
Margin
Logical margin properties work in the exact same way as logical padding properties. Here's the conversion table:
Physical property | Logical property |
---|---|
margin-top | margin-block-start |
margin-right | margin-inline-end |
margin-bottom | margin-block-end |
margin-right | margin-inline-start |
The margin property has also has shorthands equivalent to the padding shorthands.
Proposal for flow-relative margin
shorthand
margin
shorthandIn the future, we might have a margin-logical
, as a logical shorthand
equivalent to the physical margin
shorthand. See the proposal discussion
here: https://github.com/w3c/csswg-drafts/issues/1282
Borders
Here's the conversion table for border properties:
Physical property | Logical property |
---|---|
border-top | border-block-start |
border-right | border-inline-end |
border-bottom | border-block-end |
border-left | border-inline-start |
border-top-{color|style|width}
can also be converted to border-block-start-{color|style|width}
, exactly as you would imagine. I won't add these to the conversion table to avoid making it too cluttered.
Shorthands
A logical property shorthand addition is that you can set block and inline borders, respectively, in one declaration:
Physical declaration | Logical declaration |
---|---|
border-top: 1px solid red; border-bottom: 1px solid red; | border-block: 1px solid red; |
border-left: 1px solid red; border-right: 1px solid red; | border-inline: 1px solid red; |
Border radii
Converting border radius to logical requires thinking about the order of properties carefully. The format is border-{block-direction}-{inline-direction}-radius
:
Physical property | Logical property |
---|---|
border-top-left-radius | border-start-start-radius |
border-top-right-radius | border-start-end-radius |
border-bottom-right-radius | border-end-end-radius |
border-bottom-left-radius | border-end-start-radius |
Sizing
Here's the conversion table for sizing properties:
Physical property | Logical property |
---|---|
height | block-size |
min-height | min-block-size |
max-height | max-block-size |
width | inline-size |
min-width | min-inline-size |
max-width | max-inline-size |
Alignment
As you might have noticed, physical directions like left
and right
don't exist at all in logical property land. That's not only true for property names, but also for values. left
can mostly be converted to start
and right
to end
.
Physical declaration | Logical declaration |
---|---|
text-align: left; | text-align: start; |
text-align: right; | text-align: end; |
justify-content: left; | justify-content: start; |
justify-content: right; | justify-content: end; |
The justify-content
property accepts start
and end
as values, but also flex-start
and flex-end
. So what's the difference? The main difference is browser support. flex-{start|end}
has better support, but start|end
will replace the flex values eventually.
Positioning
For positioning, we're going to start working a lot with inset
in different shape and forms.
Physical property | Logical property |
---|---|
top | inset-block-start |
right | inset-inline-end |
bottom | inset-block-end |
left | inset-inline-start |
Shorthands
There are some useful shorthands as well!
Physical declaration | Logical declaration |
---|---|
top: 0 ;bottom: 0; | inset-block: 0; |
left: 0; right: 0; | inset-inline: 0; |
top: 0; right: 0; bottom: 0; left: 0; | inset: 0; |
There's also an inset
shorthand, that sets offsets in all directions. However, it defines physical offsets rather than logical.
Scroll margin and scroll padding
Here's the conversion table for scroll margin and scroll padding:
Physical property | Logical property |
---|---|
scroll-padding-top | scroll-padding-block-start |
scroll-padding-right | scroll-padding-inline-end |
scroll-padding-bottom | scroll-padding-block-bottom |
scroll-padding-left | scroll-padding-inline-start |
scroll-margin-top | scroll-margin-block-start |
scroll-margin-right | scroll-margin-inline-end |
scroll-margin-bottom | scroll-margin-block-bottom |
scroll-margin-left | scroll-margin-inline-start |
Shorthands
Here are some shorthands:
Physical declaration | Logical declaration |
---|---|
scroll-margin-top: 1px; scroll-margin-bottom: 1px; | scroll-margin-block: 1px; |
scroll-margin-left: 1px; scroll-margin-right: 1px; | scroll-margin-inline: 1px; |
scroll-padding-top: 1px; scroll-padding-bottom: 1px; | scroll-padding-block: 1px; |
scroll-padding-left: 1px; scroll-padding-right: 1px; | scroll-padding-inline: 1px; |
Browser support
Logical properties are not supported in Internet Explorer. But since it has reached end-of-life, we might not need to consider that. Other than that, browser support is generally great.
There are a few exceptions, though.
- Flow-relative
resize
values: https://caniuse.com/mdn-css_properties_resize_flow_relative_support - Flow-relative
overflow
propertiesoverflow-inline
andoverflow-block
: https://caniuse.com/?search=overflow-inline
Logical vs physical properties
It might be tempting to do a complete migration from physical to logicl properties, and perhaps even add some kind of linting rule that prevents physical properties from being used. However, physical properties are not deprecated in any sense. In some situations, it does not make sense to convert. Here's an example from the CSS specification:
Documents might need both logical and physical properties. For instance the drop shadows on buttons on a page must remain consistent throughout, so their offset will be chosen based on visual considerations and physical directions, and not vary by writing system.
It's important to remember that left is always left. If you say that something is placed on the left side of the screen, left
should be used rather than inset-inline-start
.
Stay pragmatic!
Logical units
We have been talking a lot about logical properties here, and a little bit about logical values. But what about units? Let me explain with an example declaration:
inline-size: 70vw;
Does it ring a bell? It probably should. In this declaration, the logical inline-size
property is used, but the declaration value 70vw
is physical. In other words, inline-size
can define the horizontal or vertical size depending on writing mode, while 70vw
is always calculated based on the horizontal size undependent on the writing mode. In practice, we probably want the value to be flow relative as well in this case.
The units vb
and vi
can make this happen! vb
is the percentage the viewport's block size, while vi
is the percentage of the viewport's inline size. So here's what you can do instead to make the value flow relative:
inline-size: 70vi;
Here it is in table format:
Physical unit | Logical unit |
---|---|
vh | vb |
vw | vi |
Conclusion
- Logical properties are awesome!
- Both logical and physical properties might be needed. Think about whether something should be relative to writing mode or not when you choose between logical and physical properties.
- Consider browser support before using logical properties.
References
- https://web.dev/learn/css/logical-properties/
- https://www.w3.org/TR/css-logical-1/
- https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties
- https://adactio.com/journal/19457
- https://adrianroselli.com/2019/11/css-logical-properties.html
- https://github.com/w3c/csswg-drafts/issues/1282
- https://www.smashingmagazine.com/2022/12/deploying-css-logical-properties-on-web-apps/