0

Im having trouble making a valid HTML table with vertical spacing between rows and shadow below each row.

The shadow always goes on top of other table data.

I have positioned the elements and set a z-index.

enter image description here

  table {
    border-collapse: separate;
    border-spacing: 0;
  }
  td,
  th {
    min-width: 170px;
  }

  .shadow {
    position: relative;
    z-index: 1;
    margin: 2px 0 2px 0;
  }

  .shadow:before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: -1;
    box-shadow: 0 0 10px 10px #000;
  }
<main>
  <table>
    <thead>
      <tr>
        <td>head</td>
        <td>head</td>
        <td>head</td>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><div class="shadow">div</div></td>
        <td><div class="shadow">div</div></td>
        <td><div class="shadow">div</div></td>
      </tr>
      <tr>
        <td><div class="shadow">div</div></td>
        <td><div class="shadow">div</div></td>
        <td><div class="shadow">div</div></td>
      </tr>
      <tr>
        <td><div class="shadow">div</div></td>
        <td><div class="shadow">div</div></td>
        <td><div class="shadow">div</div></td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <td>foot</td>
        <td>foot</td>
        <td>foot</td>
      </tr>
    </tfoot>
  </table>
</main>

1
  • That's the problem, but what's your expected/desired result? Can you share an image, or a detailed explanation? Are the shadows to give the illusion of depth, or to create a border around the cell contents? Commented Jun 28, 2023 at 9:46

3 Answers 3

2

You can do this like.

table {
    border-collapse: collapse;
    border-spacing: 0;
  }
  
  .shadow {
    position: relative;
    z-index: 1;
    margin: 2px 0 2px 0;
  }

  .shadow:before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: -1;
    box-shadow: 0 0 2px 2px #000; 
    }
    
  tr:hover {
      box-shadow: 0 5px 8px 0 rgba(50, 50, 50, 0.35);
      -webkit-box-shadow: 0 5px 8px 0 rgba(50, 50, 50, 0.35);
      -moz-box-shadow: 0 5px 8px 0 rgba(50, 50, 50, 0.35);
       cursor: pointer;
       box-shadow: 0px 2px 18px 0px rgba(0, 0, 0, 0.5);
       background-color: #fbfbfb;
}


td, th {
 text-align:center;
  min-width: 170px;
  border: 1px solid #999;
  padding: 0.5rem;
}
<main>
  <table>
    <thead>
      <tr>
        <td>head</td>
        <td>head</td>
        <td>head</td>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><div class="shadow">1</div></td>
        <td><div class="shadow">2</div></td>
        <td><div class="shadow">3</div></td>
      </tr>
      <tr>
        <td><div class="shadow">4</div></td>
        <td><div class="shadow">5</div></td>
        <td><div class="shadow">6</div></td>
      </tr>
      <tr>
        <td><div class="shadow">7</div></td>
        <td><div class="shadow">8</div></td>
        <td><div class="shadow">9</div></td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <td>foot</td>
        <td>foot</td>
        <td>foot</td>
      </tr>
    </tfoot>
  </table>
</main>

1

table {
    border-collapse: collapse;
    border-spacing: 0;
  }
  
  .shadow {
    position: relative;
    z-index: 1;
    margin: 2px 0 2px 0;
  }

  .shadow:before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: -1;
    box-shadow: 0 0 0px 0px #000; 
    }
    

  
  tr:hover {
      box-shadow: 0 5px 8px 0 rgba(50, 50, 50, 0.35);
      -webkit-box-shadow: 0 5px 8px 0 rgba(50, 50, 50, 0.35);
      -moz-box-shadow: 0 5px 8px 0 rgba(50, 50, 50, 0.35);
}


td, th {
 text-align:center;
  min-width: 170px;
  border: 1px solid #999;
  padding: 0.5rem;
}
<main>
  <table>
    <thead>
      <tr>
        <td>head</td>
        <td>head</td>
        <td>head</td>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><div class="shadow">1</div></td>
        <td><div class="shadow">2</div></td>
        <td><div class="shadow">3</div></td>
      </tr>
      <tr>
        <td><div class="shadow">4</div></td>
        <td><div class="shadow">5</div></td>
        <td><div class="shadow">6</div></td>
      </tr>
      <tr>
        <td><div class="shadow">7</div></td>
        <td><div class="shadow">8</div></td>
        <td><div class="shadow">9</div></td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <td>foot</td>
        <td>foot</td>
        <td>foot</td>
      </tr>
    </tfoot>
  </table>
</main>

3
  • see that approach you like it Commented Jun 28, 2023 at 11:16
  • 1
    Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Jun 30, 2023 at 1:13
  • @Community okay i am trying to improve it. Commented Jun 30, 2023 at 21:41
0

One approach is as below, with explanatory comments in the code itself:

/* removing all default padding and margins, and ensuring
   that all elements are sized to include their padding
   and border-widths: */
*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

table {
  /* CSS custom properties for various properties to ensure
     common styling where appropriate;
      --tr-space-between is the size of the percieved/visible
     gap between adjacent rows: */
  --tr-space-between: 0.5rem;
  /* --td-padding-block is derived from the previous variable,
     using calc() so that the space between 'rows' is inserted
     as padding (padding-block) to create that row-spacing: */
  --td-padding-block: calc(var(--tr-space-between)/2);
  --shadow-color: lightgray;
  --row-color: #fff;
  /* the desired radius of the 'rows': */
  --row-radius: 0.5rem;
  /* to ensure page background can be seen (if required)
     in the row-gaps: */
  background-color: transparent;
  /* collapsing the borders between cells in order to allow the
     content to be contiguous, and using a different means to
     achieve row-"separation": */
  border-collapse: collapse;
  border-spacing: 0;
  /* using a CSS filter, drop-shadow(), to create the shadows: */
  filter: drop-shadow(0 0 0.5rem var(--shadow-color));
  /* centering the <table> */
  margin-inline: auto;
}

td,
th {
  min-width: 170px;
}

td,
th {
  /* again to ensure that the page background is - where
     appropriate - visible through the visual gaps: */
  background-color: transparent;
}

td {
  /* setting the cell padding on the block axis, to "separate"
     the "rows", while no padding is applied on the inline
     axis, so that the rows are visually contiguous: */
  padding-block: var(--td-padding-block);
}

/* using logical properties to set the border radii: */
td:first-child .content {
  border-start-start-radius: var(--row-radius);
  border-end-start-radius: var(--row-radius);
}

td:last-child .content {
  border-start-end-radius: var(--row-radius);
  border-end-end-radius: var(--row-radius);
}

.content {
  /* setting the background-color of the "row": */
  background-color: var(--row-color);
  /* applying padding on all axes, to move the content
     away from the edges of the 'row': */
  padding: 0.5rem;
}
<main>
  <table>
    <thead>
      <tr>
        <th>head</th>
        <th>head</th>
        <th>head</th>
      </tr>
    </thead>
    <tbody>
      <!-- I've changed the class-name of the <div> from 'shadow' to
           'content' to reflect what the "purpose" of the element: -->
      <tr>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
      </tr>
      <tr>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
      </tr>
      <tr>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <td>foot</td>
        <td>foot</td>
        <td>foot</td>
      </tr>
    </tfoot>
  </table>
</main>

There are, of course, other approaches; I've shown a couple more in the following example, again: explanatory comments are in the code:

const changeBackground = (evt) => {
  let {
    currentTarget
  } = evt, {
    value
  } = currentTarget,
  table = document.querySelector('table');

  table.dataset.shadow = value;
}

document.querySelectorAll('input[type=radio]').forEach(
  (el) => el.addEventListener('change', changeBackground)
);

document.querySelector('input:checked').dispatchEvent(new Event('change'));
/* removing all default padding and margins, and ensuring
   that all elements are sized to include their padding
   and border-widths: */

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  /* a relatively simple background to show how the transparencies
     might be beneficial; otherwise irrelevant to the demo: */
  background-image:
    repeating-linear-gradient(
      to bottom left,
      transparent,
      hsl(140deg 90% 80% / 0.7)
    ), radial-gradient(
      at 0 0,
      hsl(300deg 95% 85% / 1),
      hsl(180deg 95% 85% / 1)
    );
  block-size: 100vh;
  padding: 1rem;
}

form {
  inline-size: 70%;
  margin-block: 1rem;
  margin-inline: auto;
}

fieldset {
  border: 0 none transparent;
  display: flex;
  flex-flow: row wrap;
  gap: 1rem;
  justify-content: space-between;
  padding: 1rem;
}

legend {
  font-size: 1.2rem;
  position: relative;
}

label {
  flex-grow: 1;
}

.labelText {
  /* using a CSS custom property to simplify the CSS: */
  --active-indication: hsl(0deg 75% 70% / 0.55);
  background-color: #cccd;
  /* using a background-image to give show whether a
     selection has, or has not, been made; this is the
     default 'inactive' state: */
  background-image:
    linear-gradient(
    270deg,
    var(--active-indication) 0 1.5rem,
    /* gives the illusion of a border separating the
       background-color of the element, and the
       'activation'/checked state: */
    #0009 1.5rem calc(1.5rem + 1px),
    /* transparent, to allow the background to show through: */
    transparent calc(1.5rem + 1px)
  );
  border: 1px solid #0009;
  border-radius: 1rem;
  display: block;
  padding: 0.75rem;
  padding-inline-end: 2rem;
}

/* moving the radio inputs off screen to hide them: */
input[type=radio] {
  position: absolute;
  left: -2000px;
}

/* this selector matches the .labelText that immediately
   follows a checked <input>; despite being hidden off-screen
   the <input> still precedes the .labelText element in
   the DOM: */
input:checked + .labelText {
  /* here we update the custom property, which updates
     the background-image/linear-gradient: */
  --active-indication: hsl(160deg 95% 75% / 1);
}

table {
  /* CSS custom properties for various properties to ensure
     common styling where appropriate;
      --tr-space-between is the size of the percieved/visible
     gap between adjacent rows: */
  --tr-space-between: 0.5rem;
  /* --td-padding-block is derived from the previous variable,
     using calc() so that the space between 'rows' is inserted
     as padding (padding-block) to create that row-spacing: */
  --td-padding-block: calc(var(--tr-space-between)/2);
  --shadow-color: #339a;
  --row-color: #fff;
  --row-inset: 1rem;
  /* the desired radius of the 'rows': */
  --row-radius: 0.5rem;
  /* to ensure page background can be seen (if required)
     in the row-gaps: */
  background-color: transparent;
  /* collapsing the borders between cells in order to allow the
     content to be contiguous, and using a different means to
     achieve row-"separation": */
  border-collapse: collapse;
  border-spacing: 0;
  margin-block: 1rem;
  /* centering the <table> */
  margin-inline: auto;
}

/* using attribute-selectors along wtih custom data-* attributes
   to appropriately style the <table> based on the choice made as
   to the approach: */
table[data-shadow="drop-shadow"] {
  /* using a CSS filter, drop-shadow(), to create the shadows: */
  filter: drop-shadow(0 0 2rem var(--shadow-color));
}

table[data-shadow="box-shadow"] {
  /* using a box-shadow, note that this provides a shadow *around*
     the element, but not between the 'rows' (this is why I didn't
     use box-shadow in the original code, and this is simply to
     illustrate that point): */
  box-shadow: 0.5rem 0.5rem 2rem var(--shadow-color);
}

table[data-shadow="backdrop-filter"] {
  /* this allows a number of different functions to be used to
     to style the view of whatever is visible "through" the
     background of the <table> element: */
  backdrop-filter: hue-rotate(245deg) opacity(0.4);
}

td,
th {
  min-width: 170px;
}

td,
th {
  /* again to ensure that the page background is - where
     appropriate - visible through the visual gaps: */
  background-color: transparent;
}

td {
  /* setting the cell padding on the block axis, to "separate"
     the "rows", while no padding is applied on the inline
     axis, so that the rows are visually contiguous: */
  padding-block: var(--td-padding-block);
}


/* using logical properties to set the border radii: */

td:first-child .content {
  border-start-start-radius: var(--row-radius);
  border-end-start-radius: var(--row-radius);
}

td:last-child .content {
  border-start-end-radius: var(--row-radius);
  border-end-end-radius: var(--row-radius);
}

td:first-child {
  padding-inline-start: var(--row-inset);
}

td:last-child {
  padding-inline-end: var(--row-inset);
}

.content {
  /* setting the background-color of the "row": */
  background-color: var(--row-color);
  /* applying padding on all axes, to move the content
     away from the edges of the 'row': */
  padding: 0.5rem;
}
<main>
  <form action="#" id="choices" method="post">
    <fieldset>
      <legend>Choose approach</legend>
      <label>
        <input type="radio" name="shadowType" value="drop-shadow" checked>
        <span class="labelText"><code>filter: drop-shadow()</code></span>
      </label>
      <label>
        <input type="radio" name="shadowType" value="box-shadow" >
        <span class="labelText"><code>box-shadow</code></span>
      </label>
      <label>
        <input type="radio" name="shadowType" value="backdrop-filter" >
        <span class="labelText"><code>backdrop-filter</code></span>
      </label>
    </fieldset>
  </form>
  <table>
    <thead>
      <tr>
        <th>head</th>
        <th>head</th>
        <th>head</th>
      </tr>
    </thead>
    <tbody>
      <!-- I've changed the class-name of the <div> from 'shadow' to
           'content' to reflect what the "purpose" of the element: -->
      <tr>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
      </tr>
      <tr>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
      </tr>
      <tr>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
        <td>
          <div class="content">div</div>
        </td>
      </tr>
    </tbody>
    <tfoot>
      <tr>
        <td>foot</td>
        <td>foot</td>
        <td>foot</td>
      </tr>
    </tfoot>
  </table>
</main>

Not the answer you're looking for? Browse other questions tagged or ask your own question.