diff --git a/packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap b/packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap
index 8978f5123b5e..dc44850c5cb2 100644
--- a/packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap
+++ b/packages/tailwindcss/src/__snapshots__/intellisense.test.ts.snap
@@ -2327,6 +2327,15 @@ exports[`getVariants 1`] = `
       "sm",
     ],
   },
+  {
+    "hasDash": true,
+    "isArbitrary": true,
+    "name": "max-h",
+    "selectors": [Function],
+    "values": [
+      "sm",
+    ],
+  },
   {
     "hasDash": true,
     "isArbitrary": false,
@@ -2343,6 +2352,22 @@ exports[`getVariants 1`] = `
       "sm",
     ],
   },
+  {
+    "hasDash": true,
+    "isArbitrary": false,
+    "name": "h-sm",
+    "selectors": [Function],
+    "values": [],
+  },
+  {
+    "hasDash": true,
+    "isArbitrary": true,
+    "name": "min-h",
+    "selectors": [Function],
+    "values": [
+      "sm",
+    ],
+  },
   {
     "hasDash": true,
     "isArbitrary": true,
@@ -2368,6 +2393,27 @@ exports[`getVariants 1`] = `
       "4",
     ],
   },
+  {
+    "hasDash": true,
+    "isArbitrary": true,
+    "name": "@max-h",
+    "selectors": [Function],
+    "values": [],
+  },
+  {
+    "hasDash": true,
+    "isArbitrary": true,
+    "name": "@-h",
+    "selectors": [Function],
+    "values": [],
+  },
+  {
+    "hasDash": true,
+    "isArbitrary": true,
+    "name": "@min-h",
+    "selectors": [Function],
+    "values": [],
+  },
   {
     "hasDash": true,
     "isArbitrary": false,
diff --git a/packages/tailwindcss/src/variants.test.ts b/packages/tailwindcss/src/variants.test.ts
index 18ef39eeb459..ec4c7711d5db 100644
--- a/packages/tailwindcss/src/variants.test.ts
+++ b/packages/tailwindcss/src/variants.test.ts
@@ -998,7 +998,7 @@ test('sorting stacked min-* and max-* variants', () => {
         }
         @tailwind utilities;
       `,
-      ['min-sm:max-xl:flex', 'min-md:max-xl:flex', 'min-xs:max-xl:flex'],
+      ['min-sm:max-xl:flex', 'min-md:max-xl:flex', 'min-xs:max-xl:flex', 'min-h-xs:max-xl:flex'],
     ),
   ).toMatchInlineSnapshot(`
     ":root {
@@ -1031,6 +1031,14 @@ test('sorting stacked min-* and max-* variants', () => {
           display: flex;
         }
       }
+    }
+
+    @media (width < 1280px) {
+      @media (height >= 280px) {
+        .min-h-xs\\:max-xl\\:flex {
+          display: flex;
+        }
+      }
     }"
   `)
 })
@@ -1062,6 +1070,21 @@ test('min, max and unprefixed breakpoints', () => {
         'min-sm:flex',
         'sm:flex',
         'lg:flex',
+        'h-sm:flex',
+        'h-lg:flex',
+        'min-h-lg:flex',
+        'max-h-lg:flex',
+        'h-sm:md:flex',
+        'h-lg:min-md:flex',
+        'h-lg:max-lg:flex',
+        'min-h-lg:md:flex',
+        'max-h-lg:lg:flex',
+        'min-h-lg:max-md:flex',
+        'max-h-lg:min-lg:flex',
+        'min-lg:max-h-md:flex',
+        'max-lg:min-h-lg:flex',
+        'min-[900px]:max-h-[700px]:flex',
+        'max-[700px]:min-h-[900px]:flex',
       ],
     ),
   ).toMatchInlineSnapshot(`
@@ -1095,6 +1118,12 @@ test('min, max and unprefixed breakpoints', () => {
       }
     }
 
+    @media (height < 1024px) {
+      .max-h-lg\\:flex {
+        display: flex;
+      }
+    }
+
     @media (width >= 640px) {
       .min-sm\\:flex {
         display: flex;
@@ -1125,16 +1154,122 @@ test('min, max and unprefixed breakpoints', () => {
       }
     }
 
+    @media (height < 700px) {
+      @media (width >= 900px) {
+        .min-\\[900px\\]\\:max-h-\\[700px\\]\\:flex {
+          display: flex;
+        }
+      }
+    }
+
     @media (width >= 1024px) {
       .lg\\:flex {
         display: flex;
       }
     }
 
+    @media (width >= 1024px) {
+      @media (height < 1024px) {
+        .max-h-lg\\:lg\\:flex {
+          display: flex;
+        }
+      }
+    }
+
     @media (width >= 1024px) {
       .min-lg\\:flex {
         display: flex;
       }
+    }
+
+    @media (width >= 1024px) {
+      @media (height < 1024px) {
+        .max-h-lg\\:min-lg\\:flex {
+          display: flex;
+        }
+      }
+    }
+
+    @media (height < 768px) {
+      @media (width >= 1024px) {
+        .min-lg\\:max-h-md\\:flex {
+          display: flex;
+        }
+      }
+    }
+
+    @media (height >= 1024px) {
+      .h-lg\\:flex {
+        display: flex;
+      }
+    }
+
+    @media (width < 1024px) {
+      @media (height >= 1024px) {
+        .h-lg\\:max-lg\\:flex {
+          display: flex;
+        }
+      }
+    }
+
+    @media (width >= 768px) {
+      @media (height >= 1024px) {
+        .h-lg\\:min-md\\:flex {
+          display: flex;
+        }
+      }
+    }
+
+    @media (height >= 640px) {
+      .h-sm\\:flex {
+        display: flex;
+      }
+    }
+
+    @media (width >= 768px) {
+      @media (height >= 640px) {
+        .h-sm\\:md\\:flex {
+          display: flex;
+        }
+      }
+    }
+
+    @media (height >= 900px) {
+      @media (width < 700px) {
+        .max-\\[700px\\]\\:min-h-\\[900px\\]\\:flex {
+          display: flex;
+        }
+      }
+    }
+
+    @media (height >= 1024px) {
+      .min-h-lg\\:flex {
+        display: flex;
+      }
+    }
+
+    @media (height >= 1024px) {
+      @media (width < 1024px) {
+        .max-lg\\:min-h-lg\\:flex {
+          display: flex;
+        }
+      }
+    }
+
+    @media (width < 768px) {
+      @media (height >= 1024px) {
+        .min-h-lg\\:max-md\\:flex {
+          display: flex;
+        }
+      }
+    }
+
+    @media (width >= 768px) {
+      @media (height >= 1024px) {
+        .min-h-lg\\:md\\:flex {
+          display: flex;
+        }
+      }
     }"
   `)
 })
@@ -1570,6 +1705,21 @@ test('container queries', () => {
         '@max-lg/name:flex',
         '@max-[123px]:flex',
         '@max-[456px]/name:flex',
+
+        '@h-lg:flex',
+        '@h-lg/name:flex',
+        '@h-[123px]:flex',
+        '@h-[456px]/name:flex',
+
+        '@min-h-lg:flex',
+        '@min-h-lg/name:flex',
+        '@min-h-[123px]:flex',
+        '@min-h-[456px]/name:flex',
+
+        '@max-h-lg:flex',
+        '@max-h-lg/name:flex',
+        '@max-h-[123px]:flex',
+        '@max-h-[456px]/name:flex',
       ],
     ),
   ).toMatchInlineSnapshot(`
@@ -1647,6 +1797,30 @@ test('container queries', () => {
       .\\@min-lg\\/name\\:flex {
         display: flex;
       }
+    }
+
+    @container name (height < 456px) {
+      .\\@max-h-\\[456px\\]\\/name\\:flex {
+        display: flex;
+      }
+    }
+
+    @container (height < 123px) {
+      .\\@max-h-\\[123px\\]\\:flex {
+        display: flex;
+      }
+    }
+
+    @container (height >= 123px) {
+      .\\@min-h-\\[123px\\]\\:flex {
+        display: flex;
+      }
+    }
+
+    @container name (height >= 456px) {
+      .\\@min-h-\\[456px\\]\\/name\\:flex {
+        display: flex;
+      }
     }"
   `)
 })
diff --git a/packages/tailwindcss/src/variants.ts b/packages/tailwindcss/src/variants.ts
index 25a37c9489af..82914c535694 100644
--- a/packages/tailwindcss/src/variants.ts
+++ b/packages/tailwindcss/src/variants.ts
@@ -548,6 +548,27 @@ export function createVariants(theme: Theme): Variants {
         () => Array.from(breakpoints.keys()).filter((key) => key !== null) as string[],
       )
 
+      variants.group(
+        () => {
+          variants.functional(
+            'max-h',
+            (ruleNode, variant) => {
+              let value = resolvedBreakpoints.get(variant)
+              if (value === null) return null
+
+              ruleNode.nodes = [rule(`@media (height < ${value})`, ruleNode.nodes)]
+            },
+            { compounds: false },
+          )
+        },
+        (a, z) => compareBreakpoints(a, z, 'desc', resolvedBreakpoints),
+      )
+
+      variants.suggest(
+        'max-h',
+        () => Array.from(breakpoints.keys()).filter((key) => key !== null) as string[],
+      )
+
       // Min
       variants.group(
         () => {
@@ -581,6 +602,39 @@ export function createVariants(theme: Theme): Variants {
         'min',
         () => Array.from(breakpoints.keys()).filter((key) => key !== null) as string[],
       )
+
+      variants.group(
+        () => {
+          // Registers breakpoint variants like `h-sm`, `h-md`, `h-lg`, etc.
+          for (let [key, value] of theme.namespace('--breakpoint')) {
+            if (key === null) continue
+            variants.static(
+              `h-${key}`,
+              (ruleNode) => {
+                ruleNode.nodes = [rule(`@media (height >= ${value})`, ruleNode.nodes)]
+              },
+              { compounds: false },
+            )
+          }
+
+          variants.functional(
+            'min-h',
+            (ruleNode, variant) => {
+              let value = resolvedBreakpoints.get(variant)
+              if (value === null) return null
+
+              ruleNode.nodes = [rule(`@media (height >= ${value})`, ruleNode.nodes)]
+            },
+            { compounds: false },
+          )
+        },
+        (a, z) => compareBreakpoints(a, z, 'asc', resolvedBreakpoints),
+      )
+
+      variants.suggest(
+        'min-h',
+        () => Array.from(breakpoints.keys()).filter((key) => key !== null) as string[],
+      )
     }
 
     {
@@ -685,6 +739,109 @@ export function createVariants(theme: Theme): Variants {
         () => Array.from(widths.keys()).filter((key) => key !== null) as string[],
       )
     }
+
+    {
+      let heights = theme.namespace('--height')
+
+      // Container queries
+      let resolvedHeights = new DefaultMap((variant: Variant) => {
+        switch (variant.kind) {
+          case 'functional': {
+            if (variant.value === null) return null
+
+            let value: string | null = null
+
+            if (variant.value.kind === 'arbitrary') {
+              value = variant.value.value
+            } else if (variant.value.kind === 'named') {
+              value = theme.resolveValue(variant.value.value, ['--height'])
+            }
+
+            if (!value) return null
+            if (value.includes('var(')) return null
+
+            return value
+          }
+          case 'static':
+          case 'arbitrary':
+          case 'compound':
+            return null
+        }
+      })
+
+      variants.group(
+        () => {
+          variants.functional(
+            '@max-h',
+            (ruleNode, variant) => {
+              let value = resolvedHeights.get(variant)
+              if (value === null) return null
+
+              ruleNode.nodes = [
+                rule(
+                  variant.modifier
+                    ? `@container ${variant.modifier.value} (height < ${value})`
+                    : `@container (height < ${value})`,
+                  ruleNode.nodes,
+                ),
+              ]
+            },
+            { compounds: false },
+          )
+        },
+        (a, z) => compareBreakpoints(a, z, 'desc', resolvedHeights),
+      )
+
+      variants.suggest(
+        '@max-h',
+        () => Array.from(heights.keys()).filter((key) => key !== null) as string[],
+      )
+
+      variants.group(
+        () => {
+          variants.functional(
+            '@-h',
+            (ruleNode, variant) => {
+              let value = resolvedHeights.get(variant)
+              if (value === null) return null
+
+              ruleNode.nodes = [
+                rule(
+                  variant.modifier
+                    ? `@container ${variant.modifier.value} (height >= ${value})`
+                    : `@container (height >= ${value})`,
+                  ruleNode.nodes,
+                ),
+              ]
+            },
+            { compounds: false },
+          )
+          variants.functional(
+            '@min-h',
+            (ruleNode, variant) => {
+              let value = resolvedHeights.get(variant)
+              if (value === null) return null
+
+              ruleNode.nodes = [
+                rule(
+                  variant.modifier
+                    ? `@container ${variant.modifier.value} (height >= ${value})`
+                    : `@container (height >= ${value})`,
+                  ruleNode.nodes,
+                ),
+              ]
+            },
+            { compounds: false },
+          )
+        },
+        (a, z) => compareBreakpoints(a, z, 'asc', resolvedHeights),
+      )
+
+      variants.suggest(
+        '@min-h',
+        () => Array.from(heights.keys()).filter((key) => key !== null) as string[],
+      )
+    }
   }
 
   staticVariant('portrait', ['@media (orientation: portrait)'], { compounds: false })