I'd like to have a bottom border on my title that breaks the grid. There are 3 columns, left, middle, and right, and my title and content go in the middle. I'd like to have a bottom border extend all the way to the left side of the left column. Is there a better way to do this than what I came up with below? Does CSS grid provide a built-in mechanism for doing this sort of thing? I'd appreciate any suggestions.
.grid-container {
padding: 10px;
display: grid;
grid-template-columns: [start] 1fr [middle] 4fr [end] 1fr ;
grid-gap: 1rem;
background-color: #fff;
color: #444;
}
.start {
grid-column: start;
background-color: #ccc;
}
.middle {
grid-column: middle;
}
.title {
}
.title:after {
content: '';
display: block;
border-bottom: 1px solid red;
margin-left: calc(-25% - 1rem);
}
.end {
grid-column: end;
background-color: #ccc;
}
<div class="grid-container">
<div class="start">Start</div>
<div class="middle">
<h2 class="title">My Title</h2>
<div class="content">Some text lorem ipsum, whatever.</div>
</div>
<div class="end">End</div>
</div>
This is a modification of Michael_B's answer above. Seems to work the same but without the addition of markup to the original html.
.grid-container {
padding: 10px;
display: grid;
grid-template-columns: [start] 1fr [middle] 4fr [end] 1fr ;
grid-template-rows: repeat(100, 1px); /* new */
grid-column-gap: 1rem; /* adjusted; removed grid-row-gap */
background-color: #fff;
color: #444;
}
.start {
grid-column: start;
background-color: #ccc;
grid-row: 1 / -1; /* new */
}
.middle {
grid-column: middle;
grid-row: 1 / -1; /* new */
}
.end {
grid-column: end;
background-color: #ccc;
grid-row: 1 / -1; /* new */
}
.grid-container:after { /* new */
content: '';
background-color: red;
grid-row-start: 47;
grid-column: 1 / 3;
}
<div class="grid-container">
<div class="start">Start</div>
<div class="middle">
<h2 class="title">My Title</h2>
<div class="content">Some text lorem ipsum, whatever.</div>
</div>
<div class="end">End</div>
</div>
Your negative margin method seems to work. It's a bit clunky, but seems effective nonetheless.
You could also use absolute positioning to align that border under the title. The container would be the bounding box (i.e., position: relative). But that's also a bit clunky.
If you want a pure grid solution, that's also possible. The method I came up with is clean and effective. Some may consider it overkill for a simple border, but your need is somewhat unique, so this may be useful.
Here's the basic concept:
Divide your container into 100 tiny rows (1px).
Make your existing grid items span these rows from top to bottom.
Now you're back to your original layout, but with an upgrade under the hood.
You now have 100 places for a border line.
Create a new grid item (either DOM or pseudo-element).
Span the item across the row under your title.
VoilĂ ! :-)
.grid-container {
padding: 10px;
display: grid;
grid-template-columns: [start] 1fr [middle] 4fr [end] 1fr ;
grid-template-rows: repeat(100, 1px); /* new */
grid-column-gap: 1rem; /* adjusted; removed grid-row-gap */
background-color: #fff;
color: #444;
}
.start {
grid-column: start;
background-color: #ccc;
grid-row: 1 / -1; /* new */
}
.middle {
grid-column: middle;
grid-row: 1 / -1; /* new */
}
.end {
grid-column: end;
background-color: #ccc;
grid-row: 1 / -1; /* new */
}
.grid-container::after { /* new */
content: "";
background-color: red;
grid-row-start: 47;
grid-column: 1 / 3;
}
<div class="grid-container">
<div class="start">Start</div>
<div class="middle">
<h2 class="title">My Title</h2>
<div class="content">Some text lorem ipsum, whatever.</div>
</div>
<div class="end">End</div>
<div class="border"></div>
</div>
jsFiddle demo
Another method, based on the same idea as Micheael_B had, "you need more rows".
What you can do is split the middle part in 3 parts. Title, border, content.
You can give each their own grid-row in order.
See fiddle that only works in Firefox because instead of unwrapping the 3 tags I used the display property contents
https://jsfiddle.net/npddveag/2/
The display property contents is a magic way of making the wrapper div disappear without the actual markup disappearing.(and inherited styles will stil apply, so there is a bit more to it).
If you need it to work in other browsers you can just unwrap the 3 tags.
Related
I'm trying to make my page responsive. At a certain point my text is overflowing into another grid cell cause there is not enough space. How can I put these cells into a "new" row so the text has space and my other cells are fully visible?
Layout
Do I need to add another row into my parent-grid? What is the easiest way to fix this? Best case would be almost no media queries.
Thanks.
Here's an example by 'adding' another row below the image and text. I suspect you probably don't need the fifth box and it can easily be removed from the code. This is not the responsive part (as we presume you already have that built-in elsewhere in your CSS. Your actual setup may be a 'page grid' versus this example (grid-container), so posting some of your code would help us to visualize a better solution.
.grid-container {
display: grid;
grid-template-rows: repeat(3, auto);
grid-template-columns: repeat(3, auto);
background-color: #2196F3;
column-gap: 5px;
row-gap: 5px;
padding: 5px;
font-family: arial, san-serif;
color: #000;
}
.grid-item {
/*--background-color: rgba(255, 255, 255, 0.5); alternative color for all sections--*/
border: 2px solid rgba(0, 0, 0, 0.5);
padding: 20px;
font-size: 1.5em;
text-align: left;
}
.item1 {
grid-column: 1 / span 1;
background-color: lime;
}
.item2 {
grid-column: 2 / span 2;
background-color: yellow;
}
.item3 {
grid-column: 1 / 2;
background-color: blue;
}
.item4 {
grid-column: 2 / 2;
background-color: orange;
}
.item5 {
grid-column: 3 / 3;
background-color: red;
}
<div class="grid-container">
<div class="grid-item item1">1 image here</div>
<div class="grid-item item2">2 lots of text here <br>more text <br>more text <br>more text </div>
<div class="grid-item item3">3 link</div>
<div class="grid-item item4">4 link</div>
<div class="grid-item item5">5 link</div>
</div>
I haven't quite got my head around grid-template-areas, I suspect. Or maybe I do, but it is the behaviour of direct Grid descendant nodes that do not have a grid-area assigned.
Here is visually what I am trying to achieve:
Codepen is here: https://codepen.io/davewallace/pen/abYxWxE
Similar code example:
html, body , .container {
height: 100%;
margin: 0;
}
.container {
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: 1fr max-content;
gap: 40px 40px;
grid-template-areas:
"header header"
". graphic";
}
.header { grid-area: header; }
.graphic { grid-area: graphic; }
/* For presentation only, no need to copy the code below */
.container * {
border: 1px solid red;
position: relative;
}
<div class="container">
<div class="header">Full-width title</div>
<div class="graphic">Graphic</div>
<h4>Random element here</h4>
<p>Random element here</p>
<p>Random element here</p>
</div>
I seem to be able to only achieve two things currently:
Everything is in the right place, but all "generic content items" overlap on top of each other, where instead I want them stacked.
The title is in the right place, the image is sort of in the right place, but columns and/or rows are added automatically and do things like wrap under the image
The MDN docs for grid-template-area haven't really cleared the mix of behaviours up for me. I also tried using https://grid.layoutit.com/ to visualise the layout, and that seemed to get me off to a good start, but the default behaviour of the generic nodes has me confused.
You can approximate it like below:
body {
margin: 0;
}
.container {
min-height: 100vh;
display: grid;
grid-template-columns: 1fr max-content;
grid-auto-flow: dense;
gap: 0 40px; /* set only column gap */
}
.header {
grid-row: 1;
grid-column: 1/-1;
}
.graphic {
grid-column: 2;
grid-row: 2/ span 100; /* this will do the trick */
}
.container *:not(.graphic,:last-child) {
margin-bottom: 40px; /* replace the row gap */
}
/* For presentation only, no need to copy the code below */
.container * {
border: 1px solid red;
position: relative;
margin: 0;
}
<div class="container">
<div class="header">Full-width title</div>
<div class="graphic">Some Graphic here</div>
<h4>Random element here</h4>
<p>Random element here</p>
<p>Random element here</p>
</div>
I was trying to solve your CSS problem but it looks like there is no straightforward solution to it. According to the CSS spec, You cannot span a grid item to occupy all the rows/columns in an implicit grid.
Refer to this answer for more info - span grid item to all rows/columns
But I've come to a solution that could fix your grid by introducing a parent container for the random elements. In that way, you will only need two rows and two columns.
.container {
display: grid;
grid-template-columns: 1fr max-content;
gap: 40px 40px;
grid-template-areas: "header header" ". graphic";
}
.header {
grid-area: header;
}
.graphic {
grid-area: graphic;
}
/* Just to highlight */
.container * {
border: 1px solid;
}
<div class="container">
<div class="header">Full-width title</div>
<div class="graphic">Graphic</div>
<div class="random">
<h4>Random element here1</h4>
<p>Random element here2</p>
<p>Random element here3</p>
<p>Random element here4</p>
<p>Random element here4</p>
<p>Random element here4</p>
</div>
</div>
Is this layout remotely possible in IE11 (very crude example)?
$(document).on('click', '.js-toggle-hide', function(e){
e.preventDefault();
$(this).parent().parent().find('.js-hide').toggle();
});
#charset "UTF-8";
.c-sidebar {
display: grid;
grid-template-columns: 120px auto;
outline: 1px solid #ccc;
overflow: auto;
}
.c-cat {
display: grid;
grid-template-columns: 120px auto;
grid-template-rows: auto;
}
.c-cat__name {
grid-row: 1 / 1000;
}
.c-cat__toggle {
grid-row: 1 / 999;
min-width: 120px;
}
.c-cat__subcat {
grid-column: 3 / 4;
min-width: 120px;
}
.c-cat__subcat--all {
grid-column: 2 / 4;
}
/* decoration */
body {
padding: 15px;
background: #eee;
font-size: 11px;
}
.c-cell {
background: #fff;
outline: 1px solid #ccc;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="c-sidebar">
<div class="c-cell">All Categories</div>
<div>
<div class="c-cat">
<div class="c-cat__name c-cell">Sales</div>
<div class="c-cat__toggle c-cell">
All items
</div>
<div class="c-cat__subcat c-cell js-hide">Export sales</div>
<div class="c-cat__subcat c-cell js-hide">Other sales</div>
<div class="c-cat__subcat c-cell js-hide">Product sales</div>
<div class="c-cat__subcat--all c-cell js-hide"><b>All items</b></div>
</div>
</div>
</div>
Note that the number of items in the right columns is undefined (types of sales), client can add/remove them.
Are there any css tricks I'm not aware of?
The alternative is to use tables with complex js/jquery code (this is just a part of the code but it represents my dilemma perfectly) and that would be very tedious work.
My recommendation is if you want to be grid modern and still support something like IE11....then start with in display: flex and then do a media support to add your grid like this:
#supports (display: grid) {
#_your css grid here _#
}
So then you can add all your grid there....you are not necessarily repeating if you do this you are just doing the grid part here(grid-template, columns, rows, etc) and bypass any other styling like color, font, px and etc...
I have an article and an aside (sidebar) element - easy, except the title and sub-heading of the article need to span the entire row. If I take the title/sub-heading out of the article, the article element is no longer semantically complete.
Is there a way, using CSS Grid, to have the format below, where Title, Sub and Content are all a part of an "Article" element, and "Aside" is the second in a 2 column grid?
From my research so far, it seems this is not possible.
You can hack your way through using nested CSS grid if you know:
The width of the aside section
The height of the title and sub heading sections
(in many layouts, these dimensions are fixed)
You can use a pseudo element that create a space for the aside element and then sneak it inside the outer grid container - check out the demo below:
body {
margin: 0;
}
* {
box-sizing: border-box;
}
article,
aside {
border: 1px solid;
display: flex;
align-items: center;
justify-content: center;
}
div {
display: grid;
grid-template-areas: "section aside";
}
section {
grid-area: section;
display: grid;
grid-template-areas: "header header" "subhead subhead" "content empty";
grid-template-rows: 50px 50px auto;
grid-template-columns: 80vw auto;
height: 100vh;
width: 100vw;
}
section article:first-child {
grid-area: header;
}
section article:nth-child(2) {
grid-area: subhead;
}
section article:last-child {
grid-area: content;
}
section:after {
content: '';
display: block;
grid-area: empty;
}
aside {
grid-area: aside;
height: calc(100vh - 100px);
width: 20vw;
align-self: flex-end;
position:relative;
transform: translateX(-100%);
}
<div>
<section>
<article>Article title</article>
<article>Article sub-heading</article>
<article>Article content</article>
</section>
<aside>Aside</aside>
</div>
You can use something like this.
* {box-sizing: border-box;}
.wrapper {
max-width: 940px;
margin: 0 auto;
}
.wrapper > div {
border: 2px solid rgb(233,171,88);
border-radius: 5px;
background-color: rgba(233,171,88,.5);
padding: 10px;
color: #d9480f;
}.wrapper {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 10px;
grid-auto-rows: minmax(100px, auto);
}
.one {
grid-column: 1 / 4;
grid-row: 1;
}
.two {
grid-column: 1 / 4;
grid-row: 2;
}
.three {
grid-column: 1 / 3;
grid-row: 3;
min-height:200px;
}
.four {
grid-column: 3;
grid-row: 3;
min-height:200px;
}
<div class="wrapper">
<div class="one">One</div>
<div class="two">Two</div>
<div class="three">Three</div>
<div class="four">Four</div>
</div>
Also check Fiddle.
And for more details please visit https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout
You can achieve that by simply floating all the cells, as long as the article doesn't float - https://jsfiddle.net/yxbckzcq/1/
<div class="wrapper">
<article>
<div style="float:left;width:100%" class="one">One</div>
<div style="float:left;width:100%" class="two">Two</div>
<div style="float:left;width:70%" class="three">Three</div>
</article>
<div style="float:left;width:30%" class="four">Four</div>
</div>
I'm experimenting with CSS Grids, and this is the layout I'm building:
.grid {
display: grid;
align-items: center;
grid-template-columns: 1fr 4rem 1fr;
grid-template-rows: 1rem 1fr 1rem;
max-width: 900px;
margin: 0 auto;
}
.text {
/*
// Ideally, this should be
grid-area: text
*/
grid-column: 1 / 3;
grid-row: 2 / 3;
/* Fix z-index */
position: relative;
padding: 4rem;
background-color: #fff;
}
.image {
/*
// Ideally, this should be
grid-area: image;
*/
grid-column: 2 / 4;
grid-row: 1 / -1;
background-color: lightgray;
padding: 1rem;
/* Center das image */
display: flex;
justify-content: center;
}
/* Basic body */
body {
background-color: #fafafa;
font-family: sans-serif;
padding: 2rem;
}
img {
max-width: 100%;
height: auto;
}
<div class="grid">
<div class="text">One morning, when bobby woke from troubled dreams, he found himself transformed in his bed into a horrible vermin. He lay on his leg like back, and if he lifted his head a little he could see his brown belly, slightly domed and divided by arches into
stiff sections.
</div>
<div class="image">
<img src="http://unsplash.it/400/400" />
</div>
</div>
(best to preview in full page...)
What I'd like to avoid:
.text and .image both currently are using grid-column: * / *; syntax, instead I'd like to use grid-area: text and grid-area: image;.
Is it possible to define grid-template-{columns|rows} as overlapping areas? I tried using second way of defining grid areas
, but that didn't seem to work.
Looks like you can't do [a-start] [b-start] [a-end] [b-end] in that syntax, or at least I didn't manage to.
So - Is there any way to create an overlapping grid using named areas?
I'm trying to use the named areas for convenience purely - so that it's easier to reason about the responsive layout code, instead of repeating myself multiple times in media queries.
Edit
Found the answer because of #vals answer below.
This seemed to work just fine, I probably made a syntax error in my previous attempt somewhere:
grid-template-columns: [text-start] 1fr [image-start] 4rem [text-end] 1fr [image-end];
grid-template-rows: [image-start] 1rem [text-start] 1fr [text-end] 1rem [image-end];
At least in a more basic layout, it seems to work for me:
.container {
border: solid 1px green;
height: 180px;
width: 300px;
display: grid;
grid-template-columns: [left-start] 100px [right-start] 100px [left-end] 100px [right-end];
grid-template-rows: [left-start] 60px [right-start] 60px [left-end] 60px [right-end];
}
.left {
grid-area: left;
background-color: red;
}
.right {
grid-area: right;
background-color: lightgray;
opacity: 0.5;
}
<div class="container">
<div class="left">
</div>
<div class="right">
</div>
</div>