I have a layout which has a maximum width and which is centred horizontally when the screen width is greater than that maximum. The layout includes a fixed header & menu; when the screen width is less than the max, the menu's left
position is 0
and, when the screen width exceeds the max, the menu's left
position needs to be flush with the left edge of the rest of the layout.
Here's how it should look:
*{box-sizing:border-box;margin:0;padding:0;}
header{
align-items:center;
background:#eee;
border-bottom:1px solid #999;
display:flex;
height:100px;
left:0;
justify-content:center;
padding:0 10px;
position:fixed;
right:0;
top:0;
}
h1{
height:60px;
position:relative;
width:calc(100% - 40px);
max-width:740px;
z-index:2;
}
img{
height:100%;
width:auto;
}
nav{
background:#eee;
border-right:1px solid #999;
bottom:0;
left:0;
position:fixed;
top:0;
width:300px;
z-index:1;
}
@media (min-width:801px){
nav{
border-left:1px solid #999;
left:calc((100% - 800px) / 2)
}
}
nav::before{background:#ecc;bottom:0;content:"";left:0;position:absolute;top:0;width:10px;}
nav::after{background:#999;content:"";height:1px;left:0;position:absolute;right:0;top:99px;}
main{background:#ddf;border-left:1px solid #99c;border-right:1px solid #99c;height:100vh;min-height:100%;margin:0 auto;width:100%;max-width:800px;}
<header>
<h1><img src="http://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a"></h1>
<nav></nav>
</header>
<main></main>
However, when a vertical scrollbar is introduced, a problem arises due to the fact that the scrollbar width is included in the width being checked for in the media query, resulting in a negative left
position for the menu when the screen width is between 800px and 800-x
px (where x
is the width of the scrollbar). You can see this in the below Snippet (you'll probably need to view it full screen) by resizing your browser to slightly less than 800px - the right border of the menu gets a few pixels closer to the logo and the red edge of the menu is cropped.
*{box-sizing:border-box;margin:0;padding:0;}
html,body{height:101%;}
header{
align-items:center;
background:#eee;
border-bottom:1px solid #999;
display:flex;
height:100px;
left:0;
justify-content:center;
padding:0 10px;
position:fixed;
right:0;
top:0;
}
h1{
height:60px;
position:relative;
width:calc(100% - 40px);
max-width:740px;
z-index:2;
}
img{
height:100%;
width:auto;
}
nav{
background:#eee;
border-right:1px solid #999;
bottom:0;
left:0;
position:fixed;
top:0;
width:300px;
z-index:1;
}
@media (min-width:801px){
nav{
border-left:1px solid #999;
left:calc((100% - 800px) / 2)
}
}
nav::before{background:#ecc;bottom:0;content:"";left:0;position:absolute;top:0;width:10px;}
nav::after{background:#999;content:"";height:1px;left:0;position:absolute;right:0;top:99px;}
main{background:#ddf;border-left:1px solid #99c;border-right:1px solid #99c;height:100vh;min-height:100%;margin:0 auto;width:100%;max-width:800px;}
<header>
<h1><img src="http://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a"></h1>
<nav></nav>
</header>
<main></main>
I understand what's happening and why it's happening but my question is: is there any way, using CSS alone, to prevent it from happening? I've tried using viewport units instead of percentages but that creates the problem in reverse; at certain screen widths, the logo moves a bit further over to the left, away from the menu's right border. If all browsers had identical scrollbar widths or allowed for custom styling of scrollbars, it would be easy to get around this but, unfortunately, neither is the case.
08/09/16: I've accepted my own answer for now as it was the best JavaScript solution I could come up with but I'm still on the hunt for a CSS solution.
ClientWidth defines the width of an element. So scrollbar can be defined as width = offsetWidth – clientWidth.
Many assume that width: 100vw is the same as width: 100% . This is true on a page that doesn't scroll vertically, but what you might not realize is that the viewport actually includes the scrollbars, too.
We can use the CSS “::-webkit-scrollbar” property which is responsible for changing the shape, color, size, shade, shadow, etc. of the scroll bar. But, here the property which we will use is the direction property of CSS for changing the position of the scroll bar.
The default scrollbar width can range anywhere from 12px to 17px. Webkit browsers also have the ability for the user to configure scrollbar widths. Webkit browsers, such as Chrome can have custom scrollbars that can have any size scrollbar.
EDIT
/* Alexander Gomez's getScrollBarWidth shorten version Josh Bambrick from:
http://stackoverflow.com/questions/986937/how-can-i-get-the-browsers-scrollbar-sizes
*/
function getScrollBarWidth() {
var $outer = $('<div>').css({
visibility: 'hidden',
width: 100,
overflow: 'scroll'
}).appendTo('body'),
widthWithScroll = $('<div>').css({
width: '100%'
}).appendTo($outer).outerWidth();
$outer.remove();
return 100 - widthWithScroll;
};
/* Tested on Chrome 51.0.2704, Firefox 40.0.2, Internet Explorer 11 */
$(document).ready(function() {
var mydelta = 800; /* your 'main' divs width */
var delta_mq = mydelta + 1;
delta = mydelta - getScrollBarWidth();
var h1_logo = delta - 40;
var $appended = '<style>h1{max-width:' + h1_logo + 'px} main{max-width:' + mydelta + 'px} @media (min-width:' + delta_mq + 'px){main{left:calc((100% - ' + delta + 'px) / 2);} nav{border-left:1px solid #999;left:calc((100% - ' + delta + 'px) / 2); } }';
$('body').append($appended);
});
* {
box-sizing: border-box;
margin: 0;
padding: 0
}
/*
'*' selector is depreciated cause it's slow
use normalize.css instead
And i removed your css media and appended them to the
page using jquery after getting scrollbar width
*/
header {
align-items: center;
background: #eee;
border-bottom: 1px solid #999;
display: flex;
height: 100px;
left: 0;
justify-content: center;
padding: 0 10px;
position: fixed;
right: 0;
top: 0;
}
h1 {
height: 60px;
position: relative;
width: calc(100% - 40px);
max-width: 750px;
z-index: 2;
}
img {
height: 100%;
width: auto;
}
nav {
background: #eee;
border-right: 1px solid #999;
bottom: 0;
left: 0;
position: fixed;
top: 0;
width: 300px;
z-index: 1;
}
main { /* position with 100px top margin */
position: absolute;
top: 100px;
}
header { /* chrome fix */
z-index: 1;
}
nav::before{background:#ecc;bottom:0;content:"";left:0;position:absolute;top:0;width:10px;}
nav::after{background:#999;content:"";height:1px;left:0;position:absolute;right:0;top:99px;}
main{background:#ddf;border-left:1px solid #99c;border-right:1px solid #99c;height:100vh;min-height:100%;margin:0 auto;width:100%;max-width:800px;}
<header>
<h1 class=logo>
<img src="http://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.png?v=9c558ec15d8a">
</h1>
<nav></nav>
</header>
<main></main>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With