直接用 repeat(7, 1fr) 不够,因每月1号未必是周一,需用 grid-column-start 动态定位首日,并生成42格、前n格占位;小屏应切单列布局,用 grid-template-areas 响应式控制;跨月日期须靠语义化 class(如 is-prev-month)隔离样式;minmax(40px, 1fr) 比固定高度更适配多端。

为什么直接用 grid-template-columns: repeat(7, 1fr) 不够用
日历必须对齐周一到周日,但每月 1 号不一定落在周一,所以单纯等分 7 列会错位。Grid 本身不提供“自动跳过前置空白格”的逻辑,得靠内容填充或行定位来补空。常见错误是硬写 6 行 × 7 列(42 格),结果某月只有 35 天却留了 7 个空格在末尾,视觉上像多了一周。
正确做法是:用 grid-auto-flow: row 让日期项自动填入,配合 grid-column-start 控制第一天的起始列。例如 1 号是周三,就让第一个 date-cell 的 grid-column-start 设为 3。
- 计算每月 1 号是星期几(
new Date(year, month, 1).getDay(),注意 JS 中 Sunday=0,需映射为 Monday=1 → Sunday=7) - 生成 42 个格子(6 行 × 7 列),前
n个设为空白占位,从第n+1个开始填日期 - 避免用
grid-row-start硬推,容易破坏自动流;优先用grid-column-start+grid-row组合控制位置
如何让日历在小屏上折叠成单列滚动
响应式不是简单加个 @media 改 grid-template-columns 就完事。手机端如果还保持 7 列,字体挤成一条线,点击区域太小。更合理的是切换为单列布局,每天一行,保留头部星期栏固定。
关键点在于:用 display: grid + grid-template-areas 定义结构,再用媒体查询重定义区域划分。比如桌面端:
立即学习“前端免费学习笔记(深入)”;
.calendar {
display: grid;
grid-template-areas:
"header header header header header header header"
"mon tue wed thu fri sat sun";
}
移动端可改为:
@media (max-width: 480px) {
.calendar {
grid-template-areas:
"header"
"mon"
"tue"
"wed"
"thu"
"fri"
"sat"
"sun";
}
}
- 星期头(
mon~sun)用grid-area显式命名,方便媒体查询中复用 - 避免用
grid-column: span 7做 header,它在单列下会撑宽整个容器;改用grid-area: header+grid-column: 1 / -1 - 滚动体验要加
overflow-y: auto和max-height,否则长月(如含周末的 6 行)会把页面顶出去
怎么处理跨月日期的样式隔离
日历常需灰掉上月/下月日期(如 3 月 30、31 日属于 4 月前缀,显示为浅灰)。但 Grid 容器内所有格子 DOM 结构一致,仅靠位置无法区分所属月份——得靠数据驱动 class。
推荐在渲染时给每个格子加语义化 class:is-current-month、is-prev-month、is-next-month。CSS 里用属性选择器精准控制:
.date-cell.is-prev-month { opacity: 0.4; }
.date-cell.is-next-month { opacity: 0.4; }
.date-cell.is-current-month { font-weight: 500; }
- 不要用 nth-child 或 nth-of-type 区分跨月,它们依赖顺序而非语义,换月后极易错乱
- 如果支持点击选日期,务必禁用非当前月的
pointer-events: none,否则用户误点无反馈 - 注意
opacity会影响整个格子(包括背景色、边框),若需单独调文字颜色,改用color: #999更稳妥
为什么 grid-auto-rows: minmax(40px, 1fr) 比固定高度更可靠
固定高度(如 grid-auto-rows: 40px)在字体缩放、系统字号放大(iOS「更大字体」设置)、或某些安卓 WebView 下会溢出或截断。而 minmax(40px, 1fr) 保证最小高度,又允许内容撑开——尤其当某天有多个事件标签叠加时。
-
1fr在 auto-rows 中不会均分剩余空间,而是按需分配;真正起作用的是minmax()的下限约束 - 慎用
grid-auto-rows: 1fr:会导致所有行强行等高,反而出现在小屏上文字被压扁 - 如果日历要支持事件气泡(badge),建议额外加一层
position: relative容器,让 badge 绝对定位在右上角,避免影响 Grid 行高计算
grid-column-start,导致新月份第一天仍卡在旧位置。这类 bug 不报错,只静默错位。


















