Bootstrap 4.0 Responsive Number of Cards Per Row For Dynamic Website

Viewed 708

I have an interesting problem. I am dynamically creating Bootstrap cards in my .Net 5.0 app using razor syntax on the UI.

Every 5th card a new row is created, so I have 4 cards visible aligned horizontally to the user.

On most screen sizes this works fine. There are 4 cards, or on smaller screens there is 1 or 2 cards visible aligned horizontally.

The problem is the medium screen, when its too small for 4 cards in a row, but too big to drop down to 2 cards per row. This results in 3 cards visible aligned horizontally to the user, and the 4th drops below them. They are all still inside the same 'row' element.

I can't see how to fix this with class properties, because the rows are created and populated with 4 cards dynamically. I also can't see how to fix it in code, because my razor function does not know what screen size the elements are being generated for? Any advice is much appreciated...

Card Display on Different Screen Sizes

cshtml code:

        <body class="body-bg-img">
        
        <!-- This function checks if the card iteration is divisible by 4 to move to the next row -->
        
            @{
                bool check(int i)
                {
                    var iAsString = i.ToString();
                    int iLength = iAsString.Length;
        
                    if ((iLength == 1 && ((iAsString[0] - '0') % 4 == 0)) || iLength == 0)
                    {
                        return true;
                    }
                    else if (iLength > 1)
                    {
                        int last = iAsString[iLength - 1] - '0';
                        int secondLast = iAsString[iLength - 2] - '0';
        
                        if ((secondLast * 10 + last) % 4 == 0)
                        {
                            return true;
                        }
                        else
                        {
                            return false;
                        }
                    }
                    else
                    {
                        return false;
                    }
                }
        
            }
        
        <!-- This function creates a row and populates the first card, the nested loop then populates 3
    more cards on the same row before breaking out to the main loop again, which creates the next row.  
    The nested loop then populates the next row etc. NOTE: I have [snipped] out some code for posting 
    to make it easier to read. -->
        
            @for (int i = 0; i < Model.MainArticleList.Count(); i++)
            {
                var item = Model.MainArticleList[i];
        
                if (check(i))
                {

                <div class="container-body">
                <div class="row align-content-center justify-content-center">

                <!-- CREATE A NEW CONTAINER WITH ROW & ADD FIRST CARD IN ROW -->
        
                            @if (item.UserIdRef != "none")
                            {
                [snip]
        
                                @if (item.RazorRef != "none")
                                {
                                        <div class="card h-35 border-success m-2" style="max-width: 18rem;">

               [snip] <!-- Bootstrap card properties are being populated here -->

                                        </div>
                                }
                                else if (item.HREFRef != "none")
                                {
                                    <div class="card h-35 border-success m-2" style="max-width: 18rem;">

               [snip] <!-- Just a different card content type -->

                                    </div>
                                }
                            }
        
                            @for (int j = i + 1; j < Model.MainArticleList.Count(); j++)
                            {

               <!-- CREATE CARDS 2, 3 and 4 INSIDE THE EXISTING ROW -->

                                var item1 = Model.MainArticleList[j];
        
                                if (!check(j))
                                {
        
                                    @if (item1.UserIdRef != "none")
                                    {
               [snip]
        
                                        @if (item1.RazorRef != "none")
                                        {
                                                <div class="card h-35 border-success m-2" style="max-width: 18rem;">

               [snip] <!-- Bootstrap card properties are being populated here -->

                                                </div>
                                        }
                                        else if (item1.HREFRef != "none")
                                        {
                                            <div class="card h-35 border-success m-2" style="max-width: 18rem;">

               [snip] <!-- Just a different card content type -->

                                            </div>
                                        }
                                    }
                                }
                                else
                                {
                                    break;
                                }
                            }
                        </div>
                    </div>
                }
            }
            <br />
        </body>
1 Answers

The trick is to not calculate rows and cols yourself and leave this calculation on the CSS level. Today we can do this by using flex-box. This is what Bootstrap 4 is already using with the card-layout card-deck.
How to adjust the data in grid columns in placing the data dynamically from the backend

CSS

.card-deck .card {
    flex-basis: 100%;
}

@media (min-width: 992px) {
    .card-deck .card {
        flex-basis: calc(25% - 30px); /* #{$grid-gutter-width} */
    }
}

Optionally your design requirements should tell you to let a .card grow if it's a left-over card. You control this in CSS by using flex-grow 1 or 0. Usually you set this to 0 with the chance of creating blank space for some resolutions.

DEMO 1 (1 card mobile, 2 small, 3 medium, 4 large)
DEMO 2 (1 card mobile, 3 medium)
DEMO 3 (1 card mobile, 2 medium)

To make it more dynamic from the backend where you can choose how many cols a card-deck has, you can use helper classes to map breakpoints like .two-col, .three-col, .four-col and so on as shown in DEMO 2 and 3.


Please see my other answer if you really must use rows and cols. You can calculate left-overs with the modulus operator => amount % 4. In my experience I managed to pull this off for a three column layout. Four columns will definatly be even more tricky.


Also note on Bootstrap 5 this changes again because they got rid of card-deck. Here you can simply use .row and .col which again is controlled in CSS.
bootstrap 5 alpha 2 card deck example not showing like it is in documentation

Related