I am building a list of services for my website using CSS Grid. One of the rows in that overall website grid is broken into two CSS Grid columns.
In the first column of the first row, there is a description of a service. In the second column, there is an image that represents the service.
With each row, the description and image alternate, so on the second row, first column, there is an image, and in the second column, there is a description. Check out the attached image to see what I have working so far (note: I re-sized images to make it easier to take a screenshot).
The mobile version of the CSS Grid is a single column. When I display the same content in the mobile version, the layout no longer works. Since my layout is determined by the HTML content (probably a bad thing, I know), the titles will not always show up above the image, which is what I want. See attached to see the issue.
I believe the answer to solving this problem lies in using flex-direction: row-reverse; however, it is quite hard to come across some good examples (maybe I am just searching the wrong way). The best Codepen I could find does what I want using flex-direction, but it does not nicely place the description in one CSS Grid box and the image in another CSS Grid box, so when resizing the browser, the images overlap the text.. that is probably due to my lack of knowledge using Flexbox (still learning).
Could you help me figure out how to properly create an alternating 2-column list of items that also displays the text and image properly when in a 1-column list?
I would prefer to stay within the CSS Grid/Flexbox/no script world, but I am happy to entertain other ideas.
Thank you very much for any help you can provide!
HTML
<!-- Services area -->
<div class="services-area">
<div class="services-text">
<h3>This is service 1</h3>
</div>
<div class="services-div">
<img class="services-image" src="images/home/home-agile-transformation.png" alt="Agile transformation image.">
</div>
<div class="services-div">
<img class="services-image" src="images/home/home-agile-coaching.png" alt="Agile transformation image.">
</div>
<div class="services-text">
<h3>This is service 2</h3>
</div>
<div class="services-text">
<h3>This is service 3</h3>
</div>
<div class="services-div">
<img class="services-image" src="images/home/home-agile-sw-implementation.png" alt="Agile transformation image.">
</div>
</div>
CSS
// layout for services
// display a stacked grid <767 pixels
.services-area {
grid-area: svcs;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto;
#if $debug { background-color: $debugServicesArea; }
}
// display a 2-column grid >768
#include for-size(full-size) {
.services-area {
grid-area: svcs;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto;
margin-left: $gridMarginLeft;
margin-right: $gridMarginRight;
#if $debug { background-color: $debugServicesArea; }
}
}
.services-text {
display: grid;
align-items: center;
justify-content: center;
}
.services-image {
max-width: 100%;
height: auto;
}
For accessibility concerns and better SEO keep the markup in the logical order (title, image).
On mobile, you do not need a grid at all, use display:block for the container. Use grid-auto-flow: row dense to fill the grid as densely as possible following the row order. This ensure no grid cell will be empty.
Then alternate the title elements by specifying the column they need to start from. You can use the :nth-child() sibling selector to pick the titles, starting from the 3rd and then every 4 (4n - 1 means 4 * 0 - 1 = -1 (invalid sibling, skipping); 4 * 1 - 1 = 3; 4 * 2 - 1 = 7; ...).
/* display a stacked grid <767 pixels */
.services-area {
display: block;
}
/* display a 2-column grid >768 */
#media (min-width: 768px) {
.services-area {
display: grid;
grid-template-columns: 1fr 1fr;
align-items: center;
justify-content: center;
grid-auto-flow: row dense;
}
}
.services-area > :nth-child(4n - 1) {
grid-column-start: 2;
}
.services-image {
max-width: 100%;
height: auto;
}
This helped me:
For example, if you want to reverse the columns on screen lower than 1200px, then in your parent div, use this instead
#media (max-width:1200px) {
.services-area {
display: flex;
flex-flow: column-reverse;
}
}
Related
I am new to writing html and css code. I tried to make an image gallery with three column using CSS FlexBox
html:
<div class="row">
<div class="column">
<img src="sea.jpg">
<img src="guns.jpg">
</div>
<div class="column">
<img src="cars.jpg">
<img src="gg.png">
</div>
<div class="column">
<img src="games.jpg">
<img src="games.jpg">
</div>
</div>
css:
.row {
display: flex;
flex-wrap: wrap;
padding: 0 4px;
}
.column {
flex: 33.33%;
padding:10,10px;
}
.column img {
margin-top: 8px;
vertical-align: middle;
width: 100%;
}
The result of the code above is a normal three column image grid with pictures filled in each column. Is there any way to make the first image in the first row, first column span to first row, second column?
Pretty good for someone new to HTML/CSS! But you probably have never heard about CSS grid.
Flexbox is great for one-column or one-row organization, but lacks in multi-column/multi-row organization. That's where css grid comes in.
To make an object a grid, do
#container {
display: grid;
}
Then, we need to specify the amount of columns and rows. We can use the grid-template-rows and grid-template-columns properties for this. For every extra value you add, you add another row/column. For example, grid-template-columns: 20px 20px;' would generate two columns, each 20px wide. If you specify the width and height of the container, then you can set the value to auto (e.g. grid-template-columns: auto auto;, which creates two columns that are equal size and as wide as possible while still staying in the boundaries of the grid. The same applies for grid-template-rows. So lets assume, for now, you want a 100px by 100px 3x3 grid.
#container {
display: grid;
width: 100px;
height: 100px;
grid-template-rows: auto auto auto;
grid-template-columns: auto auto auto;
}
Great! Now we need to specify where we want the grid items to go. We use the grid-row and grid-column properties for this. Let's take a look at what it looks like.
#image-one {
grid-row: 1 / 2;
grid-column: 1 / 2;
}
So what do those numbers mean?
Well, when we created the 3x3 grid, the actually created 4 vertical lines and 4 horizontal lines. Draw out a 3x3 grid with borders if you are confused, you'll see 4 horizontal lines and 4 vertical lines.
So, the code is saying that the first image should be between horizontal grid lines 1 and 2 and between vertical grid lines 1 and 2. Going back to your drawing, this is the first box!
Now you can do this for every single image. Here's the second image:
#image-two {
grid-row: 1 / 2;
grid-column: 2 / 3;
}
This puts the image on the first row, second column. Remember, draw a picture if you get stuck! This is hard because we are describing visual instructions with text, so using a drawing to help translate is super helpful.
Also, the images do not have to take up a 1 by 1 space on the grid. For example, you could make a 2x2 image that starts at the very first box by doing...
#test-image {
grid-row: 1 / 3;
grid-column: 1 / 3;
}
...which puts the image between horizontal grid lines 1 and 3 and vertical grid lines 1 and 3, a.k.a. making a 2x2 box! Again, draw a diagram if you're confused.
Also, for further reading and helpful diagrams, check out https://css-tricks.com/snippets/css/complete-guide-grid/. They do a better job explaining than I ever could. Also, they have an excellent tutorial/review/documentation on flex-box, if you're curious: https://css-tricks.com/snippets/css/a-guide-to-flexbox/.
Good luck and happy coding! :)
This question already has answers here:
Is it possible for flex items to align tightly to the items above them?
(5 answers)
Closed 4 years ago.
I have 3 div elements which I want to put in 2-columns. One of them I want to fill in the first column vertically and the others to fill the next column in 2
separate rows. (Run code snippet to see the sections)
.news-feed {
display: flex;
flex-flow: row wrap;
}
.image {
flex-direction: column;
height: 20px;
}
.title_date {
flex-direction: column;
}
.job-description {
flex-direction: column;
}
<section class="news-feed">
<section class="image">
<img src= 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRlUiyrmhTXFppqk4aYzqTOU9nimCQsYibukwAV8rstsDkAVQT-mA'/>
</section>
<section class="title_date">
<p class="date">27 Dec 1997</p>
<h3 class="title">Urgent Job offer</h3>
</section>
<section class="job_description">
<p class="job_info">This is a job for speacial people like you. Long text bla bla bla. The main issue is that I cannot put the 'title_date' and 'job_description' section in a single div because I want to put the 'title_date' at top of 'image' section when the device is mobile. Any workaround idea is highly appreciated. Thanks</p>
<p class="tags">Jobs, HighPay, Carrer</p>
</section>
</section>
The main issue is that I cannot put the 'title_date' and 'job_description' section in a single div because I want to put the 'title_date' at top of 'image' section when the device is mobile. Any workaround idea is highly appreciated. Thanks
This is easily achieved if you just use grid not flex in your css files. Then you won't need any JavaScript. Just change your CSS code like this:
.news-feed {
display: grid;
grid-template-columns: 1fr 2fr;
grid-template-rows: 50% 50%;
grid-template-areas:
"image title_date"
"image job_description"
}
.image {
grid-area: image;
}
.title_date {
grid-area: title_date;
}
.job-description {
grid-area: job_description;
}
#media screen and (max-width: 425px) {
.news-feed {
grid-template-columns: 100%;
grid-template-rows: 1fr 1fr 1fr;
grid-template-areas:
"title_date"
"image"
"job_description"
}
}
This will position image on the left in desktop mode and in the middle on the mobile version. You can of course change the columns and rows height and width as you want.
wrap "title_date" and "job_description" in one section and give them flex-direction: column; and inside that wrapper div give flex-direction: row; css to this two div "title_date" and "job_description"
I had to go javascript way to resolve this.
For this, I look into the "window.innerheight" property and according to it, I either wrap the "title_date" and "job_description" inside a new section (for normal display) or separate them and give them required flexbox ordering (for mobile display).
Is there a way in Boostrap to make grid columns adjust according to the width of the viewport? I have the following page, where the first column is col-md-3 and the second col-md-9:
The right hand column is supposed to show a list of files when the user selects a folder in the left hand column. Yet the column widths stay fixed, and if I reduce the screen width just the tiny bit, the right-hand column is moved to below the left-hand column. The left hand column then fills the height and width of the screen, and the file list column is invisble below it, making for a very bad UX.
Is there some way, preferably in Bootstrap, to have the columns adjust their width in proportion to the screen width, or is there some alternative to Bootstrap with a grid that can work like that?
You can simply achieve this using css grid.
body{
margin:0;
}
.container{
display:grid;
grid-template: 100vh / repeat(12, 1fr);
color:#fff;
}
.container > div{
display: flex;
justify-content: center;
align-items:center;
font-size: 2em;
}
.left-panel{
grid-column: 1 / 4;
background: #F7A072;
}
.right-panel{
grid-column: 4 / -1;
background: #0FA3B1;
}
<body>
<div class="container">
<div class="left-panel">Left</div>
<div class="right-panel">Right</div>
</div>
</body>
A very common element containing an image, title and text is
<div class="mediaelement">
<img src="http://placekitten.com/g/500/500" />
<h1>The title. Unknown length.</h1>
<p>And the description. Which could also span multiple lines.</p>
</div>
Now display: flex appears to be a very flexible way to center and display items. E.g with the order property I could move the title before the image without touching dom.
But is it possible to align both the h1 and p vertically to the image without putting the into another container? Using only css?
Like this: http://imgur.com/draDl2q
If i put them into a container, I cannot move the h1 before the image with the flex order value.
Yes, it is....with one proviso. You need to know the height of the parent element.
Basically, you need column layout with wrapping. In order for the parent to know when to wrap you must have a limited height...or it just won't wrap.
.mediaelement {
display: inline-flex;
flex-direction: column;
flex-wrap: wrap;
height: 200px;
justify-content: center;
}
<div class="mediaelement">
<img src="http://placekitten.com/g/200/200" />
<h1>The title. Unknown length.</h1>
<p>And the description. Which could also span multiple lines.</p>
</div>
Once display:contents gains support this kind of fiddling about will become unnecessary as it will be possible to wrap and unwrap elements from their containers as required.
Here's Paulie_D's answer expanded to show the smaller issue with the widths of h1 and p. But this is already good enough solution for some cases.
.mediaelement {
display: inline-flex;
flex-direction: column;
flex-wrap: wrap;
height: 200px;
justify-content: center;
width: 600px;
}
<div class="mediaelement">
<img src="http://placekitten.com/g/200/200" style="
padding-right: 10px;
">
<h1 style="
width: calc(100% - 210px);
margin: 0;
">The title. Unknown length. Could be 2 lines</h1>
<p style="
width: calc(100% - 210px);
margin: 0;
">And the description. Which could also span multiple lines.And the description. Which could also span multiple lines.</p>
</div>
Answering my own question :)
With CSS grid, this is easier to solve.
https://codepen.io/Jaska/pen/VQGqWK
.mediaelement {
display: grid;
grid-template-columns: 1fr 1fr;
grid-column-gap: 1rem;
}
img {
grid-row: 1 / 5;
}
h1 {
grid-row: 2
}
p {
grid-row: 3;
}
So the img fills all the rows 1-4 so the h1 and p have to go to the second column. And I added h1 to start from row 2 and p to start from row 3 so there remains 2 equal height rows above and under the content.
I need to create a horizontal layout where one block takes all available space and the other ones shrink to fit their content.
For example:
<div class="grid">
<div class="expand">Long text label</div>
<div class="shrink">Button</div>
</div>
A more complex example with two rows (an actual grid):
<div class="grid">
<div class="row">
<div class="shrink">...</div>
<div class="expand">...</div>
<div class="shrink">...</div>
<div class="shrink">...</div>
</div>
<div class="row">
<div class="shrink">...</div>
<div class="expand">...</div>
<div class="shrink">...</div>
<div class="shrink">...</div>
</div>
</div>
My requirements:
The large block should fill all available space even if short
The small blocks should fit their content
The large block (usually a text label) may be a single word larger that the available space, so it should be truncated in this case
The large block should not wrap if multi-word
The small blocks should not wrap (though in the case of multiple buttons or icons, this can be solved by making one block per component)
Support multiple rows (i.e. columns should be aligned)
I am targeting Android and iOS smartphones.
I have tried to adapt the code from this answer but I could not make it work for multiple rows. Also, the source code must be out of order, which is confusing (though not blocking for my use case). Here's a jsfiddle: http://jsfiddle.net/k3W8L/
You need to use grid-template-column and set the size of column you want to shrink-to-fit as auto, and specify the size of at least one other column using the fr unit.
Example:
To recreate the sidebar-content layout, where the sidebar is collapsible,
-------------------------------
| Sidebar | Content |
-------------------------------
you can create the grid as:
.grid {
display: grid;
...
grid-template-columns: auto minmax(0, 1fr); // see note below
grid-template-areas: "sidebar content";
}
.sidebar {
grid-area: sidebar;
}
.content {
grid-area: content;
}
See the codepen here for a demo & code: https://codepen.io/khs/pen/vegPBL
You can click on the navbar to see auto-sizing in action.
Note: One thing I've learnt since writing this answer is that in most cases, and especially in a fixed-height/fixed-width layout, you should use minmax(0, 1fr) instead of 1fr. The reason is that 1fr is actually a shorthand for minmax(auto, 1fr). This breaks when used for fixed layouts with longer content. See this question for more details: Why does minmax(0, 1fr) work for long elements while 1fr doesn't?
Hence I've updated my answer to reflect the fact. The minmax might be a little verbose, but it's almost always what you want for this kind of layout.
Context: I bumped into this question recently while learning to use grid in my app. With some help from ilyaigpetrov's answer, I was able to get a shrink-to-fit column sizing to work. Their answer doesn't give much explanation though, so I thought I'd add this:
I found the answer to my own question while trying to create a concise example.
It makes use of table layout:
.grid {
display: table;
table-layout: auto;
width: 100%;
}
.row {
display: table-row;
}
.expand {
display: table-cell;
width: 100%;
max-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.shrink {
display: table-cell;
white-space: nowrap;
}
The width: 100% in .expand is what makes the block fill all available space, thus fulfilling the first two requirements.
Note that the width in .grid simply sets the width of the entire grid, you can use any value here.
For some reason, putting max-width: 0 in .expand prevents the block to grow more than the available space, a smallish value like 100px will also do. I discovered this by chance and I don't know why it works.
Here's the jsfiddle: http://jsfiddle.net/fr253/
This answer helped me getting started.
I don't know whether my code is valid, but it gets the job done: either with grid or with flex.
.container {
display: grid;
grid-template-columns: 1fr auto auto;
}
.one {
background-color: pink;
}
.two {
background-color: yellow;
}
.three {
background-color: lightgreen;
}
/* Now the same but with flex. */
.whole-row {
grid-column: 1 / -1;
}
.flexy {
display: flex;
border: 1px solid red;
}
.flexy > .one {
flex-grow: 1;
}
.flexy > .two,
.flexy > .three {
/* flex-grow: 0; <- This value is a default. */
}
<div class="container">
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three</div>
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three</div>
<div class="whole-row flexy">
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three</div>
</div>
</div>