From d2fdc1f7e46d552b90a2bf9d06ad2efab73d3376 Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Wed, 12 Aug 2026 19:25:23 -0700 Subject: [PATCH 1/2] Add sticky table headers and tidy table docs Add a `.thead-sticky` class so a `` stays visible while the `` scrolls. Add `--table-thead-sticky-top` and `--table-thead-sticky-zindex` tokens to offset fixed headers and control stacking. Document the class and shorten several table headings. --- scss/content/_tables.scss | 13 ++++ site/src/content/docs/content/tables.mdx | 78 ++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 4 deletions(-) diff --git a/scss/content/_tables.scss b/scss/content/_tables.scss index 781d1f2c797e..0e8cb21648de 100644 --- a/scss/content/_tables.scss +++ b/scss/content/_tables.scss @@ -29,6 +29,8 @@ $table-tokens: defaults( --table-hover-color: var(--table-color), --table-hover-bg-factor: 7.5%, --table-hover-bg: color-mix(in srgb, var(--table-color) var(--table-hover-bg-factor), transparent), + --table-thead-sticky-top: 0, + --table-thead-sticky-zindex: var(--z-3), ), $table-tokens ); @@ -176,6 +178,17 @@ $table-striped-columns-order: even !default; } } + // Sticky table headers + // + // Add `.thead-sticky` to a `` to keep it in view while the `` + // scrolls. Set `--table-thead-sticky-top` to offset any fixed headers. + + .thead-sticky { + position: sticky; + top: var(--table-thead-sticky-top); + z-index: var(--table-thead-sticky-zindex); + } + // Responsive tables // // Generate `.table-responsive` classes that act as container query contexts diff --git a/site/src/content/docs/content/tables.mdx b/site/src/content/docs/content/tables.mdx index 5a65188abdbd..8e551902b526 100644 --- a/site/src/content/docs/content/tables.mdx +++ b/site/src/content/docs/content/tables.mdx @@ -205,7 +205,7 @@ Highlight a table row or cell by adding a `.table-active` class. `} /> -## How do the variants and accented tables work? +## Variants explained For the accented tables ([striped rows](#striped-rows), [striped columns](#striped-columns), [hoverable rows](#hoverable-rows), and [active tables](#active-tables)), we used some techniques to make these effects work for all our [table variants](#variants): @@ -225,7 +225,7 @@ Behind the scenes it looks like this: ## Table borders -### Bordered tables +### Bordered Add `.table-bordered` for borders on all sides of the table and cells. @@ -235,7 +235,7 @@ Add `.table-bordered` for borders on all sides of the table and cells. -### Tables without borders +### No borders Add `.table-borderless` for a table without borders. @@ -427,7 +427,7 @@ Border styles, active styles, and table variants are not inherited by nested tab
`} /> -## How nesting works +### How nesting works To prevent *any* styles from leaking to nested tables, we use the child combinator (`>`) selector in our CSS. Since we need to target all the `td`s and `th`s in the `thead`, `tbody`, and `tfoot`, our selector would look pretty long without it. As such, we use the rather odd looking `.table > :not(caption) > * > *` selector to target all `td`s and `th`s of the `.table`, but none of any potential nested tables. @@ -619,6 +619,76 @@ Both `.table-stacked` and `.table-responsive` use container queries, so the `.ta ``` +## Sticky table headers + +Add `.thead-sticky` to a `` to keep it in view while the `` contents scroll. The table needs a scrollable parent, such as a container with a fixed height. Set the `--bs-table-thead-sticky-top` CSS variable to offset any fixed headers or navigation above the table. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#FirstLastHandle
1MarkOtto@mdo
2JacobThornton@fat
3Larry the Bird@twitter
4MarkOtto@mdo
5JacobThornton@fat
6Larry the Bird@twitter
7MarkOtto@mdo
8JacobThornton@fat
9Larry the Bird@twitter
+`} /> + ## Responsive tables Responsive tables allow tables to be scrolled horizontally with ease. Make any table responsive across all viewports by wrapping a `.table` with `.table-responsive`. Or, pick a maximum breakpoint with which to have a responsive table up to by using `.{sm|md|lg|xl|2xl}:table-responsive`. From 479b4d94da36577160bbe70c18883c59d315fd05 Mon Sep 17 00:00:00 2001 From: Mark Otto Date: Fri, 21 Aug 2026 08:50:37 -0700 Subject: [PATCH 2/2] fix(tables): close the sticky header 1px gap Scroll a table with `.thead-sticky`: a 1px strip of the next row shows above the header. `border-collapse` shares that border, so `top: 0` cannot cover it. Offset sticky `top` by the table border width and clip the example in an `overflow-hidden` wrapper. --- scss/content/_tables.scss | 9 +- site/src/content/docs/content/tables.mdx | 138 ++++++++++++----------- 2 files changed, 76 insertions(+), 71 deletions(-) diff --git a/scss/content/_tables.scss b/scss/content/_tables.scss index 0e8cb21648de..b082cd83901a 100644 --- a/scss/content/_tables.scss +++ b/scss/content/_tables.scss @@ -29,7 +29,7 @@ $table-tokens: defaults( --table-hover-color: var(--table-color), --table-hover-bg-factor: 7.5%, --table-hover-bg: color-mix(in srgb, var(--table-color) var(--table-hover-bg-factor), transparent), - --table-thead-sticky-top: 0, + --table-thead-sticky-top: 0px, --table-thead-sticky-zindex: var(--z-3), ), $table-tokens @@ -180,13 +180,14 @@ $table-striped-columns-order: even !default; // Sticky table headers // - // Add `.thead-sticky` to a `` to keep it in view while the `` - // scrolls. Set `--table-thead-sticky-top` to offset any fixed headers. + // Subtract the collapsed cell border so scrolling rows cannot show through a + // 1px gap at the top of the scrollport. .thead-sticky { position: sticky; - top: var(--table-thead-sticky-top); + top: calc(var(--table-thead-sticky-top) - var(--table-border-width, 1px)); z-index: var(--table-thead-sticky-zindex); + background-color: var(--theme-bg-subtle, var(--table-bg)); } // Responsive tables diff --git a/site/src/content/docs/content/tables.mdx b/site/src/content/docs/content/tables.mdx index 8e551902b526..00cd7e8e518e 100644 --- a/site/src/content/docs/content/tables.mdx +++ b/site/src/content/docs/content/tables.mdx @@ -621,73 +621,77 @@ Both `.table-stacked` and `.table-responsive` use container queries, so the `.ta ## Sticky table headers -Add `.thead-sticky` to a `` to keep it in view while the `` contents scroll. The table needs a scrollable parent, such as a container with a fixed height. Set the `--bs-table-thead-sticky-top` CSS variable to offset any fixed headers or navigation above the table. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#FirstLastHandle
1MarkOtto@mdo
2JacobThornton@fat
3Larry the Bird@twitter
4MarkOtto@mdo
5JacobThornton@fat
6Larry the Bird@twitter
7MarkOtto@mdo
8JacobThornton@fat
9Larry the Bird@twitter
-`} /> +Add `.thead-sticky` to a `` to keep it in view while the table scrolls. Wrap the table in a scrollable container with a max height. Do not put `overflow` on the `` itself—browsers keep that value as `visible`. Set `--bs-table-thead-sticky-top` to offset any fixed headers or navigation above the table. + +To prevent additional bleed through from `border-collapse`, we recommend wrapping everything in an extra `overflow-hidden` container. + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#FirstLastHandle
1MarkOtto@mdo
2JacobThornton@fat
3Larry the Bird@twitter
4MarkOtto@mdo
5JacobThornton@fat
6Larry the Bird@twitter
7MarkOtto@mdo
8JacobThornton@fat
9Larry the Bird@twitter
+ + `} /> ## Responsive tables