1

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>
DMur
  • 625
  • 13
  • 26

1 Answers1

1

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

Tim Vermaelen
  • 6,869
  • 1
  • 25
  • 39
  • Thanks Tim. So just to be clear, I should just have them all on one row and use flex to make them jump to the next row as required on all screen sizes? (So lots of wasted coding effort in my solution! Oops! :D ) – DMur Dec 14 '21 at 14:01
  • 1
    Yes, one row and one col-12. Inside you put your card-deck. If you need a full-width background-color it becomes 1 section with 1 row with 1 col-12 and a container. – Tim Vermaelen Dec 14 '21 at 20:07
  • 1
    Worked a treat, thanks @Tim – DMur Dec 16 '21 at 21:04