I am trying to come up with a solution for advanced layout. I decided to use a css grid as it seemed to be the best match for my needs.
The requirements:
Header - consists of three elements - logo, search bar and menu
search bar should be aligned with the main content if space allows, i.e. on small screen search bar doesn't need to start where the main content starts but it should end where main content ends or take all available space
Main - consists of content and sidebar- should be centred and take max 100rem width space
content should be 2 times bigger than the sidebar
main content should have at least 1rem space from left and right
This is how it looks now. It matches my requirements on big screens (4k) but when the screen gets smaller it gets messy. I would really like to avoid any javascript and solve this with pure CSS if possible.
How would you approach this problem? I am now more inclining that this is not solvable with pure CSS and JS is needed. (Probably some resize observer on main-content element)
Examples:
Big screen -> alignment is correct ✔️
Small screen -> alignment is not correct. ❌ The search box should be within the black "brackets"
Even smaller screen -> alignment is not correct too. ❌ The search box should expand up to the black line
html,
body {
padding: 0;
margin: 0;
}
body {
font-family: sans-serif;
display: grid;
grid-template-columns: minmax(1rem, 1fr) minmax(min-content, 100rem) minmax(
1rem,
1fr
);
grid-template-rows: auto 1fr auto;
grid-template-areas:
"header header header"
". main .";
min-height: 100vh;
}
.header {
padding: 1rem 1rem;
background: green;
grid-area: header;
display: grid;
grid-template-columns: minmax(max-content, 1fr) minmax(min-content, 100rem) minmax(
max-content,
1fr
);
}
.search-box {
background: yellow;
max-width: calc(2/3 * 100%);
}
.logo {
background: yellowgreen;
min-width: 5rem;
}
.menu {
background: brown;
}
.main {
grid-area: main;
display: flex;
}
.main-content {
background: red;
flex: 2;
}
.sidebar {
background: blue;
flex: 1;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<header class="header">
<div class="logo">Logo</div>
<div class="search-box">
Search box should should match main content position
</div>
<div class="menu">Menu with two submenus at least</div>
</header>
<main class="main">
<section class="main-content">Main content</section>
<aside class="sidebar">Sidebar</aside>
</main>
</body>
</html>
Sandbox available here
Related
I'm currently making a NextJS app which should have a fullscreen layout that does not change regardless of zoom level. At default zoom level, you see the header, footer, toolbar, and vertically-scrollable content window
When you zoom out, the size of the header, footer, toolbar, and content window remain fixed even if the content inside them changes size
Note that there are multiple types of toolbars and content windows that may be shown depending on what the user selects. I've been able to get a header and footer working by editing _app.js, but the toolbar and content window are not taking up the full remaining space if I zoom out because there is some extra div that is injected by NextJS at some point. I'm using inline styling with material UI.
Has anyone run into this issue before?
This is one approach you could use for your layout, using CSS Grid. I'm defining the grid to have 12 columns, but you could set it up in any way you'd like.
Note, the following block means, start from the 3rd grid line and span to the end of the grid. This will satisfy your horizontal space requirements.
main .main-content {
grid-column: 3 / -1;
}
For the center column taking up the maximum space of the viewport, I'm using the following:
grid-template-rows: auto 1fr auto;
The first auto is the header, the 1fr is the middle section (most room), and the last auto is the footer.
Here's a demo.
.grid {
display: grid;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
min-height: 100dvh;
gap: 0.5rem;
padding: 0.5rem;
}
main {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 0.5rem;
}
main aside {
grid-column: 1 / 3;
}
main .main-content {
grid-column: 3 / -1;
}
header, main aside, .main-content, footer {
background-color: #eee;
}
header,
footer,
main :where(aside, .main-content) { padding: 0.5rem;}
html, body { padding: 0; margin: 0; }
*, *::before, *::after {
box-sizing: border-box;
}
<div class="grid">
<header>header</header>
<main>
<aside>aside</aside>
<div class="main-content">main content</div>
</main>
<footer>footer</footer>
</div>
Is it possible to set a css grid row height based on if the grid area is being used?
.container {
height: 100vh;
grid-template-areas:
"mobile-head mobile-head mobile-head"
"nav app app"
"footer footer footer";
}
.nav {
grid-area: nav
}
<div class="container">
<div class="nav">Nav stuff</div>
</div>
Given the above grid template and html, I see the Nav stuff in the nav area, but naturally the mobile nav area is taking a 1/3rd of the screen height. Because there is no div that has a grid-area: mobile-head I want the height to be 0px.
But if I change the .nav to
...
.nav {
grid-area: mobile-head
}
I would want the row to be a fixed height of say 200px.
I can get close. I can get the row to have a height of 0 when there is nothing in the mobile-area by doing this:
...
grid-template-rows: minmax(0px, auto) 1fr 1fr
But then the height is dictated by the content, rather than the grid.
I'm fairly sure the only alternative to achieve what im trying to do is to use 2 grids, and media queries. (I want to place the nav on the left for desktop, or top if mobile)
I think grid-auto-rows: min-content is what you're after.
<button class="toggle" style="margin-bottom:1em">Toggle hidden row</button>
<div class="container">
<div class="nav">Nav stuff</div>
</div>
<style>
.container {
box-shadow: inset 0 0 0 1px lightgray;
background-color: #fafafb;
display: grid;
height: 100vh;
grid-auto-rows: min-content;
grid-template-areas:
"mobile-head mobile-head mobile-head"
"nav app app"
"footer footer footer";
}
.nav {
grid-area: nav;
background-color: lightblue;
}
.nav.mobile {
grid-area: mobile-head;
}
</style>
<script>
document.querySelector('.toggle').onclick = function(){
document.querySelector('.nav').classList.toggle('mobile');
};
</script>
I'm trying to take a single image and centre it using CSS grid, both as a way to teach myself grid and because my page calls for a single, centred image. The image is centring left-to-right but not top-to-bottom. It sits right at the top. I made the image smaller to make viewing it for test purposes easier. The size of my image is less important than its placement. What am I doing wrong?
Also, I want this in a 3x3 grid, not some other number value.
.wrapper {
display: grid;
display: flex;
justify-content: center;
}
.ConstructGrid {
justify-items: center;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
grid-gap: 1px;
grid-template-areas: ". . ." ". picture ." ". . .";
}
<div class="wrapper">
<div class="ConstructGrid">
<img class="picture" src="http://www.cafenocturne.com/images/Under_Construction.png" border="0" width="400">
</div>
</div>
It's easier than you think.
the container should be the grid, within you can set the child in its area/cell.
an height or min-height is also required even if 1fr would manage some height
body {
margin:0;
}
.wrapper {/* i draw the grid , i'm the boss !*/
height: 100vh;/* let me lay from top to bottom. I'm the boss, i want it whole */
display: grid;
grid-template-rows: repeat(3, 1fr);
grid-template-columns: repeat(3, 1fr);
grid-gap: 1px;
grid-template-areas: ". . ." ". picture ." ". . .";
}
.ConstructGrid {
grid-area: picture;/* tell me where to be, i'm the prisoner */
margin:auto;/* let me be in the center of my area/cell :) */
}
<div class="wrapper">
<div class="ConstructGrid">
<img class="picture" src="http://www.cafenocturne.com/images/Under_Construction.png" border="0" width="400">
</div>
</div>
You didn't use the grid. Because you didn't set display:grid for .ConstructGrid. You can use only flexbox to center the image. Also you should set height: 100% to center it vertically.
html,
body,
.wrapper,
.ConstructGrid {
height: 100%;
}
.ConstructGrid {
display: flex; /* this can be display: grid also */
justify-content: center;
align-items: center;
}
<div class="wrapper">
<div class="ConstructGrid">
<img class="picture" src="http://www.cafenocturne.com/images/Under_Construction.png" border="0" width="400">
</div>
</div>
You should't have both display grid and display flex at the same block of code, otherwise, it will overwritten by the other.
In your code you have display:flex just below of display grid.
Display grid will apply when you remove display flex within .wrapper.
I would solve it like that to reserve the centered Area for the image. I use .picture-container as the wrap-element for the Image and use responsive widths and heights to get it centered right. Flex is only used inside the Wrap-Element for the Image so it doesnt interfere with the Css-Grid:
body{
height: 100vh;
width: 100vw;
padding: 0;
margin: 0;
}
.wrapper {
height: 100%;
width: 100%;
display: grid;
justify-items: center;
grid-template-rows: auto;
grid-template-columns: 25% 25% 25% 25%;
grid-template-areas:
". . . ."
" . image image ."
". . . .";
}
.picture-container{
grid-area: image;
display: flex;
align-items: center;
justify-content: center;
}
<div class="wrapper">
<div class="picture-container">
<img class="picture" src="http://www.cafenocturne.com/images/Under_Construction.png" border="0">
</div>
</div>
I also always prefer to set a height and width for your whole Area in cases like this, thats why i added them to the body. That makes it way easier to position Elements.
This is something that I've been struggling with for a while, but I can't seem to find a way to do it.
If you have an odd number of items in grid and you want 2 items per row (1fr 1fr), you end up with a single item in the last row that is left-centered.
I just want to make it centered so it looks nicer.
Here's a picture too.
You can try something like this jsfiddle:
/* visibility properties */
body {
width: 60%;
margin: 5% auto;
}
div {
margin: 3%;
width: 100px;
height: 100px;
background-color: black;
justify-self: center;
}
div:nth-of-type(2n) {
background-color: red;
}
/* actual code: */
section {
display: grid;
grid-template-columns: 1fr 1fr;
}
#last-div {
grid-column: 1 / span 2;
}
<section>
<div>
</div>
<div>
</div>
<div>
</div>
<div>
</div>
<div id="last-div">
</div>
</section>
Get more info on CSS Grid: complete-guide-grid
You could try something like this since I faced a similar issue in one of my earlier projects.
grid-template-columns : repeat(auto-fit, minmax(<minSize>, 1fr));
Set minSize to whatever minimum width you want an element to occupy.
I am trying to achieve the below layout without using media queries. The reason I'd prefer to not use media queries is that this content is embedded on a site with a sidebar on the left that can expand and collapse. When it's expanded, it takes up about 400px, and it takes up maybe 50 when it's collapsed. It expands and collapses based off of a user interaction, not screen-width, so there's no good way for me to detect if it's open or not from CSS. Because of this, there could be some errors in my layout if I use a media query based on the screen width, instead of the actual width of the content. Is there any way I can use something like flex-basis or minmax to achieve this layout without media queries?
/* functional code */
.a1 {
grid-area: a1;
}
.a2 {
grid-area: a2;
}
.b1 {
grid-area: b1;
}
.grid {
display: grid;
grid-template-rows: 400px 200px 200px;
grid-template-columns: 1fr 300px;
grid-template-areas:
"a1 b1"
"a2 b1"
"a2 .";
}
/* Can I emulate this on the container width instead of the screen width? */
#media (max-width:1000px) {
.grid {
grid-template-columns: 1fr;
grid-template-areas: "a1"
"b1"
"a2";
grid-template-rows: 400px 600px 200px;
}
.b1 {
width:300px;
justify-self:center;
}
}
/* other styling */
.grid {
gap:8px;
}
.grid * {
border-radius: 5px;
background-color: lightgray;
display: grid;
place-items: center;
text-align: center;
}
.collapsed { display: none; }
#media (max-width:1000px) {
.uncollapsed {
display: none;
}
.collapsed {
display: block;
}
}
<div class="uncollapsed">Uncollapsed State (make screen smaller to see collapsed state)</div>
<div class="collapsed">Collapsed State (make screen bigger to see uncollapsed state)</div>
<div class="grid">
<div class="a1">
A1
<br>
Top-left normally
<br>
Top when collapsed
</div>
<div class="a2">
A2
<br>
Bottom-left normally
<br>
Bottom when collapsed
</div>
<div class="b1">
B1
<br>
Right side normally
<br>
Middle row when collapsed
</div>
</div>
FYI: I ended up just using a ResizeObserver polyfill and some JavaScript. Definitely not ideal, but it seems like container queries are currently impossible without JS.