如何调整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默认的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
相关产品推荐
相关产品推荐

