Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Responsive Card Decks #20321

Closed
markoheijnen opened this issue Jul 18, 2016 · 56 comments
Closed

Responsive Card Decks #20321

markoheijnen opened this issue Jul 18, 2016 · 56 comments

Comments

@markoheijnen
Copy link

The previous issue about this was #19826 but got closed by the reporter without getting the ability to receive proper feedback.

It would really great to be able to define how many cards per row you maximum want. The card deck is an awesome (and only way in BS) to get the same height of all the items in the row. But the main problem is that if you have more then 2-3 items in it, it already starts to become squeezed together on smaller screens.

In the previous ticket it was mentioned to use something like card-deck cards-sm-6 cards-md-4 cards-lg-3 but maybe something like card-deck-md-3 card-deck-lg-4 where you define the amount of columns you want could work too. As people may want to include 5 columns and that doesn't work well with the grid system.

@XaserAcheron
Copy link

Funny enough, the entire reason I opted for trying out Bootstrap 4's alpha was to use card decks for same-height containers, but when I realized I couldn't adjust where the breakpoint occurs, I had to step back and roll my own CSS anyway. Welp! :P

Long-winded +1, basically.

@robwent
Copy link

robwent commented Aug 19, 2016

When using flexbox, columns get the same height anyway, so you could probably get the effect you are looking for by just using standard columns?

This works for me

<div class="row">
    <div class="col-sm-6 col-md-4 col-lg-3">
        <div class="card">
            <div class="card-block">
                <h3 class="card-title">The title</h3>
                <div class="card-text">
                    The text
                </div>
            </div>
        </div>
    </div>
    <div class="col-sm-6 col-md-4 col-lg-3">
        <div class="card">
            <div class="card-block">
                <h3 class="card-title">The title</h3>
                <div class="card-text">
                    The text
                </div>
            </div>
        </div>
    </div>
    <div class="col-sm-6 col-md-4 col-lg-3">
        <div class="card">
            <div class="card-block">
                <h3 class="card-title">The title</h3>
                <div class="card-text">
                    The text
                </div>
            </div>
        </div>
    </div>
    <div class="col-sm-6 col-md-4 col-lg-3">
        <div class="card">
            <div class="card-block">
                <h3 class="card-title">The title</h3>
                <div class="card-text">
                    The text
                </div>
            </div>
        </div>
    </div>
</div>

Although it kind of defeats the point of having card decks.

Edit: You would need to add a class to the card to give it 100% height to make the border extend to the full column.

@whodidthis
Copy link

Hopefully not too offtopic but ignoring the flexbox option: What if worked around this by rendering separate card decks for each breakpoint, eg. 2 card decks for medium screen, 3 card decks for large screen. It will produce some duplicate data so will I get penalized by search engines or is there a way to avoid that?

@mdo mdo modified the milestone: v4.1 ideas Sep 9, 2016
@niftylettuce
Copy link

niftylettuce commented Sep 27, 2016

OK I figured out how to do this at least effectively and cleanly. I was inspired by this article http://www.minimit.com/demos/bootstrap-3-responsive-columns-of-same-height.

Add this SCSS to your project:

// bootstrap
@import "_variables";
@import "../../../node_modules/bootstrap/scss/_variables";
@import "../../../node_modules/bootstrap/scss/mixins/_breakpoints";

// <http://www.minimit.com/demos/bootstrap-3-responsive-columns-of-same-height>

/* USAGE
<div class="row">
  <div class="row-height">
    <div class="col-xs-2 col-xs-height col-xs-middle">
      <div class="inside"></div>
    </div>
    <div class="col-xs-4 col-lg-5 col-xs-height col-xs-middle">
      <div class="inside"></div>
    </div>
  </div>
</div>
*/

/* content styles */

.inside-full-height {
  // if you want to give content full height give him height: 100%;
  // with content full height you can't apply margins to the content
  // content full height does not work in ie http://stackoverflow.com/questions/27384433/ie-display-table-cell-child-ignores-height-100
  height: 100%;
  margin-top: 0;
  margin-bottom: 0;
}

/* columns of same height styles */
.row-height {
  display: table;
  table-layout: fixed;
  height: 100%;
  width: 100%;
}
.col-height {
  display: table-cell;
  float: none;
  height: 100%;
}
.col-top {
  vertical-align: top;
}
.col-middle {
  vertical-align: middle;
}
.col-bottom {
  vertical-align: bottom;
}

@include media-breakpoint-only(xs) {
  .row-xs-height {
    display: table;
    table-layout: fixed;
    height: 100%;
    width: 100%;
  }
  .col-xs-height {
    display: table-cell;
    float: none;
    height: 100%;
  }
  .col-xs-top {
    vertical-align: top;
  }
  .col-xs-middle {
    vertical-align: middle;
  }
  .col-xs-bottom {
    vertical-align: bottom;
  }
}

@include media-breakpoint-only(sm) {
  .row-sm-height {
    display: table;
    table-layout: fixed;
    height: 100%;
    width: 100%;
  }
  .col-sm-height {
    display: table-cell;
    float: none;
    height: 100%;
  }
  .col-sm-top {
    vertical-align: top;
  }
  .col-sm-middle {
    vertical-align: middle;
  }
  .col-sm-bottom {
    vertical-align: bottom;
  }
}

@include media-breakpoint-only(md) {
  .row-md-height {
    display: table;
    table-layout: fixed;
    height: 100%;
    width: 100%;
  }
  .col-md-height {
    display: table-cell;
    float: none;
    height: 100%;
  }
  .col-md-top {
    vertical-align: top;
  }
  .col-md-middle {
    vertical-align: middle;
  }
  .col-md-bottom {
    vertical-align: bottom;
  }
}

@include media-breakpoint-only(lg) {
  .row-lg-height {
    display: table;
    table-layout: fixed;
    height: 100%;
    width: 100%;
  }
  .col-lg-height {
    display: table-cell;
    float: none;
    height: 100%;
  }
  .col-lg-top {
    vertical-align: top;
  }
  .col-lg-middle {
    vertical-align: middle;
  }
  .col-lg-bottom {
    vertical-align: bottom;
  }
}

@include media-breakpoint-only(xl) {
  .row-xl-height {
    display: table;
    table-layout: fixed;
    height: 100%;
    width: 100%;
  }
  .col-xl-height {
    display: table-cell;
    float: none;
    height: 100%;
  }
  .col-xl-top {
    vertical-align: top;
  }
  .col-xl-middle {
    vertical-align: middle;
  }
  .col-xl-bottom {
    vertical-align: bottom;
  }
}

Example HTML usage (reference the article for more info):

      <div class="row text-xs-center">
        <div class="row-md-height row-lg-height row-xl-height">
          <div class="col-xs-12 col-sm-12 col-md-6 col-lg-6 col-xl-6 col-md-height col-lg-height col-xl-height col-md-top col-lg-top col-xl-top">
            <div class="inside inside-full-height card bg-inverse">
              <div class="card-block p-y-2">
                <h2 class="card-title">Get Started</h2>
                <kbd class="d-inline-block p-a-1 bg-success">npm install -g crocodile</kbd>
                <div class="clearfix"></div>
                <p class="card-text m-t-1">
                  <a href="https://github.com/crocodilejs/crocodile-node-mvc-framework#requirements" target="_blank" class="btn btn-outline-secondary bg-inverse btn-sm"><i class="fa fa-cogs"></i> You'll need Node.js + MongoDB + Redis</a>
                </p>
              </div>
            </div>
          </div>
          <div class="hidden-md-up col-xs-12 col-sm-12 m-y-1"></div>
          <div class="col-xs-12 col-sm-12 col-md-6 col-lg-6 col-xl-6 col-md-height col-lg-height col-xl-height col-md-top col-lg-top col-xl-top">
            <div class="inside inside-full-height card card-outline-secondary">
              <div class="card-block p-y-2">
                <h3 class="card-title h2">Using Commercially?</h2>
                <button type="button" class="btn btn-primary btn-md">Purchase License + Free Swag</button>
                <p class="card-text"><small class="text-muted">We use <a href="https://stripe.com" target="_blank">Stripe</a> for credit-card processing</small></p>
              </div>
            </div>
          </div>
        </div>
      </div>

I built this for my framework @crocodilejs which uses Bootstrap 4 https://github.com/crocodilejs/crocodile-node-mvc-framework.

screen shot 2016-09-27 at 8 41 39 am

screen shot 2016-09-27 at 8 41 33 am

screen shot 2016-09-27 at 8 41 28 am

@niftylettuce
Copy link

Updated HTML/SCSS above to account for XL class.

@robwent
Copy link

robwent commented Sep 27, 2016

I don't really see how that is clean. Compare it to the code above that just needs a class to give 100% height and uses built in classes.

@niftylettuce
Copy link

@robwent show the example + CSS that gives 100% height?

@robwent
Copy link

robwent commented Sep 27, 2016

.any-class {
height: 100%;
}

Add that to each card or col and turn on flexbox.

Probably don't even need that with flexbox, make it stretch.

@niftylettuce
Copy link

Does not work without flexbox

On Sep 27, 2016 11:22 AM, "Robert Went" [email protected] wrote:

.any-class {
height: 100%;
}

Add that to each card or col and turn on flexbox.

Probably don't even need that with flexbox, make it stretch.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
#20321 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAf7hbNpr_2IAE5RSwReM_ZnDz82Gab0ks5quTO9gaJpZM4JOy7_
.

@elafari
Copy link

elafari commented Nov 8, 2016

@robwent Thanks for the tip with height: 100% in combination with flexbox.

However when I do so, the margin-bottom of the cards looses its effect which causes the cards on top of each other to stick together. Any idea why this happens?

As a workaround I added a padding-bottom to the card-wrapping columns.

@robwent
Copy link

robwent commented Nov 8, 2016

@elafari Yeah, I just noticed this myself a couple of days ago.
I ended up adding a mb-1 class and adding it to the column (Or I created a mb-g class for a margin bottom grid spacer).

I thought it was actually down to the way I was using the cards as looking at my setup they should just really be columns with a background colour :)

So basically, I have nothing helpful to add, sorry.

@garygreen
Copy link

+1 would be nice to see grid type system support for card decks.

@allaire
Copy link

allaire commented Dec 23, 2016

height: 100% isn't working in Safari :(

@JacobLett
Copy link

I encountered this issue and the non-flexbox CSS uses display:table to achieve the 100% height. I decided to just write a custom media query to remove that styling at the breakpoint where things start looking funky. In my case it was 950px. You can see a demo here http://codepen.io/JacobLett/pen/rWXJPV

@jbblanchet
Copy link

As an intermediate easy step, maybe just adding decks that break at different break points. The current implementation is based on media-breakpoint-up(sm), but having card-deck-md, card-deck-lg and card-deck-xl would be easy to implement and offer a lot more flexibility.

@iatek
Copy link
Contributor

iatek commented Apr 11, 2017

Another approach/workaround for responsive card-deck is to force a wrap using the visibility utils every X cards...

<div class="card-deck">
        <div class="card">
            ..
        </div>
        <div class="card">
            ..
        </div>
        <div class="w-100 hidden-xs-down hidden-md-up"><!-- wrap every 2 on sm--></div>
        <div class="card">
            ..
        </div>
        <div class="w-100 hidden-sm-down hidden-lg-up"><!-- wrap every 3 on md--></div>
        <div class="card">
            ..
        </div>
        <div class="w-100 hidden-xs-down hidden-md-up"><!-- wrap every 2 on sm--></div>
        <div class="w-100 hidden-md-down hidden-xl-up"><!-- wrap every 4 on lg--></div>
        <div class="card">
            ..
        </div>
        <div class="w-100 hidden-lg-down"><!-- wrap every 5 on xl--></div>
        <div class="card">
            ..
        </div>
</div>

@mbrookes
Copy link

mbrookes commented Jun 1, 2017

@robwent

I don't really see how that is clean. Compare it to the code above that just needs a class to give 100% height and uses built in classes.

Clean, but doesn't work in all browsers. Dirty is better than clean and broken...

@robwent
Copy link

robwent commented Jun 1, 2017

@mbrookes If you mean flexbox then you might as well stick with bs3

@mbrookes
Copy link

mbrookes commented Jun 1, 2017

@robwent #20321 (comment)
I had the same issue.

@robwent
Copy link

robwent commented Jun 2, 2017

@mbrookes It worked fine for me in all browsers supported by bs4.

If it doesn't work for you, then try the other suggestions and then move on.

Tagging people in 6-month-old threads to make smug comments is just a waste of everybody's time.
I'm blocking your notifications, so don't expect any more replies.

@TC72
Copy link

TC72 commented Jun 7, 2017

The most annoying part of this is they already have a solution for card-columns. When you use them all you need to do is specify the column-count you want at each breakpoint:

.card-columns {
  @include media-breakpoint-only(xl) {
    column-count: 5;
  }
  @include media-breakpoint-only(lg) {
    column-count: 4;
  }
  @include media-breakpoint-only(md) {
    column-count: 3;
  }
  @include media-breakpoint-only(sm) {
    column-count: 2;
  }
}

Card decks only behave responsively when you get to the xs breakpoint, they then go to 1 column.

@TC72
Copy link

TC72 commented Jun 17, 2017

I've managed to find a way of having cards in the responsive system while having equal heights just like a deck. I found most of the solution here, option 4:
https://scotch.io/bar-talk/different-tricks-on-how-to-make-bootstrap-columns-all-the-same-height

Not all of this CSS is required as some options are already enabled by Bootstrap. I enable it by adding the CSS class 'equal-height' to a row which then has cards contained within responsive columns:

.row.equal-height {
    display: flex;
    flex-wrap: wrap;
}
.row.equal-height > [class*='col'] {
    display: flex;
    flex-direction: column;
}

.card {
    flex: 1;
}

basic html structure:

<div class="row equal-height">
    <div class="col-12 col-md-6">
        <div class="card">
            <!-- Your card content here -->
        </div>
    </div>
</div>

edit: the value 'flex: 1 0 0;' I was using on class .card was causing problems. Safari is happy with 0 but Chrome expects 0%. Being honest I don't understand what flex-shrink and flex-basis do but specifying flex: 1; so only setting the flex-grow value works in all the browsers I tested.

edit2 June 2020: I've come back as I needed this on another project, I see this is closed and a solution has been found for responsive columns but they don't give you equal height. I used my fix again and realised it didn't work if you just used col, not col-(some modifier). I've changed the code to fix that, match 'col' not 'col-'.
But now the footer is broken, if I find a fix I'll post it.

@lf-jeremy
Copy link

The solution from @iatek inspired me to use this strategy to break up a deck of 4 cards to start stacked, then display 2x2 at sm, and 1x4 across at larger viewports:

<!-- placed after second card -->
<div class="d-none d-sm-block d-md-none flex-basis-100"><!-- wrap every 2 on sm --></div>
<!-- placed before third card -->
.flex-basis-100 {
  flex-basis: 100%;
}

The flex-basis forces the next card to wrap to a new row, and the responsive utility classes control when the wrapping occurs. I added some utility spacing classes to the cards to adjust the vertical spacing when stacked 2x2.

@mdo mdo removed this from the v4.1 ideas milestone Jul 8, 2018
@juanjzv
Copy link

juanjzv commented Jul 17, 2018

The solution from @tihab worked just right in my case, it didn't even needs to create new css classes and works with the grid layout just as expected.

@korthjp17
Copy link
Contributor

I know there are several solutions proposed throughout this thread, but maybe even just a simple solution of adding a variable to select which breakpoint they go full width would be helpful. I was experimenting with something like this:

Changing https://github.com/twbs/bootstrap/blob/v4-dev/scss/_card.scss#L149 to

@include media-breakpoint-up($card-deck-breakpoint) {

and adding that variable to vars.scss - $card-deck-breakpoint: sm;

@millturnK
Copy link

Bootstrap 4.1 has .card-columns which solves this problem. They must have listened to all these bright ideas ;-).

@SkuterPL
Copy link

Bootstrap 4.1 has .card-columns which solves this problem. They must have listened to all these bright ideas ;-).

This is not we expect, column works different than gropus.

@Misiu
Copy link

Misiu commented Mar 12, 2019

@migliori @midzer thank You for Your solutions.
They will help me a lot.
@mdo any chance this could be added to v4 or must we wait for v5?

@manju-reddys
Copy link

Following css proposal to make card-group, card-deck or simply cards can be made responsive (without any JS code).
This feature only supports the browsers listed here.

.grid-container {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
        grid-gap: 20px;
        padding: 3px 8px;
        width: 100%;
    }
    .grid-item {
        display: grid;
        height: 148px;
        overflow: hidden;
        max-width: 400px;
        box-shadow: 0 6px 10px rgba(0, 0, 0, .08), 0 0 6px rgba(0, 0, 0, .05);
        transition: .3s transform cubic-bezier(.155, 1.105, .295, 1.12), .3s box-shadow;
        border-radius: 5px;
    }

Demo

@olegjs
Copy link

olegjs commented Mar 22, 2019

We might not need this feature after all...
Simply drop h-100 on cards in grid.
An example in the API doc is a valid solution.

<div class="row">
  <div class="col-sm-6 my-3">
    <div class="card h-100"> <!-- full height -->
      <div class="card-header"></div>
      <div class="card-body"></div>
      <div class="card-footer"></div>
    </div>
  </div>
</div>

@whitespacecode
Copy link

As said before being able to use the class .card-deck and followed by classes card-deck-md-3 card-deck-lg-4 would be great!

The way i did my code now is like this:

<div class="container">
  <div class="card-group">
    <div class="col-md-6 col-lg-4 mb-4 m-lg-0"> <!-- Wrap card in a col -->
      <div class="card h-100"> <!-- Add full height to card -->
        <div class="card-header"></div>
        <div class="card-body"></div>
        <div class="card-footer"></div>
      </div>
    </div>
  </div>
</div>

dtrevino-grupopv added a commit to cetinajero/jekyll-theme-marketing that referenced this issue Jun 11, 2019
Also updated jumbotron component to extend all his height

Based on: 
twbs/bootstrap#20321 (comment)
@mdo
Copy link
Member

mdo commented Jul 18, 2019

Just opened #29073 to try to tackle this in a way that works for v4 and v5. This is a long time coming, and thanks to all the code snippets shared here, I had a few paths I could explore. Can y'all weigh in on that PR?

@amela-arades
Copy link

just add
height: calc(100vh - 120px);

@whitespacecode
Copy link

just add
height: calc(100vh - 120px);

What a stupid way to code.. adding a fix height vh and fix px height is not done!

@mdo
Copy link
Member

mdo commented Aug 30, 2019

With #29073 merged, this will be possible in v4.4 and v5!

<div class="row row-cols-md-2">
  <div class="col mb-4">
    <div class="card">
      {{< placeholder width="100%" height="140" class="card-img-top" text="Image cap" >}}
      <div class="card-body">
        <h5 class="card-title">Card title</h5>
        <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
      </div>
    </div>
  </div>
  <div class="col mb-4">
    <div class="card">
      {{< placeholder width="100%" height="140" class="card-img-top" text="Image cap" >}}
      <div class="card-body">
        <h5 class="card-title">Card title</h5>
        <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
      </div>
    </div>
  </div>
  <div class="col mb-4">
    <div class="card">
      {{< placeholder width="100%" height="140" class="card-img-top" text="Image cap" >}}
      <div class="card-body">
        <h5 class="card-title">Card title</h5>
        <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content.</p>
      </div>
    </div>
  </div>
  <div class="col mb-4">
    <div class="card">
      {{< placeholder width="100%" height="140" class="card-img-top" text="Image cap" >}}
      <div class="card-body">
        <h5 class="card-title">Card title</h5>
        <p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
      </div>
    </div>
  </div>
</div>

@twbs twbs deleted a comment from whitespacecode Sep 3, 2019
@mdo
Copy link
Member

mdo commented Mar 3, 2020

Closing out per last comment.

@mdo mdo closed this as completed Mar 3, 2020
@goulvench
Copy link

goulvench commented Jun 15, 2020

I know this issue has been closed, but I wanted to share the solution I found which works well for me, and allows card-footers to align with each other —which isn't the case with the row/col solution above.

On smaller screens, the existing card-deck implementation displays only one card per row, without margins, and I've ensured this is still the case for maximum interoperability. It might be useful to mention this in the card-deck documentation.

Example HTML:

<div class="card-deck deck-md-2 deck-lg-3">
    <div class="card">
        <div class="card-body">
            Card contents
        </div>
        <div class="card-footer">
            Card footer
        </div>
    </div>
    <div class="card">
        <img src="..." class="card-img-top" alt="...">
        <div class="card-body">
            This is a longer card with more content. The text should wrap, but footers should remain aligned with one another thanks to Flexbox magic. Yay!
        </div>
        <div class="card-footer">
            Card footer
        </div>
    <div class="card">
        <div class="card-body">
            This is a longer card with even more content. 
            <br />And line breaks to ensure text does wrap.
            <br />And another line break for good measure.
        </div>
        <div class="card-footer">
            Card footer
        </div>
    </div>
</div>

SCSS

// _card_deck_columns.scss
// add deck-X and deck-BP-X classes to select the number of cards per line
@for $i from 2 through $grid-columns {
  $percentage: percentage(1 / $i);
  $calc: calc(#{$percentage} - #{$card-deck-margin * 2});
  .deck-#{$i} {
    // Force flex display, even on small screens
    display: flex;
    flex-flow: row wrap;
    margin-left:  -$card-deck-margin;
    margin-right: -$card-deck-margin;
    & > .card {
      flex-basis: $calc;
      max-width:  $calc;
      // Cards have no left/right margins on small screens by default
      margin-left:  $card-deck-margin;
      margin-right: $card-deck-margin;
    }
  }
}
@each $breakpoint in map-keys($grid-breakpoints) {
  $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
  @include media-breakpoint-up($breakpoint) {
    @for $i from 1 through $grid-columns {
      $percentage: percentage(1 / $i);
      $calc: calc(#{$percentage} - #{$card-deck-margin * 2});
      .deck#{$infix}-#{$i} {
        margin-left:  -$card-deck-margin;
        margin-right: -$card-deck-margin;
        & > .card {
          flex-basis: $calc;
          max-width:  $calc;
          margin-left:  $card-deck-margin;
          margin-right: $card-deck-margin;
        }
      }
    }
  }
}

This code could probably be improved, feel free to ping me with edits if you know how.

Edit 1: Expanded HTML code to show the differences with the existing row/col solution.
Edit 2: Improved SCSS code to better leverage Bootstrap's card-deck default styles and generate the minimum required code.

@SkuterPL
Copy link

@goulvench what's the difference between your version and new version of BS which support row-cols-md-2

@goulvench
Copy link

@SkuterPL the deck-BP-X solution I suggest has two main benefits:

  1. it requires much less HTML (just a card-deck container, with cards as direct children), which is better both in terms of memory usage and for code readability, and
  2. card-footers align with each other, which is not the case with the row/col solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests