twbs / bootstrap

The most popular HTML, CSS, and JavaScript framework for developing responsive, mobile first projects on the web.
https://getbootstrap.com
MIT License
170.18k stars 78.78k forks source link

Grid System should support CSS Container Queries for responsive column sizing #37807

Open NateRadebaugh opened 1 year ago

NateRadebaugh commented 1 year ago

Prerequisites

Proposal

CSS Container Queries are becoming widely supported.

Proposal: _breakpoints.scss should be extended to be configurable to use container queries, perhaps defaulting to the body or falling back to media queries where appropriate for backwards compatibility.

Motivation and context

I have pages that can be displayed in either modal or full-page and currently there isn't a good way to wire up this sort of functionality.

Layouts with collapsible side navigation or modal dialogs would benefit from Container Queries instead of using normal Media Queries for responsive widths.

goulvench commented 1 year ago

I'm having the same issue, with grid layouts that get squished when rendered inside modals. Here's a quick hack that just stacks grid columns inside modals:

// Stack grid columns when displayed in a modal
@each $breakpoint in map-keys($grid-breakpoints) {
  $infix: breakpoint-infix($breakpoint, $grid-breakpoints);
  @for $i from 1 through $grid-columns {
    .modal .col#{$infix}-#{$i} {
      width: 100%;
    }
  }
}

Adding this, any .col-BP-I class inside a modal will just stretch to full-width. It's a bit brutal but it works well. And it doesn't even need container queries, making it compatible with older browsers 😉

NateRadebaugh commented 1 year ago

Thanks @goulvench this is a nice workaround, but doesn't fully solve the need. For instance, if we have a shared component inside one of bootstrap's various modal sizes: https://getbootstrap.com/docs/5.3/components/modal/#optional-sizes

An additional scenario I'm trying to support is a layout with a side nav menu that can be expanded or collapsed. I'd like the grid system to be based on the width of the "main" component rather than the whole page width.

Joniras commented 1 year ago

I solved my problem (which was that i wanted to be responsive against the size of my content-container) with patch-package with the following patch (named patches/bootstrap+5.2.3.patch) :

The following Code does not set breakpoints relative to @media but relative to the element you add a specific class to.

diff --git a/node_modules/bootstrap/scss/mixins/_breakpoints.scss b/node_modules/bootstrap/scss/mixins/_breakpoints.scss
index 286be89..f80a7ea 100644
--- a/node_modules/bootstrap/scss/mixins/_breakpoints.scss
+++ b/node_modules/bootstrap/scss/mixins/_breakpoints.scss
@@ -61,7 +61,7 @@
 @mixin media-breakpoint-up($name, $breakpoints: $grid-breakpoints) {
   $min: breakpoint-min($name, $breakpoints);
   @if $min {
-    @media (min-width: $min) {
+    @container #{$containerName} (min-width: #{$min}) {
       @content;
     }
   } @else {
@@ -74,7 +74,7 @@
 @mixin media-breakpoint-down($name, $breakpoints: $grid-breakpoints) {
   $max: breakpoint-max($name, $breakpoints);
   @if $max {
-    @media (max-width: $max) {
+    @container #{$containerName} (max-width: #{$max}) {
       @content;
     }
   } @else {
@@ -89,7 +89,7 @@
   $max: breakpoint-max($upper, $breakpoints);

   @if $min != null and $max != null {
-    @media (min-width: $min) and (max-width: $max) {
+    @container #{$containerName} (min-width: #{$min}) and (max-width: #{$max}) {
       @content;
     }
   } @else if $max == null {
@@ -112,7 +112,7 @@
   $max:  breakpoint-max($next, $breakpoints);

   @if $min != null and $max != null {
-    @media (min-width: $min) and (max-width: $max) {
+    @container #{$containerName} (min-width: #{$min}) and (max-width: #{$max}) {
       @content;
     }
   } @else if $max == null {

and an additional styling:


$containerName: mainSection;

// to have a fallback if certain elements are not inside the .breakpoint-container class (like modals or notifications)
body{
  container-type: inline-size;
  container-name: $containerName;
}

.breakpoint-container {
  container-type: inline-size;
  container-name: $containerName;
}

and used like this:

...
<app-sidebar ></app-sidebar>
  <div>
    <app-header></app-header>
    <section class="breakpoint-container">
      ...
    </section>
    <app-footer ></app-footer>
  </div>
...

But i hope it gets built in in some form!

EDIT: added fallback to body container

Schwarcelo commented 1 year ago

@Joniras where did you add the additional styling? Could you create a Repository or something like that for your fix?

Thank you!

Joniras commented 1 year ago

@Schwarcelo The additional styling has to be applied on a global level. It has to be included before the bootstrap scss because the scss variable is defined in the additional styling and used in the (changed) bootstrap mixins.

Moongazer commented 10 months ago

+1 for that feature. E.g. creating cards or square-tiles holding lots of content (e.g. header, sub-header, text, buttons, ...) where the font-size should be scaled beside being rendered in 1-4 responsive columns depending on the parents width, is a huge mess just using breakpoints.

lana-white commented 8 months ago

Bootstrap are in dire need of adding this, it is such a basic necessity, that columns should be based on the container that they're in and not the device width. iFrames, modals, even just adding in an aside to a page stuffs up all of your bootstrap responsive classes.

### PLEASE ADD THIS IN ASAP!!!!!!!!!!!!!!!!!!!!!!

manstie commented 6 months ago

I have added this to my scss for the time being:

.cq {
    display: flex;
    flex-wrap: wrap;
    container-type: inline-size;

    & > * {
        flex: 0 0 auto;
        width: 100cqw;
    }
}

@each $infix, $breakpoint in $grid-breakpoints {
    @container (min-width: #{$breakpoint}) {
        .cq-#{$infix}-1 {
            width: 8.33333333cqw;
        }

        .cq-#{$infix}-2 {
            width: 16.66666667cqw;
        }

        .cq-#{$infix}-3 {
            width: 25cqw;
        }

        .cq-#{$infix}-4 {
            width: 33.33333333cqw;
        }

        .cq-#{$infix}-5 {
            width: 41.66666667cqw;
        }

        .cq-#{$infix}-6 {
            width: 50cqw;
        }

        .cq-#{$infix}-7 {
            width: 58.33333333cqw;
        }

        .cq-#{$infix}-8 {
            width: 66.66666667cqw;
        }

        .cq-#{$infix}-9 {
            width: 75cqw;
        }

        .cq-#{$infix}-10 {
            width: 83.33333333cqw;
        }

        .cq-#{$infix}-11 {
            width: 91.66666667cqw;
        }

        .cq-#{$infix}-12 {
            width: 100cqw;
        }
    }
}

so you can use it like row/col:

<div class="cq">
    <div class="cq-sm-6 cq-lg-4">Hello</div>
    <div class="cq-sm-6 cq-lg-4">Hello</div>
    <div class="cq-sm-6 cq-lg-4">Hello</div>
</div>
bastienmoulia commented 5 months ago

I have started a repo to try implementing container queries in the bootstrap way, but it's still work in progress: https://github.com/bastienmoulia/bootstrap-container-queries PR welcomed 😉