You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调整CSS Subgrid右侧列垂直间隙以匹配其他间隙?

React自定义Grid组件垂直间隙不一致问题

我在React中实现了自定义CSS Grid组件,核心问题应该可以仅通过CSS解决。目前遇到的问题是:右侧列的垂直间隙和其他区域不一致——给子元素设置height: fit-content可以避免占用过多空间,但会导致间隙过大;移除该属性后间隙显示正常,但元素会被拉伸占用多余空间。

Grid组件代码

import React, { ReactNode, useCallback, useEffect, useMemo } from "react";
import * as CSS from "csstype";
import { v4 as uuidv4 } from "uuid";
import { useMediaQuery } from "react-responsive";
interface GridProps {
  children: ReactNode;
  style?: React.CSSProperties;
  columns?: number | CSS.Property.GridTemplateColumns;
  smallScreenColumns?: number | CSS.Property.GridTemplateColumns;
  rows?: number | CSS.Property.GridTemplateColumns;
  rowGap?: string;
  columnGap?: string;
  className?: string;
  uniqueClass?: string;
}
interface Gap {
  [key: string]: string;
}

const toCssProperty = (property: string) => {
  return property.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);
};
const stylesToCSS = (styles: React.CSSProperties) =>
  Object.entries(styles)
    .map(([key, value]) => `${toCssProperty(key)}:${value};`)
    .join("");

const Grid = ({
  children,
  className,
  style,
  columns,
  smallScreenColumns,
  rows,
  rowGap,
  columnGap,
  uniqueClass
}: GridProps) => {
  // 检测屏幕尺寸实现响应式
  const hasWideScreen = useMediaQuery({
    query: `(min-width: 796px)`
  });
  // 生成唯一类名
  const uniqueClassName: string = useMemo(
    () => uniqueClass || `grid-${uuidv4().slice(0, 8)}`,
    [uniqueClass]
  );

  // 根据props获取预设间隙值
  const getGap = useCallback((g?: string) => {
    const gaps: Gap[] = [
      { xs: "0.625rem" },
      { s: "0.750rem" },
      { m: "0.875rem" },
      { l: "1rem" },
      { xl: "1.125rem" },
      { xxl: "1.25rem" }
    ];
    if (g) {
      const selectedGap = gaps.find((gap) => gap[g]);
      return selectedGap ? selectedGap[g] : "0";
    }
    return "0";
  }, []);

  const getColumnPattern = useCallback(() => {
    if (!hasWideScreen) {
      return smallScreenColumns || "1fr";
    }
    return columns || "auto";
  }, [columns, hasWideScreen, smallScreenColumns]);
  // 定义Grid核心CSS属性
  const styles: React.CSSProperties = useMemo(
    () => ({
      display: "grid",
      gridTemplateColumns: getColumnPattern(),
      gridTemplateRows: rows || "auto",
      columnGap: getGap(columnGap),
      rowGap: getGap(rowGap),
      ...style
    }),
    [columnGap, getColumnPattern, getGap, rowGap, rows, style]
  );
  // 检查是否已创建对应类名的样式标签
  const hasClassNameCreated = useCallback(() => {
    const documentStyles = document.head.getElementsByTagName("style");
    for (let i = 0; i < documentStyles.length; i += 1) {
      if (documentStyles[i].innerText?.includes(uniqueClassName)) {
        return true;
      }
    }
    return false;
  }, [uniqueClassName]);

  // 在<head>中注入自定义样式
  useEffect(() => {
    const styleElement = document.createElement("style");
    if (!hasClassNameCreated()) {
      styleElement.innerHTML = `.${uniqueClassName} {${stylesToCSS(styles)}}`;
      document.head.appendChild(styleElement);
    }
    return () => {
      if (!uniqueClass) {
        document.head.removeChild(styleElement);
      }
    };
  }, [hasClassNameCreated, styles, uniqueClass, uniqueClassName]);

  return (
    <div className={`${uniqueClassName}${className ? " " + className : ""}`}>
      {children}
    </div>
  );
};

export default Grid;

使用示例代码

import "./styles.css";
import Grid from "./grid";
const App = () => {
  return (
    <div className="App">
      <Grid
        columns="7fr 26fr 11fr"
        smallScreenColumns="7fr 26fr 11fr"
        columnGap="l"
      >
        <div style={{ backgroundColor: "gray", height: "50%" }}>test</div>
        <Grid columns="1fr" smallScreenColumns="1fr" rowGap="l" columnGap="l">
          <Grid
            columns="1fr"
            smallScreenColumns="5fr 2fr"
            rowGap="l"
            columnGap="l"
          >
            <div style={{ backgroundColor: "gray" }}>test</div>
            <div style={{ backgroundColor: "gray" }}>test</div>
          </Grid>
          <div style={{ backgroundColor: "gray" }}>test</div>
          <div style={{ backgroundColor: "gray" }}>test</div>
          <div style={{ backgroundColor: "gray" }}>test</div>
          <div style={{ backgroundColor: "gray" }}>test</div>
          <div style={{ backgroundColor: "gray" }}>test</div>
          <div style={{ backgroundColor: "gray" }}>test</div>
          <div style={{ backgroundColor: "gray" }}>test</div>
          <div style={{ backgroundColor: "gray" }}>test</div>
          <div style={{ backgroundColor: "gray" }}>test</div>
        </Grid>
        <Grid
          columns="subgrid"
          smallScreenColumns="subgrid"
          rowGap="l"
          columnGap="l"
        >
          <div style={{ backgroundColor: "gray", height: "fit-content" }}>
            test
          </div>
          <div style={{ backgroundColor: "gray", height: "fit-content" }}>
            test
          </div>
          <div style={{ backgroundColor: "gray", height: "fit-content" }}>
            test
          </div>
        </Grid>
      </Grid>
    </div>
  );
};

export default App;

当前效果

当前Grid效果

解决方案

问题根源在于:Grid默认的align-items: stretch会让单元格拉伸至和同列其他单元格等高,当子元素设置height: fit-content时,单元格高度仍被拉伸,导致间隙被“挤”得看起来过大。以下两种方案可解决:

方案1:给右侧Grid容器单独设置对齐方式

修改右侧使用subgrid的Grid组件props,添加align-items: start,让子元素在单元格顶部对齐,同时保留height: fit-content:

<Grid
  columns="subgrid"
  smallScreenColumns="subgrid"
  rowGap="l"
  columnGap="l"
  style={{ alignItems: "start" }}
>
  <div style={{ backgroundColor: "gray", height: "fit-content" }}>test</div>
  <div style={{ backgroundColor: "gray", height: "fit-content" }}>test</div>
  <div style={{ backgroundColor: "gray", height: "fit-content" }}>test</div>
</Grid>

方案2:全局修改Grid组件默认对齐方式

如果希望所有Grid容器都采用该行为,可在Grid组件的核心样式中添加align-items: start:

const styles: React.CSSProperties = useMemo(
  () => ({
    display: "grid",
    gridTemplateColumns: getColumnPattern(),
    gridTemplateRows: rows || "auto",
    columnGap: getGap(columnGap),
    rowGap: getGap(rowGap),
    alignItems: "start", // 添加该行设置默认对齐方式
    ...style
  }),
  [columnGap, getColumnPattern, getGap, rowGap, rows, style]
);

内容的提问来源于stack exchange,提问作者Pamwamba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 00:34:53