Border inside Grid Layout - html

I have a CSS grid that represents the tic-tac-toe game. I wanted to put an border only inside the grid. Today, I proceed in this way:
:root {
--border: 2px dashed #393939;
--symbol-color: #FF7F5B;
}
.grid {
height: 100%;
display: grid;
grid-template-columns: repeat(3, calc(100%/3));
grid-template-rows: repeat(3, calc(100%/3));
}
.child {
display: flex;
align-items: center;
align-content: center;
color: var(--symbol-color);
font-size: 2.5rem;
}
.child:nth-child(1),
.child:nth-child(2),
.child:nth-child(3) {
border-bottom: var(--border);
}
.child:nth-child(7),
.child:nth-child(8),
.child:nth-child(9) {
border-top: var(--border);
}
.child:nth-child(1),
.child:nth-child(4),
.child:nth-child(7) {
border-right: var(--border);
}
.child:nth-child(3),
.child:nth-child(6),
.child:nth-child(9) {
border-left: var(--border);
}
<div class="grid">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
Result:
This solution works but I find it unattractive. Do you have an idea to refactor this solution?

Since you want a stylized border (dashed, in this case), then your approach and the approach taken in the other answers appears to be useful.
However, if you decide to use a simple, solid line border, then the approach can be simplified. Just use the background color of the grid for border color, and the grid-gap property for border width.
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
background-color: black;
grid-gap: 1px;
height: 100vh;
}
.child {
background-color: white;
display: flex;
align-items: center;
justify-content: center;
color: #FF7F5B;
font-size: 2.5rem;
}
body { margin: 0;}
<div class="grid">
<div class="child"></div>
<div class="child"></div>
<div class="child">X</div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child">O</div>
<div class="child"></div>
</div>

One thing you can use the nth-child selector in a better way like below instead of targeting one by one.
.child:nth-child(-n+3) {
border-bottom: var(--border);
}
.child:nth-child(3n+1) {
border-right: var(--border);
}
.child:nth-child(3n) {
border-left: var(--border);
}
.child:nth-child(n+7) {
border-top: var(--border);
}
:root {
--border: 2px dashed #393939;
--symbol-color: #FF7F5B;
}
.grid {
height: 100%;
display: grid;
grid-template-columns: repeat(3, calc(100%/3));
grid-template-rows: repeat(3, calc(100%/3));
}
.child {
display: flex;
align-items: center;
align-content: center;
color: var(--symbol-color);
font-size: 2.5rem;
}
.child:nth-child(-n+3) {
border-bottom: var(--border);
}
.child:nth-child(3n+1) {
border-right: var(--border);
}
.child:nth-child(3n) {
border-left: var(--border);
}
.child:nth-child(n+7) {
border-top: var(--border);
}
<div class="grid">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
<div class="child">6</div>
<div class="child">7</div>
<div class="child">8</div>
<div class="child">9</div>
</div>

You may consider this workaround.
You may use grid-template-columns to do the trick.
create a parent container that will hold your four images.
set a background color (desire color of the border).
set the padding to 0
then do the trick arrange the images by grid-template-column: auto
auto;
then add gap to them grid-gap: 10px; (to show the background color of
the container as grid).
please see code below for reference
.container {
width: 200px;
display: grid;
grid-template-columns: auto auto;
grid-gap: 10px;
background-color: #000;
padding: 0;
}
.container > div {
background-color: #ccc;
padding: 20px;
text-align: center;
}
html
<div class="container">
<div>Image here</div>
<div>Image Here</div>
<div>Image here</div>
<div>Image here</div>
</div>
to help you visualize i create a sample code
http://plnkr.co/edit/gIeumXLt0k3FPVCgGlDd?p=preview
Hope it helps
Cheers!

You can reduce number of nth-child selector here from this answer.
body {
margin: 0;
}
:root {
--border: 2px dashed #393939;
--symbol-color: #FF7F5B;
}
.grid {
height: 100vh;
display: grid;
grid-template-columns: repeat(3, calc(100%/3));
grid-template-rows: repeat(3, calc(100%/3));
}
.child {
display: flex;
align-items: center;
justify-content: center;
color: var(--symbol-color);
font-size: 2.5rem;
}
.child:not(:nth-child(3n)) {
border-right: var(--border);
}
.child:not(:nth-last-child(-n + 3)) {
border-bottom: var(--border);
}
<div class="grid">
<div class="child"></div>
<div class="child"></div>
<div class="child">x</div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child">o</div>
<div class="child"></div>
</div>

Try to make use of negative margin in .child class with overflow:hidden in parent .grid class here...No need to use nth-child selector here...
:root {
--border: 2px dashed #393939;
--symbol-color: #FF7F5B;
}
.grid {
height: 100%;
display: grid;
grid-template-columns: repeat(3, calc(100%/3));
grid-template-rows: repeat(3, calc(100%/3));
overflow: hidden;
}
.child {
height: 100px;
display: flex;
align-items: center;
align-content: center;
color: var(--symbol-color);
font-size: 2.5rem;
border-bottom: var(--border);
border-left: var(--border);
margin-left: -2px;
margin-bottom: -2px;
}
<div class="grid">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>

What about using background and linear-gradient:
body {
margin: 0;
}
.grid {
--b: #393939 0px, #393939 5px, transparent 5px, transparent 8px;
height: 100vh;
display: grid;
grid-template-columns: repeat(3, calc(100% / 3));
grid-auto-rows:calc(100% / 3);
background:
repeating-linear-gradient(to right ,var(--b)) 0 calc(100% / 3),
repeating-linear-gradient(to bottom,var(--b)) calc(2 * (100% / 3)) 0,
repeating-linear-gradient(to right ,var(--b)) 0 calc(2 * (100% / 3)),
repeating-linear-gradient(to bottom,var(--b)) calc(100% / 3) 0;
background-size:100% 2px,2px 100%;
background-repeat: no-repeat;
}
.child {
display: flex;
align-items: center;
justify-content: center;
}
<div class="grid">
<div class="child">A</div>
<div class="child">B</div>
<div class="child">C</div>
<div class="child">D</div>
<div class="child">E</div>
<div class="child">F</div>
<div class="child">G</div>
<div class="child">H</div>
<div class="child">I</div>
</div>

Related

Div under grid in flexbox

I have a layout that is a sidebar and a grid both wrapped in a flexbox. I'd like to put a div underneath the grid so it can have prev/next buttons, like in this image, but I can't figure out how to do that. The grid resizes itself with the window so the grid can take as many rows as necessary and then the div should go below that, and be as wide as the grid.
This is what I have, but the div is on the right of the grid:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Boardgame Database</title>
<style>
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
aside {
background-color: red;
flex: 1;
min-width: 250px;
}
.grid-container {
flex: 4;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.grid-item {
border: 1px solid black;
padding: 20px;
font-size: 24px;
text-align: center;
overflow: hidden;
}
#flex-container {
display: flex;
flex-direction: row;
min-height: 100vh;
}
</style>
</head>
<body>
<div id="flex-container">
<aside class="sidebar">
</aside>
<section class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
<div class="grid-item">10</div>
<div class="grid-item">11</div>
<div class="grid-item">12</div>
<div class="grid-item">13</div>
<div class="grid-item">14</div>
<div class="grid-item">15</div>
<div class="grid-item">16</div>
<div class="grid-item">17</div>
<div class="grid-item">18</div>
</section>
<div id="page-buttons">
prev
next
</div>
</div>
Checkout the following Code.
#main{
display :flex;
}
#sidebar{
width:70px;
height: 300px;
border: solid black 1px;
}
#grid-area{
width:200px;
height: 300px;
border: solid black 1px;
display: block;
}
#grid{
width:200px;
height: 250px;
border: solid black 1px;
display: block;
}
<div id="main">
<div id="sidebar"></div>
<div id="grid-area">
<div id="grid"></div>
<div id="button">next / prev</div>
</div>
</div>
You should use nested flex containers. Section and bottom div should be wrapped inside another flex container with flex direction to column.
So outer flex will make sidebar & inner flex container to be side by side.
Or just use a normal div container instead of flex.
here is another example only with grid keeping the pre/next button at the bottom of the viewport:
body {
margin: 0;
}
#grid-container {
display: grid;
height: 100vh;
grid-template-columns: minmax(250px, 1fr) 4fr;
grid-template-rows: 1fr auto;
}
aside {
background-color: red;
border: 1px solid;
margin: 0.25em;
grid-row: span 2;
grid-column: 1;
}
section,
#page-buttons {
grid-column: 2;
border: solid 1px;
margin: 0.25em;
}
section {
overflow: auto;
}
#page-buttons {
display: flex;
gap: 1em;
padding: 0.5em;
background: lightgray;
justify-content: center;
}
.grid-item {
border: 1px solid black;
padding: 20px;
font-size: 24px;
text-align: center;
overflow: hidden;
}
<div id="grid-container">
<aside class="sidebar">
</aside>
<section class="grid-container">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-item">5</div>
<div class="grid-item">6</div>
<div class="grid-item">7</div>
<div class="grid-item">8</div>
<div class="grid-item">9</div>
<div class="grid-item">10</div>
<div class="grid-item">11</div>
<div class="grid-item">12</div>
<div class="grid-item">13</div>
<div class="grid-item">14</div>
<div class="grid-item">15</div>
<div class="grid-item">16</div>
<div class="grid-item">17</div>
<div class="grid-item">18</div>
</section>
<div id="page-buttons">
prev
next
</div>
</div>

How to make CSS grid that stretches children and respects min-height: 0 on them?

I have simple css grid. I'd like it to stretch children to fill available content but also respect it if any of the children has height: 0. Right now it also "reserves" space for the child with height: 0.
Here's the fiddle showing my problem:
https://jsfiddle.net/f3r0b5e9/7/
.wrapper {
height: 300px;
width: 200px;
border: 1px solid red;
display: grid;
align-content: stretch;
grid-template-rows: auto;
}
.child {
width: 100%;
border: 1px solid blue;
background: #cad5e8;
}
.child.one {
height: 0;
min-height: 0;
max-height: 0;
}
<div class="wrapper">
<div class="child one">
</div>
<div class="child two">
</div>
<div class="child three">
</div>
</div>
Here's what I'm trying to accomplish:
http://prntscr.com/kqcry4
Note: I know how to do this with flexbox:)
Thanks!
Instead of auto, use min-content or max-content for the grid-template-rows's value:
.wrapper {
height: 300px;
width: 200px;
border: 1px solid red;
display: grid;
align-content: stretch;
grid-template-rows: min-content;
}
.child {
width: 100%;
border: 1px solid blue;
background: #cad5e8;
}
.child.one {
height: 0;
min-height: 0;
max-height: 0;
}
<div class="wrapper">
<div class="child one">
</div>
<div class="child two">
</div>
<div class="child three">
</div>
</div>

Flex-wrap property not responsive when parent has a set width

In the example below, when I set a width for a wrapper, the parent flex container can no longer use the flex-wrap property. The top two boxes won't wrap, but the bottom ones will.
<div class="wrapper">
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
</div>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
.wrapper {
width: 700px;
margin: 0 auto;
border: solid cadetblue 5px;
}
.parent {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.child {
height: 250px;
min-width: 250px;
max-width: 300px;
flex: 1;
background: mistyrose;
border: solid goldenrod 2px;
margin: 30px;
}
The 'issue' you raise is by design; you're specifying a width for the parent that is wide enough for your children to be wholly contained within (a 700px container for two 300px children). flex-wrap only causes elements to overflow when there's not enough space for the container to hold them. In your example, there is.
To force an overflow responsively, you could either specify a narrow width on the parent(which will cause an overflow for all viewports):
.wrapper {
width: 400px;
margin: 0 auto;
border: solid cadetblue 5px;
}
.parent {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.child {
height: 250px;
min-width: 250px;
max-width: 300px;
flex: 1;
background: mistyrose;
border: solid goldenrod 2px;
margin: 30px;
}
<div class="wrapper">
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
</div>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
Or use max-width instead(which will only overflow on narrow viewports):
.wrapper {
max-width: 700px;
margin: 0 auto;
border: solid cadetblue 5px;
}
.parent {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.child {
height: 250px;
min-width: 250px;
max-width: 300px;
flex: 1;
background: mistyrose;
border: solid goldenrod 2px;
margin: 30px;
}
<div class="wrapper">
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
</div>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>

How can I make this responsive grid layout?

What I thought would be a simple layout turned out to be something I'm not able to accomplish yet.
I need the grid to be responsive and spread from one side to another (it has to be aligned with the text and box above).
I tried a few options, I commented them out for easier checking:
.big-container {
padding: 0 20%;
}
.header {
width: 100%;
height: 50px;
background-color: blue;
}
.container {
border: 1px solid black;
/* display: flex;
flex-wrap: wrap;
*/
/* display: flex;
flex-wrap: wrap;
justify-content: space-between;
*/
}
.box {
border: 1px solid black;
width: 100px;
height: 30px;
background-color: yellow;
/* float: left;
margin-left: 10px; */
}
<div class="big-container">
<div class="header"></div>
<p>Text</p>
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</div>
I tried with flex but it doesn't sort nicely the last row, flex-wrap: wrap and float:left with some margin doesn't align correctly to the left.
If you use flexbox and set justify-content: space-between then last row will also have the same spacing instead of left align. Instead you can use grid-layout for this.
.big-container {
padding: 0 20%;
}
.header {
width: 100%;
height: 50px;
background-color: blue;
}
.container {
border: 1px solid black;
display: grid;
justify-content: space-between;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
grid-gap: 10px;
}
.box {
border: 1px solid black;
height: 30px;
background-color: yellow;
}
<div class="big-container">
<div class="header"></div>
<p>Text</p>
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</div>

Flexbox: 4 items per row

I'm using a flex box to display 8 items that will dynamically resize with my page. How do I force it to split the items into two rows? (4 per row)?
Here is a relevant snip:
(Or if you prefer jsfiddle - http://jsfiddle.net/vivmaha/oq6prk1p/2/)
.parent-wrapper {
height: 100%;
width: 100%;
border: 1px solid black;
}
.parent {
display: flex;
font-size: 0;
flex-wrap: wrap;
margin: -10px 0 0 -10px;
}
.child {
display: inline-block;
background: blue;
margin: 10px 0 0 10px;
flex-grow: 1;
height: 100px;
}
<body>
<div class="parent-wrapper">
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
</div>
</body>
You've got flex-wrap: wrap on the container. That's good, because it overrides the default value, which is nowrap (source). This is the reason items don't wrap to form a grid in some cases.
In this case, the main problem is flex-grow: 1 on the flex items.
The flex-grow property doesn't actually size flex items. Its task is to distribute free space in the container (source). So no matter how small the screen size, each item will receive a proportional part of the free space on the line.
More specifically, there are eight flex items in your container. With flex-grow: 1, each one receives 1/8 of the free space on the line. Since there's no content in your items, they can shrink to zero width and will never wrap.
The solution is to define a width on the items. Try this:
.parent {
display: flex;
flex-wrap: wrap;
}
.child {
flex: 1 0 21%; /* explanation below */
margin: 5px;
height: 100px;
background-color: blue;
}
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
With flex-grow: 1 defined in the flex shorthand, there's no need for flex-basis to be 25%, which would actually result in three items per row due to the margins.
Since flex-grow will consume free space on the row, flex-basis only needs to be large enough to enforce a wrap. In this case, with flex-basis: 21%, there's plenty of space for the margins, but never enough space for a fifth item.
Add a width to the .child elements. I personally would use percentages on the margin-left if you want to have it always 4 per row.
DEMO
.child {
display: inline-block;
background: blue;
margin: 10px 0 0 2%;
flex-grow: 1;
height: 100px;
width: calc(100% * (1/4) - 10px - 1px);
}
Here is another apporach.
You can accomplish it in this way too:
.parent{
display: flex;
flex-wrap: wrap;
}
.child{
width: 25%;
box-sizing: border-box;
}
Sample:
https://codepen.io/capynet/pen/WOPBBm
And a more complete sample:
https://codepen.io/capynet/pen/JyYaba
I would do it like this using negative margins and calc for the gutters:
.parent {
display: flex;
flex-wrap: wrap;
margin-top: -10px;
margin-left: -10px;
}
.child {
width: calc(25% - 10px);
margin-left: 10px;
margin-top: 10px;
}
Demo: https://jsfiddle.net/9j2rvom4/
Alternative CSS Grid Method:
.parent {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px;
}
Demo: https://jsfiddle.net/jc2utfs3/
For more detail you can follow this Link
.parent{
display: flex;
flex-wrap: wrap;
}
.parent .child{
flex: 1 1 25%;
/*Start Run Code Snippet output CSS*/
padding: 5px;
box-sizing: border-box;
text-align: center;
border: 1px solid #000;
/*End Run Code Snippet output CSS*/
}
<div class="parent">
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
<div class="child">5</div>
<div class="child">6</div>
<div class="child">7</div>
<div class="child">8</div>
</div>
I believe this example is more barebones and easier to understand then #dowomenfart.
.child {
display: inline-block;
margin: 0 1em;
flex-grow: 1;
width: calc(25% - 2em);
}
This accomplishes the same width calculations while cutting straight to the meat. The math is way easier and em is the new standard due to its scalability and mobile-friendliness.
.parent-wrapper {
height: 100%;
width: 100%;
border: 1px solid black;
}
.parent {
display: flex;
font-size: 0;
flex-wrap: wrap;
margin-right: -10px;
margin-bottom: -10px;
}
.child {
background: blue;
height: 100px;
flex-grow: 1;
flex-shrink: 0;
flex-basis: calc(25% - 10px);
}
.child:nth-child(even) {
margin: 0 10px 10px 10px;
background-color: lime;
}
.child:nth-child(odd) {
background-color: orange;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
</style>
</head>
<body>
<div class="parent-wrapper">
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
</div>
</body>
</html>
;)
Flex wrap + negative margin
Why flex vs. display: inline-block?
Flex gives more flexibility with elements sizing
Built-in white spacing collapsing (see 3 inline-block divs with exactly 33% width not fitting in parent)
Why negative margin?
Either you use SCSS or CSS-in-JS for the edge cases (i.e. first element in column), or you set a default margin and get rid of the outer margin later.
Implementation
https://codepen.io/zurfyx/pen/BaBWpja
<div class="outerContainer">
<div class="container">
<div class="elementContainer">
<div class="element">
</div>
</div>
...
</div>
</div>
:root {
--columns: 2;
--betweenColumns: 20px; /* This value is doubled when no margin collapsing */
}
.outerContainer {
overflow: hidden; /* Hide the negative margin */
}
.container {
background-color: grey;
display: flex;
flex-wrap: wrap;
margin: calc(-1 * var(--betweenColumns));
}
.elementContainer {
display: flex; /* To prevent margin collapsing */
width: calc(1/var(--columns) * 100% - 2 * var(--betweenColumns));
margin: var(--betweenColumns);
}
.element {
display: flex;
border: 1px solid red;
background-color: yellow;
width: 100%;
height: 42px;
}
you can try this
.parent-wrapper {
height:100%;
width:100%;
border: 1px solid black;
}
.parent {
display: grid;
font-size: 0;
grid-template-columns: 25% 25% 25% 25%;
}
.child {
background:blue;
flex-grow: 1;
height:100px;
margin: 10px;
margin-bottom: 0;
}
.child:last-child {
margin-bottom: 10px;
}
<body>
<div class="parent-wrapper">
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
</div>
</body>
https://jsfiddle.net/samet19/gdntwLhb/
Here's another way without using calc().
// 4 PER ROW
// 100 divided by 4 is 25. Let's use 21% for width, and the remainder 4% for left & right margins...
.child {
margin: 0 2% 0 2%;
width: 21%;
}
// 3 PER ROW
// 100 divided by 3 is 33.3333... Let's use 30% for width, and remaining 3.3333% for sides (hint: 3.3333 / 2 = 1.66666)
.child {
margin: 0 1.66666% 0 1.66666%;
width: 30%;
}
// and so on!
That's all there is to it. You can get fancy with the dimensions to get a more aesthetic sizes but this is the idea.