Сворачивание границ с помощью CSS Grid

Я с удовольствием разбираюсь в новой спецификации CSS Grid, но у меня возникают проблемы с границами.

Можно ли свернуть границы в сетке CSS или как-то изменить стиль поля?

Как вы можете видеть во фрагменте ниже, 10px границ стека (всего 20px) между блоками.

Я понимаю, что эта проблема не уникальна для CSS Grids, но я надеюсь, что это позволит найти новые решения для создания единой границы в 10 пикселей между всеми блоками и на внешних краях.

Мой фактический вариант использования — это календарь, который я делаю, чтобы попрактиковаться в работе с компонентами Grid и React. Вы можете увидеть проблему, с которой я столкнулся здесь:

CSS Grid Calendar.

Поскольку каждый месяц отличается, мне нужно будет рассмотреть множество различных крайних случаев.

.container {
  display: grid;
  grid-template-columns: 120px 120px;
  box-sizing: border-box;
}

.block {
  width: 100px;
  height: 100px;
  background-color: lightgrey;
  border: 10px solid palegreen;
}

.first {
  grid-column: 2 / span 1;
}
<div class='container'>
  <div class='block first'>1</div>
  <div class='block'>2</div>
  <div class='block'>3</div>
</div>


person Tim Foley    schedule 28.04.2017    source источник
comment
К вашему сведению, вот созданный кем-то календарь CSS Grid: stackoverflow.com/q/43311943/3597276   -  person Michael Benjamin    schedule 28.04.2017
comment
Также распространенным решением проблемы является заполнение пустых ячеек блеклыми ячейками для дней из предыдущего и следующего месяца.   -  person Michael Benjamin    schedule 28.04.2017
comment
Спасибо @Michael_B! Мой план состоит в том, чтобы сделать супер-настраиваемый интерфейс календаря, чтобы я мог распечатывать месячные календари, которые выглядят именно так, как мне нравится. Однако сейчас речь идет в основном об учебных упражнениях. С нетерпением жду рассмотрения этого вопроса и ответов на него; похоже, это будет полезно.   -  person Tim Foley    schedule 28.04.2017
comment
Примечательно, что эти современные способы рисования сетки по-прежнему имеют столь фундаментальные недостатки. Сравните это с разметкой таблиц на заре HTML, когда можно было просто использовать border-collapse: collapse.   -  person Ciantic    schedule 17.03.2021


Ответы (5)


Вы можете использовать grid-gap и box-shadow:

.container {
  display: grid;
  grid-template-columns: 100px 100px;
  box-sizing: border-box;
  grid-gap:10px;
}

.block {
  width: 100px;
  height: 100px;
  background-color: lightgrey;
 box-shadow:0 0 0 10px palegreen;
}

.first {
  grid-column: 2 / span 1;
}
<div class='container'>
  <div class='block first'>1</div>
  <div class='block'>2</div>
  <div class='block'>3</div>
</div>

Или объедините настройку шаблона строки и столбца:

.container {
  display: grid;
  grid-template-columns: 110px 110px;
  grid-template-rows:110px;
  box-sizing: border-box;
  
}

.block {
  width: 100px;
  height: 100px;
  background-color: lightgrey;
 border:solid 10px palegreen;
}

.first {
  grid-column: 2 / span 1;
}
<div class='container'>
  <div class='block first'>1</div>
  <div class='block'>2</div>
  <div class='block'>3</div>
</div>

Обратите внимание, что столбцы и строки размером 120 пикселей будут отображать границы с обеих сторон, если для поля установлено значение 100 пикселей...

Если для столбцов используется значение fr, то не устанавливайте ширину для полей (строки будут иметь такое же ограничение).

.container {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-template-rows: 110px;
  /*whatever else */
  box-sizing: border-box;
}

.block {
  margin: 0 -10px 0 0;/* fixed width value missing */
  height: 100px;
  background-color: lightgrey;
  border: solid 10px palegreen;
}

.first {
  grid-column: 2 / span 1;
}
<div class='container'>
  <div class='block first'>1</div>
  <div class='block'>2</div>
  <div class='block'>3</div>
  <div class='block'>4</div>
  <div class='block'>5</div>
  <div class='block'>6</div>
  <div class='block'>7</div>
</div>

person G-Cyrillus    schedule 28.04.2017
comment
Потрясающий! Я обязательно попробую и то, и другое. - person Tim Foley; 28.04.2017
comment
Как вы думаете, эти подходы все еще могли бы работать, если бы я определил свой шаблон сетки с помощью frs? - person Tim Foley; 28.04.2017
comment
@TimFoley Если вы это сделаете, не устанавливайте ширину для дочерних элементов, только номер столбца в шаблоне edit добавил пример фрагмента - person G-Cyrillus; 28.04.2017
comment
@TimFoley пример с 2 сетками рядом и 1fr для строк и столбцов: codepen.io/gc -nomade/pen/QvpBaO - person G-Cyrillus; 28.04.2017
comment
Только что проверил это, и я думаю, что вы решили это! Благодаря тонну - person Tim Foley; 28.04.2017

Рассмотрите возможность управления всеми размерами и интервалами на уровне контейнера сетки, а не на уровне элемента сетки. Удалите границы и размеры, примененные к элементам.

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); /* 1 */ /* 2 */
  grid-auto-rows: 100px; /* 3 */
  grid-gap: 5px; /* 4 */
  padding: 5px;
  background-color: tomato;
}

.block {
  background-color: lightgrey;
}

/* for demo only */
.block:nth-child(-n + 2) {
  visibility: hidden;
}
<div class='container'>
  <div class='block'>0</div>
  <div class='block'>0</div>
  <div class='block'>1</div>
  <div class='block'>2</div>
  <div class='block'>3</div>
  <div class='block'>4</div>
  <div class='block'>5</div>
  <div class='block'>6</div>
  <div class='block'>7</div>
  <div class='block'>8</div>
  <div class='block'>9</div>
  <div class='block'>10</div>
  <div class='block'>11</div>
  <div class='block'>12</div>
  <div class='block'>13</div>
  <div class='block'>14</div>
  <div class='block'>15</div>
  <div class='block'>16</div>
  <div class='block'>17</div>
  <div class='block'>18</div>
  <div class='block'>19</div>
  <div class='block'>20</div>
  <div class='block'>21</div>
  <div class='block'>22</div>
  <div class='block'>23</div>
  <div class='block'>24</div>
  <div class='block'>25</div>
  <div class='block'>26</div>
  <div class='block'>27</div>
  <div class='block'>28</div>
  <div class='block'>29</div>
  <div class='block'>30</div>
  <div class='block'>31</div>  
</div>

демонстрация jsFiddle

Примечания:

  1. auto-fit: Заполните столько столбцов, сколько может поместиться в строке. Столбцы переполнения будут перенесены.
  2. minmax(): Каждый столбец будет иметь минимальную ширину 120 пикселей и максимальную ширину любого доступного свободного места. Единица fr сравнима со свойством flex-grow гибкого макета.
  3. grid-auto-rows: Автоматически создаваемые строки (неявные строки) будут иметь высоту 100 пикселей.
  4. grid-gap: промежутки шириной 5 пикселей по всему периметру. Сокращение для grid-column-gap и grid-row-gap.
person Michael Benjamin    schedule 28.04.2017
comment
Хороший! Я надеюсь иметь больше контроля над тем, что происходит с незаполненным пространством. Я обязательно учту это для других приложений. - person Tim Foley; 28.04.2017
comment
Я имею в виду зеленый фоновый цвет, отображаемый в верхнем левом и нижнем правом углу. - person Tim Foley; 28.04.2017

Я только что нашел простой способ добиться этого, используя css outline вместо border.

Свойство outline рисует линию за пределами элемента, поэтому зазор в 1 пиксель сворачивает обе линии.

.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  gap: 1px; /* you can use gap instead of grid-gap */
}

.block {
  width: 100px;
  height: 100px;
  background-color: lightgrey;
  outline: 1px solid darkgreen; /* Use outline instead of border */
}

.first {
  grid-column: 2 / span 1;
}
<div class='container'>
  <div class='block first'>1</div>
  <div class='block'>2</div>
  <div class='block'>3</div>
  <div class='block'>4</div>
  <div class='block'>5</div>
  <div class='block'>6</div>
</div>

Как прокомментировал TylerH, контур не занимает места и может перекрываться, поэтому вам нужно использовать для него пробел, если вы хотите линию 5px, вы должны написать 5px для обоих свойств, контура и пробела.

.container {
  display: grid;
  grid-template-columns: 100px 100px 100px;
  gap: 5px;
}

.block {
  width: 100px;
  height: 100px;
  background-color: lightgrey;
  outline: 5px solid darkgreen; /* The same width as the gap */
}
person stramin    schedule 03.03.2021
comment
Хотя контур рисует линию за пределами элемента, важно отметить, что он не занимает места, поэтому не вызывает изменений в макете. Это также позволяет использовать странную «функцию», когда вы можете иметь два элемента рядом, и их контуры могут казаться «перекрывающимися». - person TylerH; 03.03.2021

Другой подход, который вы могли бы использовать, если бы вас устраивало, что цвет границы промежутка такой же, как у ячеек дня, которые не попадают в текущий месяц, состоит в том, чтобы обернуть div вокруг всего контейнера сетки и установить его background-color в цвет, который вы хотите, чтобы ваш границ, чтобы быть и дать ему 1px padding с grid-gap 1px. При таком подходе вы можете получить сетку с равномерными границами без сложности использования box-shadow, что мне кажется хаком.

person eriklharper    schedule 20.12.2017
comment
Разве это не то же самое, что и ответ @Michael_B? За исключением того, что он использует границы 5px вместо 1px. - person Håken Lid; 18.04.2018

Я искал чистый CSS-способ свернуть границы сетки, но так как не смог найти, сделал небольшой прототип.

Сетка CSS свернута, границы закруглены, углы

HTML

<div class="container">
  <div id="grid" class="grid">
    <div class="element">1</div>
    <div class="element">2</div>
    <div class="element">3</div>
    <div class="element">4</div>
    <div class="element">5</div>
    <div class="element">6</div>
    <div class="element">7</div>
    <div class="element">8</div>
    <div class="element">9</div>
    <div class="element">10</div>
    <div class="element">11</div>
  </div>
</div>

CSS

.container {
  max-width: 720px;
  margin: 0 auto;
}

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));  
}

.element {
  text-align: center;
  padding: 20px;
  background: #f4f4f4;
  border-bottom: 1px solid black;
  border-right: 1px solid black;
}

.border-top {
  border-top: 1px solid black;
}

.border-left {
  border-left: 1px solid black;
}

.border-top-left-rounded {
  border-top-left-radius: 8px;
}

.border-top-right-rounded {
  border-top-right-radius: 8px;
}

.border-bottom-left-rounded {
  border-bottom-left-radius: 8px;
}

.border-bottom-right-rounded {
  border-bottom-right-radius: 8px;
}

JS

function dynamicRoundedCorners() {
  // get
  const grid = document.getElementById("grid");
  const elements = grid.children;
  const gridStyle = getComputedStyle(grid);

  // reset
  for (element of elements) {
    element.classList = "";
    element.classList.add("element");
  }

  // analyze
  const elementsPerRowCount = gridStyle.gridTemplateColumns
    .split(" ")
    .filter((element) => Number(element.replace("px", ""))).length;
  const rowCount = Math.ceil(elements.length / elementsPerRowCount);
  const rowsFirstAndLastElements = [];
  let firstAndLastElementIndex = 0;

  for (let i = 1; i <= rowCount; i++) {
    const rowFirstAndLastElements = [firstAndLastElementIndex];

    if (i === rowCount && rowCount > 1) {
      rowFirstAndLastElements.push(
        firstAndLastElementIndex + (elements.length % elementsPerRowCount) - 1
      );
    } else {
      rowFirstAndLastElements.push(
        firstAndLastElementIndex + elementsPerRowCount - 1
      );
    }
    rowsFirstAndLastElements.push(rowFirstAndLastElements);
    firstAndLastElementIndex += elementsPerRowCount;
  }

  // apply
  // -> add border-top on the first row
  for (let i = 0; i <= rowsFirstAndLastElements[0][1]; i++) {
    elements[i].classList.add("border-top");
  }

  // -> add border-left on every first element of a row
  for (let i = 0; i < rowCount; i++) {
    elements[rowsFirstAndLastElements[i][0]].classList.add("border-left");
  }

  // -> add top-left rounded corner on first element of first row
  elements[0].classList.add("border-top-left-rounded");
  // -> add top-right rounder corner on last element of first row
  elements[rowsFirstAndLastElements[0][1]].classList.add(
    "border-top-right-rounded"
  );
  // -> add bottom-left rounded corner on first element of last row
  elements[rowsFirstAndLastElements[rowCount - 1][0]].classList.add(
    "border-bottom-left-rounded"
  );
  // -> add bottom-right rounder corner on last element of last row
  elements[elements.length - 1].classList.add("border-bottom-right-rounded");
  // -> if elements.length % elementsPerRowCount != 0, add bottom-right rounder corner on last element of second to last row
  if (elements.length % elementsPerRowCount !== 0) {
    elements[
      rowsFirstAndLastElements[rowsFirstAndLastElements.length - 2][1]
    ].classList.add("border-bottom-right-rounded");
  }
}

// call
dynamicRoundedCorners();
window.addEventListener("resize", dynamicRoundedCorners);

Вот ссылка: https://codepen.io/RilDev/pen/gOmjNrQ

person RilDev    schedule 30.06.2021