1

I have a model that called LinksListWidgetModel that contains an IEnumerable property named Links that contains a list of my website links.

In my Razor view, I have two columns (i.e. one for the grid "col-md-8" and one for another tool "col-md-4". I have a foreach loop that adds each link to the grid tool. I am trying to achieve the following look: Matrix View, however the buttons don't wrap after three, it just continues throughout the width of the page. I am using Bootstrap4.x and flexbox.

I tried following the example in this question: How I can make nice looking matrix of buttons with bootstrap 3?, but that didn't fix the issue either.

Here is my code:

LinksListGrid.cshtml:

@if (Model == null)
{
@Html.Partial("_WidgetNotConfigured") }
else
{
  <div class="btn-group btn-matrix" role="group">
    @foreach (var link in Model.Links)
    {
        <div class="linkContainer-gridItem">
            <button>
                <a class="linkContainer-gridItem-link btn btn-default" href="@link.Url">
                    @if (!string.IsNullOrEmpty(link.Icon))
                    {
                        <div>
                            <i class="icon @link.Icon"></i>
                        </div>
                    }
                    @link.Label
                </a>
                </button>
        </div>
    }
</div>
}

LinkContainer.scss:

.linkContainer {
    padding: 1rem;
    margin-bottom: 2rem;
    position: relative;
    background: #fff;
    border-radius: 8px;
    border: 1px solid #dddfe2;

    .linkContainer-title {
        color: #fff;
        background: $colorBrandDarkBlue;
        padding: 1rem;
        border-radius: 5px;
        margin: -.5rem -.5rem 1rem -.5rem;
        font-weight: 400;
    }

    .linkContainer-list {
        list-style: none;
        padding: 0;
        margin-bottom: 0;
        font-family: "Montserrat",sans-serif;

        .linkContainer-item {
            position: relative;

            .linkContainer-link {
                display: -webkit-box;
                display: -ms-flexbox;
                display: flex;
                padding: .75rem .5rem;
                color: $colorBrandBlue;
                font-weight: 400;
                font-size: 18px;
                -webkit-box-align: center;
                -ms-flex-align: center;
                align-items: center;
                text-decoration: none;
                background-color: transparent;
                font-family: "Montserrat",sans-serif;
            }

            .icon:before {
                font-size: 30px;
                margin-right: 1rem;
            }
        }

        .linkContainer-item:after {
            content: '';
            position: absolute;
            top: 0;
            right: 0;
            left: 0;
            border-top: 1px dotted;
            opacity: .25;
        }
    }

    .linkContainer-grid {
        width: 290px;
        display: block;
        margin: 0 auto;
        padding: 0;
        padding-bottom: 19px;
        font-family: "Montserrat",sans-serif;
        text-align: center;

        .linkContainer-gridItem {
            background-color: $colorBrandBlue;
            border-radius: 25px;
            width: 120px;
            height: 120px;
            margin: 10px;
            display: inline-block;
            vertical-align: top;

            .linkContainer-gridItem-link {
                color: white;
                text-decoration: none;
                display: flex;
                flex-direction: column;
                justify-content: center;
                height: 100%;
                padding: 20px 10px;
            }

            .icon {
                font-size: 30px;
                margin-bottom: 0.25rem;
            }
        }
    }

    @media screen and (max-width: 767px) {
        &.linkContainer-gridLayout {
            padding: 0;
        }
    }
}

.btn-matrix {
    > .btn {
        &:nth-child(3n+4) {
            clear: left;
            margin-left: 0;
        }

        &:nth-child(n+4) {
            margin-top: -1px;
        }

        &:first-child {
            border-bottom-left-radius: 0;
        }

        &:nth-child(3) {
            border-top-right-radius: 4px !important;
        }

        &:nth-last-child(3) {
            border-bottom-left-radius: 4px !important;
        }

        &:last-child {
            border-top-right-radius: 0;
        }
    }
}

It ends up looking like this: Grid View

How can I change this to make it look like the first screenshot's matrix/grid display? After every 3 buttons it goes on the second line.

developer
  • 35
  • 3

1 Answers1

0

I don't think clear:left; will work on flexbox. But there are other ways to do it.

HTML & SCSS way

I think you would be better off just styling .btn-matrix yourself, without mixing .btn-group styles, because Bootstrap .btn-group brings in lots of noice on its .btn children, such as border-radius, border, etc. You would need to style them using your own styles anyway so why bother.

I think the following structure should be generic enough you can use to construct a matrix:

<div class="btn-matrix btn-matrix-white" role="group">
    <a class="btn" href="#">
        <div class="fa-stack fa-2x">
            <i class="fas fa-circle fa-stack-2x" />
            <i class="fas fa-chart-bar fa-stack-1x" />
        </div>
        Reports
    </a>
    ...
</div>

First of all, in your original HTML structure, you had a button that wraps an anchor link. That's not necessary because Bootstrap has classes to style an anchor link to just look like a button.

Secondly, I am using icon stacking method to construct icons with circle backgrounds. You can read about it here: https://fontawesome.com/how-to-use/on-the-web/styling/stacking-icons

Finally, here is the styles:

$numOfMatrixItemPerRow: 3;
$matrixItemBorderWidth: 1px;

.btn-matrix {
    display: flex;
    flex-flow: row wrap;
    box-shadow: 0 0 1rem #ccc;
    border: 1px solid transparent;
    border-radius: .25rem;
    
    > .btn {
        display: flex;
        flex-flow: column nowrap;
        align-items: center;
        justify-content: flex-start;
        flex-grow: 0;
        flex-shrink: 0;
        padding: 5%;
        width: calc(100% / #{$numOfMatrixItemPerRow});
        border-radius: 0;
    }

    &.btn-matrix-white {
        > .btn {
            background-color: #fff;
            border-right: $matrixItemBorderWidth solid #ddd;
            border-bottom: $matrixItemBorderWidth solid #ddd;
            
            .fa-stack-2x {
                color: var(--primary);
            }
            
            .fa-stack-1x {
                color: #fff;
            }
            
            &:nth-child(#{$numOfMatrixItemPerRow}n) {
                border-right: none !important;
            }
            
            &:last-child {
                border-bottom: none !important;
            }
        }
    }
}

Really nothing tricky there except you would need to use SASS Interpolation to involve $numOfMatrixItemPerRow variable into calculations:

  • Display .btn-matrix as a wrappable flex row
  • Set the width of each button to be 100% / $numOfMatrixItemPerRow so that each row will contain the exact number of items before breaking into new rows
  • Display .btn as flexbox column so that you can easily align the icons and text there
  • Set the right and bottom border of each button, with exceptions of the last one on each row and last one

enter image description here

demo: https://jsfiddle.net/davidliang2008/1wqost69/201/


ASP.NET MVC way

Now since you're using ASP.NET MVC, I think there is another approach you can consider. That is, in the loop where you loop though each link from the model, you can define a variable there and do something like:

<div class="btn-matrix">
    @for (int i = 0; i < Model.Links.Count(); i++)
    {
        var link = Model.Links[i];

        if (i % numOfItemsPerRow == 0)
        {
            <div class="row">
        }
        <div class="col-md-4">
            <a class="btn" href="@link.Url">
                @if (!string.IsNullOrEmpty(link.Icon))
                {
                    <div>
                        <i class="icon @link.Icon"></i>
                    </div>
                }
                @link.Label
            </a>
        </div>
        if ((i+1) % numOfItemsPerRow == 0 || i + 1 == Model.Links.Count())
        {
            </div>
        }
    }
</div>

I'm just making things up but hopefully you see where I am going.

David Liang
  • 20,385
  • 6
  • 44
  • 70