Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scss/_dialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ $dialog-sizes: defaults(
--dialog-width: 100vw;
--dialog-margin: 0;
--dialog-border-radius: 0;
--dialog-border-width: 0;

width: 100%;
max-width: none;
Expand Down
4 changes: 3 additions & 1 deletion scss/layout/_grid.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@use "../config" as *;
@use "breakpoints" as *;
@use "../mixins/grid" as *;

// mdo-do
Expand All @@ -23,11 +24,12 @@
--rows: 1;
--gap: #{$grid-gutter-x};

@include set-container();

display: grid;
grid-template-rows: repeat(var(--rows), 1fr);
grid-template-columns: repeat(var(--columns), 1fr);
gap: var(--gap);

}

@include make-cssgrid();
Expand Down
2 changes: 1 addition & 1 deletion scss/mixins/_grid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
@each $breakpoint in map.keys($breakpoints) {
$prefix: breakpoint-prefix($breakpoint, $breakpoints);

@include media-breakpoint-up($breakpoint, $breakpoints) {
@include container-breakpoint-up($breakpoint, $breakpoints: $breakpoints) {
@if $columns > 0 {
@for $i from 1 through $columns {
.#{$prefix}g-col-#{$i} {
Expand Down
55 changes: 54 additions & 1 deletion site/src/components/shortcodes/ResizableExample.astro
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ interface Props {
* @default true
*/
showMarkup?: boolean
/**
* Whether to show a floating badge indicating the active container query
* breakpoint for the resized container.
* @default false
*/
showBreakpoint?: boolean
}

const {
Expand All @@ -38,7 +44,8 @@ const {
className,
initialWidth = '100%',
minWidth = '200px',
showMarkup = true
showMarkup = true,
showBreakpoint = false
} = Astro.props

// Support both class and className props
Expand Down Expand Up @@ -69,7 +76,53 @@ const simplifiedMarkup = markup.replace(
style={`width: ${initialWidth}; min-width: ${minWidth};`}
>
<Fragment set:html={previewMarkup} />
{showBreakpoint && <output class="bd-resizable-badge" data-resizable-badge aria-live="polite" />}
</div>
</div>
{showMarkup && <Code code={simplifiedMarkup} lang="html" nestedInExample={true} />}
</div>

<script>
// Breakpoints mirror `$breakpoints` in scss/_config.scss. Container queries are
// evaluated against the content-box inline size of the nearest query container.
const breakpoints = [
{ name: 'xs', min: 0 },
{ name: 'sm', min: 576 },
{ name: 'md', min: 768 },
{ name: 'lg', min: 1024 },
{ name: 'xl', min: 1280 },
{ name: '2xl', min: 1536 }
]

function activeBreakpoint(width: number) {
let active = breakpoints[0]
for (const bp of breakpoints) {
if (width >= bp.min) active = bp
}
return active
}

document.querySelectorAll<HTMLElement>('[data-resizable-badge]').forEach((badge) => {
const container = badge.closest<HTMLElement>('.bd-resizable-container')
if (!container) return

// Measure the actual query container (e.g. `.grid`) when present so the badge
// matches what `@container` evaluates; otherwise fall back to the container.
const target = container.querySelector<HTMLElement>(':scope > *:not(.bd-resizable-badge)') ?? container

const update = (width: number) => {
const bp = activeBreakpoint(width)
badge.dataset.breakpoint = bp.name
badge.textContent = `${bp.name} · ${Math.round(width)}px`
}

const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const inlineSize = entry.contentBoxSize?.[0]?.inlineSize ?? entry.contentRect.width
update(inlineSize)
}
})

observer.observe(target, { box: 'content-box' })
})
</script>
61 changes: 56 additions & 5 deletions site/src/content/docs/layout/css-grid.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
title: CSS Grid
description: Learn how to enable, use, and customize our alternate layout system built on CSS Grid with examples and code snippets.
toc: true
js: required
csstricks:
url: https://css-tricks.com/snippets/css/complete-guide-grid/
label: CSS Grid Guide
Expand All @@ -25,7 +26,7 @@ Bootstrap includes an optional grid system built on CSS Grid, but with a Bootstr

- **Columns and gutter sizes are set via CSS variables.** Set these on the parent `.grid` and customize however you want, inline or in a stylesheet, with `--bs-columns` and `--bs-gap`.

In the future, Bootstrap will likely shift to a hybrid solution as the `gap` property has achieved nearly full browser support for flexbox.
- **Responsive classes use container queries.** Each `.grid` is a query container (`container-type: inline-size`), so responsive `.g-col-*` and `.g-start-*` classes respond to the width of the `.grid` itself rather than the viewport.

## Key differences

Expand All @@ -45,7 +46,7 @@ Compared to the default grid system:

### Three columns

Three equal-width columns across all viewports and devices can be created by using the `.g-col-4` classes. Add [responsive classes](#responsive) to change the layout by viewport size.
Three equal-width columns across all container sizes and devices can be created by using the `.g-col-4` classes. Add [responsive classes](#responsive) to change the layout by the `.grid` container’s width.

<Example class="bd-example-cssgrid" code={`<div class="grid text-center">
<div class="g-col-4">.g-col-4</div>
Expand All @@ -61,21 +62,71 @@ Three equal-width columns across all viewports and devices can be created by usi

### Responsive

Use responsive classes to adjust your layout across viewports. Here we start with two columns on the narrowest viewports, and then grow to three columns on medium viewports and above.
Use responsive classes to adjust your layout across breakpoints. Here we start with two columns on the narrowest containers, and then grow to three columns on medium containers and above. Drag the example’s handle to resize the `.grid` and watch the columns reflow based on its width rather than the viewport.

<Example class="bd-example-cssgrid" code={`<div class="grid text-center">
<Callout>
Responsive `.g-col-*` and `.g-start-*` classes are powered by [container queries](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries) rather than media queries. The `.grid` is set as a query container (`container-type: inline-size`), so its grid items respond to the width of the `.grid` itself instead of the viewport. This means a `.grid` placed inside a narrow column will switch layouts based on that column’s width, not the size of the browser window.
</Callout>

<ResizableExample class="bd-example-cssgrid" showBreakpoint code={`<div class="grid text-center">
<div class="g-col-6 md:g-col-4">.g-col-6 .md:g-col-4</div>
<div class="g-col-6 md:g-col-4">.g-col-6 .md:g-col-4</div>
<div class="g-col-6 md:g-col-4">.g-col-6 .md:g-col-4</div>
</div>`} />

Compare that to this two column layout at all viewports.
Compare that to this two column layout at all container sizes.

<Example class="bd-example-cssgrid" code={`<div class="grid text-center">
<div class="g-col-6">.g-col-6</div>
<div class="g-col-6">.g-col-6</div>
</div>`} />

### Playground

The examples above are constrained by the width of this page, so you can only resize them up to the `sm` range before running out of room. Open the fullscreen demo below to drag a set of grids across the full range of breakpoints and watch each layout reflow based on the container's width rather than the viewport.

<button type="button" class="btn-solid theme-primary" data-bs-toggle="dialog" data-bs-target="#gridPlaygroundDialog">
Open fullscreen grid demo
</button>

<dialog class="dialog dialog-fullscreen dialog-scrollable" id="gridPlaygroundDialog">
<div class="dialog-header">
<h1 class="dialog-title">CSS Grid container queries</h1>
<CloseButton dismiss="dialog" />
</div>
<div class="dialog-body">
<p>Drag the handle on the right edge of the example to resize it. The badge shows the active container-query breakpoint, and every grid below responds to the example's width&mdash;not the size of the browser window.</p>
<ResizableExample class="bd-example-cssgrid" showBreakpoint showMarkup={false} code={`<div class="grid text-center">
<div class="g-col-12 sm:g-col-6 md:g-col-4 lg:g-col-3 xl:g-col-2">cell</div>
<div class="g-col-12 sm:g-col-6 md:g-col-4 lg:g-col-3 xl:g-col-2">cell</div>
<div class="g-col-12 sm:g-col-6 md:g-col-4 lg:g-col-3 xl:g-col-2">cell</div>
<div class="g-col-12 sm:g-col-6 md:g-col-4 lg:g-col-3 xl:g-col-2">cell</div>
<div class="g-col-12 sm:g-col-6 md:g-col-4 lg:g-col-3 xl:g-col-2">cell</div>
<div class="g-col-12 sm:g-col-6 md:g-col-4 lg:g-col-3 xl:g-col-2">cell</div>
</div>
<div class="grid text-center">
<div class="g-col-6 md:g-col-3">card</div>
<div class="g-col-6 md:g-col-3">card</div>
<div class="g-col-6 md:g-col-3">card</div>
<div class="g-col-6 md:g-col-3">card</div>
</div>
<div class="grid text-center">
<div class="g-col-12 lg:g-col-3">sidebar</div>
<div class="g-col-12 lg:g-col-9">main content</div>
</div>
<div class="grid text-center">
<div class="g-col-12 lg:g-col-8">primary</div>
<div class="g-col-12 lg:g-col-4">secondary</div>
</div>
<div class="grid text-center">
<div class="g-col-12 md:g-col-6 md:g-start-4">centered with .md:g-start-4</div>
</div>`} />
</div>
<div class="dialog-footer">
<button type="button" class="btn-solid theme-secondary" data-bs-dismiss="dialog">Close</button>
</div>
</dialog>

## Wrapping

Grid items automatically wrap to the next line when there’s no more room horizontally. Note that the `gap` applies to horizontal and vertical gaps between grid items.
Expand Down
17 changes: 17 additions & 0 deletions site/src/scss/_component-examples.scss
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@
}

.bd-resizable-container {
position: relative;
max-width: 100%;
padding: 1rem;
overflow: hidden;
Expand All @@ -372,6 +373,22 @@
@include border-radius(var(--radius-5));
}

.bd-resizable-badge {
position: absolute;
inset-block-start: .5rem;
inset-inline-end: .5rem;
z-index: 2;
padding: .125rem .5rem;
font-family: var(--bs-font-mono);
font-size: .75rem;
line-height: 1.5;
color: var(--bs-fg-2);
pointer-events: none;
background-color: var(--bs-bg-body);
border: 1px solid var(--bs-border-color);
@include border-radius(var(--radius-3));
}

//
// Code snippets
//
Expand Down