Reputation: 21
console.log('hello!')
.item {
display: flex;
flex-wrap: wrap;
}
.items {
border: 1px solid red;
width: 200px;
padding: 10px;
}
<body>
<h1>Hello there!</h1>
<div class="item">
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
</div>
</body>
Here I need to set red border for my cards but for this borders width is 2px but I need 1 px for all borders. How can I fix it?
Upvotes: 2
Views: 226
Reputation: 253506
There are a number of problems with the obvious attempts; here I enforce a gap
on the elements in the layout, in order to take the first, most-obvious approach, that of separating the adjacent borders. This prevents those elements from being placed immediately beside one another, so avoiding the the problem of the visual 'double-width' border
effect:
*,
::before,
::after {
box-sizing: border-box;
font: 1rem / 1.5 Roboto, Montserrat, sans-serif;
margin: 0;
padding: 0;
}
section {
width: clamp(50em, 70vw, 1200px);
margin-block: 2em;
margin-inline: auto;
}
.item {
display: flex;
flex-wrap: wrap;
margin-block: 1em;
}
.item.withGap {
gap: 1em;
}
.items {
border: 1px solid red;
width: 200px;
padding: 10px;
}
.item.noGap .items+.items {
border-inline-start-width: 0;
}
.setColumns {
--columns: 5;
--gap: 1em;
--flexBasis: calc((100%/var(--columns) - var(--gap)));
gap: var(--gap);
}
.setColumns .items {
flex-basis: var(--flexBasis);
}
<section>
<div class="item withGap">
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
</div>
</section>
This, of course, is reduces the the way in which you can lay out your elements; to later reduce, or remove, the gap means you have to reconsider the border
of the elements being laid out.
The other obvious option is to remove borders on adjacent items:
*,
::before,
::after {
box-sizing: border-box;
font: 1rem / 1.5 Roboto, Montserrat, sans-serif;
margin: 0;
padding: 0;
}
section {
width: clamp(50em, 70vw, 1200px);
margin-block: 2em;
margin-inline: auto;
}
.item {
display: flex;
flex-wrap: wrap;
margin-block: 1em;
}
.item.withGap {
gap: 1em;
}
.items {
border: 1px solid red;
width: 200px;
padding: 10px;
}
.item.noGap .items+.items {
border-inline-start-width: 0;
}
.setColumns {
--columns: 5;
--gap: 1em;
--flexBasis: calc((100%/var(--columns) - var(--gap)));
gap: var(--gap);
}
.setColumns .items {
flex-basis: var(--flexBasis);
}
<section>
<div class="item noGap">
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
</div>
</section>
This can work quite well for the first row of the layout, but if – or when – your layout runs to a second row it means that the first element of the new row is lacking its border-inline-start
, and all elements of the second row retain their border-block-start
because there's no way (as of writing) to select elements according to the row in which they're laid out, unfortunately.
Another obvious – and somewhat complicated – way is to use JavaScript, of course:
;
const getRows = (haystack, n) => {
let temp = [...haystack],
result = [];
if (n === Infinity) {
return haystack;
}
while (temp.length > 0) {
result.push(
temp.splice(0, n)
);
}
return result;
},
unitsInPixels = (val) => {
let cssVals = cssValue(val),
{
number,
units
} = cssVals,
div = document.createElement('div');
div.style.width = `1${units}`;
div.style.height = `1${units}`;
div.style.margin = 0;
div.style.padding = 0;
div.style.boxSizing = 'border-box';
div.style.position = 'absolute';
div.style.left = '1000%'
document.body.appendChild(div);
let measurements = div.getBoundingClientRect(),
{
width,
height
} = measurements;
div.remove();
return Math.floor(number * Math.min(width, height));
},
cssValue = (value, opts = {}) => {
let settings = {
toInt: false,
toSensibleMinimum: false,
};
Object.keys(opts).forEach(
(key) => settings[key] = opts[key]
);
let n = settings.toInt === true ? parseInt(value, 10) : parseFloat(value),
unit = value.replace(/^(\d+)/, '');
n = settings.toSensibleMinimum === true && n === 0 ? ++n : n;
return {
originalValue: value,
number: n,
units: unit,
numberWithUnits: `${n + (unit ? unit : '')}`,
}
},
styleCellsOf = (targets) => {
if (!targets) {
return false;
}
targets.forEach(
(el) => {
const children = el.children,
details = window.getComputedStyle(el, null),
columns = cssValue(details.getPropertyValue('--columns'), {
toSensibleMinimum: true
}).number,
gap = cssValue(details.getPropertyValue('--gap')),
matrix = getRows(children, columns);
if (unitsInPixels(gap.numberWithUnits) > 0) {
return false;
}
matrix.forEach(
(rowN, rowCounter) => {
rowN.forEach(
(cell, colCounter) => {
if (rowCounter > 0) {
cell.style.borderBlockStartWidth = 0;
}
if (colCounter > 0) {
cell.style.borderInlineStartWidth = 0;
}
});
});
});
};
styleCellsOf([...document.querySelectorAll('.setColumns')]);
window.addEventListener('resize', (e) => {
styleCellsOf([...document.querySelectorAll('.setColumns')])
});
*,
::before,
::after {
box-sizing: border-box;
font: 1rem / 1.5 Roboto, Montserrat, sans-serif;
margin: 0;
padding: 0;
}
section {
width: clamp(50em, 70vw, 1200px);
margin-block: 2em;
margin-inline: auto;
}
.item {
display: flex;
flex-wrap: wrap;
margin-block: 1em;
}
.item.withGap {
gap: 1em;
}
.items {
border: 1px solid red;
width: 200px;
padding: 10px;
}
.item.noGap .items+.items {
border-inline-start-width: 0;
}
.setColumns {
--columns: 5;
--gap: 1em;
--flexBasis: calc((100%/var(--columns) - var(--gap)));
gap: var(--gap);
}
.setColumns .items {
flex-basis: var(--flexBasis);
}
<section>
<div class="item setColumns" style="--columns: 4; --gap: 1em">
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
</div>
<div class="item setColumns" style="--columns: 5; --gap: 0.05em">
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
</div>
<div class="item setColumns" style="--columns: 3; --gap: 0.03em">
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
</div>
<div class="item setColumns" style="--columns: 3; --gap: 0em">
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
<div class="items">
<h4>Card 1</h4>
<span>Card content</span>
</div>
</div>
</section>
But this is potentially expensive, and needs to be explicitly connected to the HTML elements. This also comes with caveats, if you look at the second JavaScript flex-layout (in the demo just above), you may notice a tiny break in the apparently-flowing borders on the block-axis due to the way in which browsers, and monitors, handle sub-pixel rendering. So this approach is, also, imperfect although it does let you use CSS custom properties to style the elements.
It also reduces the purpose of using flex-layout, since it requires an explicitly-set number of columns (this could, of course, be worked around but to do so would seem to make the semi-simple JavaScript rather more complex, unfortunately).
So, again, while it mostly works in the event of a --gap
custom property being set to 0
, small deviations between, as an example (in my case) between 0em
and 0.03em
leads to either apparent-doubling of adjacent borders, or slight gaps between borders.
The second and third show gaps of 0.05em
and 0.03em
; each of which – in my case – seem to show the small notches/gaps in the borders.
Unfortunately, the only solutions are non-ideal.
Upvotes: 0
Reputation: 19049
Neighboring borders are the core problem here. There're different ways to fix it, one of them could involve forcibly removing (de-duplicating) borders that appear close to each other. However, I don't think it's a good option, especially since it doesn't allow free flow of your cards (in case of regrouping - due to window width changes).
You could consider adding margins between cards, so as the cards would look cleaner.
.item {
display: flex;
flex-wrap: wrap;
}
.items {
border: 1px solid red;
width: 200px;
padding: 10px;
margin: 5px;
}
The same look can be achieved when grid-gap:10px
used.
.item {
display: flex;
flex-wrap: wrap;
grid-gap:10px;
}
.items {
border: 1px solid red;
width: 200px;
padding: 10px;
}
Upvotes: 1