Reputation: 3819
There seems to be a lot of questions regarding the same issue but can't seem to find a satisfactory answer ... I have:
1 container (flex) (green)
2 columns (block) -> left: red, right: orange
in the left column, I have two divs (green) who follow each other 'menu1''menu2'
These two menus are themselves wrapped in a div (black) so that when I rotate it, the two menus are vertical rather than horizontal (rotation by 90 deg)
The goal is to have the top wrapper/container (green) to take on the height of the vertical black wrapper, and the left column wrapper to be no larger than the 'width' of the rotated black wrapper.
What I get is illustrated in the following example:
https://jsfiddle.net/pg373473/
<div id='container' style='display: flex; border: 3px solid green; flex-direction=row'>
<div id='leftbox' style='position: relative; display: block; border: 3px solid red'>
<div id='textwrapper' style='transform-origin: bottom left; transform: translateY(-100%) rotate(90deg); border: 2px solid black;'>
<div style='border: 3px solid green; display: inline-block'>menu 1</div>
<div style='border: 3px solid green; display: inline-block'>menu 2</div>
</div>
</div>
<div id='rightbox' style='position: relative; display: flex; flex: 1 1 auto; border: 3px solid orange';>
xx
</div>
</div>
Is this possible at all?
By default, the rotation seems to be applied after all the width/height for all divs have been calculated. I personally find this behavior to be against what anybody would expect, but maybe someone who knows the specs very well can explain why this is so? But in the end, I am more interested to know if there is a solution to what I try to achieve (thx).
Rotate elements in CSS that affect their parents... is asking something about rotations but the explanation is not satisfactory and the problem slightly different since it's only about being sure that the div of the rotated child take into account the height of the rotated child. The questions asked in this post has 3 constraints:
The aforementioned question is only asking about the height and does not solve the width problem. Additionally, the jsfiddle doesn't work either.
The only good part about this other question is the mentioned in the comments of the writing-mode
but I haven't managed to make it work with this either. I tried this option and while I can fix the height issue, I can't make it work to fix the width problem...
<div id='container' style='display: flex; border: 3px solid green; flex-direction: row'>
<div style='display: flex; flex-direction: column; border: 3px solid red; flex: 1 1 auto;'>
<div style='flex: 0 0 auto; display: inline-block; flex-direction: row; writing-mode: vertical-rl; border: 3px solid black;'>
<div style='flex: 0 0 auto; border: 1px solid green; display: inline-block'>menu 1</div>
<div style='flex: 0 0 auto; border: 1px solid green; display: inline-block'>menu 2</div>
</div>
</div>
<div id='rightbox' style='display: flex; flex: 1 1 auto; border: 3px solid orange';>
Right Box
</div>
</div>
https://jsfiddle.net/dyae4xru/
For clarity here is what I want:
There is no solution to this problem at this point in time. CSS/HTML/Browsers doesn't support that out of the box. I fixed the problem by writing a small JS function that gives me the exact width and height of the div when horizontal and used the values to set the width of the div once rotated by 90 degrees (using writing-mode: vertical-rl
).
Upvotes: 16
Views: 2579
Reputation: 42352
There may be hacky solutions, yet I'd say CSS transforms are not built for something like this.
If the property has a value different than none, a stacking context will be created. In that case the object will act as a containing block for position: fixed elements that it contains.
Source: MDN
(See this post for a scaling transform question.)
CSS Writing Mode
I suggest you use the newer CSS writing mode
- see here for browser support:
The writing-mode CSS property defines whether lines of text are laid out horizontally or vertically and the direction in which blocks progress.
Use writing-mode: vertical-lr
on the div - see demo below that works in Chrome:
#container {
display: flex;
border: 1px solid green;
flex-direction: row;
}
#container>div:first-child {
position: relative;
display: block;
border: 1px solid red;
writing-mode: vertical-lr; /* ADDED */
-webkit-writing-mode: vertical-lr;
}
#container>div>div {
/*transform-origin: bottom left;
transform: translateY(-100%) rotate(90deg);*/
border: 2px solid black;
}
#container>div>div>div {
border: 1px solid green;
display: inline-block;
}
#rightbox {
position: relative;
display: flex;
flex: 1 1 auto;
border: 1px solid orange;
}
<div id='container'>
<div>
<div>
<div>menu 1</div>
<div>menu 2</div>
</div>
</div>
<div id='rightbox'>
xx
</div>
</div>
Issue in Firefox
Note that this doesn't work in Firefox, as flex items do not behave well for vertical writing mode - there are open issues:
The above issue for vertical mode in flexboxes was fixed in Firefox 60, and now you can see that the above demo works in both Chrome & Firefox.
Upvotes: 5
Reputation: 396
The answer is in the question's edited answer for the writing-mode
solution actually. The only problem was the flex: 1 1 auto
style on the red-border div, removing it solves the width problem.
HTML w/ Style Tag Variant
<div id='container' style='display: flex; border: 3px solid green; flex-direction: row'>
<div style='display: flex; flex-direction: column; border: 3px solid red;'>
<div style='flex: 0 0 auto; display: inline-block; flex-direction: row; writing-mode: vertical-rl; border: 3px solid black;'>
<div style='flex: 0 0 auto; border: 1px solid green; display: inline-block'>
menu 1
</div>
<div style='flex: 0 0 auto; border: 1px solid green; display: inline-block'>
menu 2
</div>
</div>
</div>
<div id='rightbox' style='display: flex; flex: 1 1 auto; border: 3px solid orange';>
Right Box
</div>
</div>
CSS Classes Variant
#container
{
display: flex;
flex-direction: row;
border: 3px solid green;
}
#leftbox
{
display: flex;
flex-direction: column;
border: 3px solid red;
}
#inner-leftbox
{
display: inline-block;
flex-direction: row;
flex: 0 0 auto;
writing-mode: vertical-rl;
border: 3px solid black;
}
#rightbox
{
display: flex;
flex: 1 1 auto;
border: 3px solid orange;
}
.menu
{
display: inline-block;
flex: 0 0 auto;
border: 1px solid green;
}
<div id="container">
<div id="leftbox">
<div id="inner-leftbox">
<div class="menu">menu 1</div>
<div class="menu">menu 2</div>
</div>
</div>
<div id="rightbox">
Right Box
</div>
</div>
Upvotes: 1
Reputation: 4560
I doubt that this task can be solved without JavaScript because transformed elements are drawn on separate "layers" and their visual appearance doesn't affect DOM properties. However since your transformation is fixed - you can calculate resulted visual size of element and update size of parent element accordingly. Moreover you can use mutation observers to update size of your container in a case if its contents will be changed in runtime. This example displays correct container size and reacts on runtime mutations of menu items. Tested into Firefox and Chrome
document.addEventListener('DOMContentLoaded', function () {
const container = document.querySelector('#container .menu-container');
const menu = document.querySelector('#container .menu-items');
let items = [];
const updateItems = () => {
const nodes = document.querySelectorAll('#container .menu-item');
for (let node of nodes) {
if (items.indexOf(node) === -1) {
items.push(node);
new MutationObserver(updateSize).observe(node, {attributes: true, characterData: true, subtree: true});
}
}
updateSize();
}
const updateSize = () => {
container.style.width = menu.offsetHeight + 'px';
container.style.height = menu.offsetWidth + 'px';
}
new MutationObserver(updateSize).observe(menu, {attributes: true, characterData: true});
new MutationObserver(updateItems).observe(menu, {childList: true});
updateItems();
updateSize();
});
#container {
display: flex;
border: 1px solid green;
flex-direction: row;
}
.menu-container {
position: relative;
display: block;
border: 1px solid red;
}
.menu-items {
transform-origin: bottom left;
transform: translateY(-100%) rotate(90deg);
border: 2px solid black;
display: flex;
position: absolute;
}
.menu-item {
border: 1px solid green;
display: inline-block;
white-space: nowrap;
}
#rightbox {
position: relative;
display: flex;
flex: 1 1 auto;
border: 1px solid orange;
}
<div id="container">
<div class="menu-container">
<div class="menu-items">
<div class="menu-item">menu 1</div>
<div class="menu-item">menu 2</div>
</div>
</div>
<div id="rightbox">
xx
</div>
</div>
Upvotes: 3
Reputation: 1997
I modified your code and below is the out of CSS and HTML snippet.
*,
*:before,
*:after {box-sizing: border-box;margin: 0;padding: 0;}
.container {display: flex;border: 2px solid green;}
.left-box {flex: 0 2.5%;}
.right-box {flex: 1 auto;border: 2px solid orange;}
.left-box {flex: 0 5.5%;border: 2px solid red;}
.menu {border: 1.5px solid green;}
.inner-left-box ul {display: flex;flex-direction: column;border: 2px solid black;}
.inner-left-box ul li {list-style: none;writing-mode: vertical-lr;}
<div class="container">
<div class="left-box">
<div class="inner-left-box">
<ul>
<li class="menu">menu 1</li>
<li class="menu">menu 2</li>
</ul>
</div>
</div>
<div class="right-box">Right Box</div>
</div>
Upvotes: 1
Reputation: 13992
Here is my approach to the solution:
I use flex on the container as well as the LEFT BOX, then user writing-mode vertical left to right and finally flex direction column so they stack.
*,
*:before,
*:after {
/* So 100% means 100% */
box-sizing: border-box;
margin: 0;
padding: 0;
}
.container {
display: flex;
border: 2px solid tomato;
}
.box-left {
flex: 0 2.5%; // change % value for the size you want it!
}
.box-right {
flex: 1 auto;
border: 2px solid green;
}
.box-left ul {
display: flex;
flex-direction: column;
border: 2px solid blue;
}
.box-left ul li {
list-style: none;
writing-mode: vertical-lr;
margin-bottom: 0.5em;
}
<div class="container">
<div class="box-left">
<ul>
<li>menu01</li>
<li>menu02</li>
</ul>
</div>
<div class="box-right">
box right
</div>
</div>
Upvotes: 1