做前端这些年,我电脑里存过一个叫“卡通模板”的文件夹,翻起来大概有六十套上下。最初只是给自己省事——接到一个儿童课程页面、一个活动落地页、一张节日贺卡,不用从白纸开始画,直接挑一套 HTML 卡通模板改改就能交。后来发现身边不少人也在到处找这类东西,找到了又不会改,改完又发现手机上全糊了。所以这篇就把这六十套模板的来路、分类、挑选逻辑和改造手法一次讲清楚:它是什么、能干什么、适合谁用。如果你是会写基础 HTML/CSS 的独立开发者、做课程页面的老师、接私活的学生,或者只想给自己做个好看的生日页面的普通人,下面这些内容都能直接拿去用。全文不讲虚的,都是拆模板时踩出来的经验。
1. 六十套 HTML 卡通模板到底是些什么东西
1.1 先把“卡通模板”这个词拆开看
很多人一听 HTML 卡通模板,脑子里浮现的是一张图片。其实不是。模板的本质是一套可以直接在浏览器里打开、能跑起来的网页文件,卡通只是它的视觉风格。它的构成通常就四层:一层 HTML 结构,决定页面上有什么;一层 CSS,负责把方块画成圆形、把按钮画成果冻、把标题描上黑边;一层 SVG 或者位图插画,负责那些 CSS 画不出来的角色和小物件;最后可能还有一小段 JS,管轮播、管点击反馈、管飘落的雪花。四层里任何一层缺了,都还能看,但“卡通味”会掉一截。
我之所以强调这个拆解,是因为它决定了你拿到一套模板之后该怎么动手。你把模板当成一张图,那就只能整张换;你把它当成四层结构,就能只换插画不换布局,或者只改配色不改结构。我在实际项目里最常干的事,是把一套模板的 CSS 变量抽出来重新配一遍色,插画和结构全不动,十分钟就能从“糖果色儿童风”变成“莫兰迪手作风”。这个操作的前提,就是你得先看清楚这套模板的卡通感是结构给的、颜色给的,还是插画给的。
顺带说一句,六十这个数量不必较真。真正在项目里能反复复用的模板,一个人手上留十套左右就够了,剩下的都是备选库。数量多的意义不在于每一套都用得上,而在于你能覆盖足够多的场景——节日、课程、活动、个人主页、错误页、加载页,每类留两三套,遇到需求时不用现找。
1.2 六十套模板的六种典型类型
我把手头这些按技术构成和使用场景归了归类,大致能分成六种。这个分类不是学术分类,纯粹是为了“拿到手知道怎么改”这个目的。
| 类型 | 技术占比 | 改造难度 | 典型用途 |
|---|---|---|---|
| 纯 CSS 造型型 | HTML + CSS,无图片,形状靠 border-radius 和伪元素 | 低,改色即可 | 头像、图标、按钮、小装饰 |
| SVG 插画型 | HTML + CSS + 内联 SVG | 中,需要动 SVG 路径 | 落地页主视觉、角色展示 |
| 整页动效型 | 上述三层 + 少量 JS 或纯 CSS 动画 | 中高,动画参数要调 | 活动页、节日贺卡、开场页 |
| 卡片列表型 | 语义化结构 + 网格布局 | 低,换内容即可 | 课程列表、作品集、商品展示 |
| 单页交互型 | 完整 JS 逻辑(小游戏、翻卡、问答) | 高,逻辑要读懂 | 课堂互动、抽奖、答题页 |
| 状态页型 | 极简结构 + 一张插画 | 极低 | 404、加载中、空状态、成功提示 |
这六类里,第一类和第六类最值得优先掌握。纯 CSS 造型型的代码通常只有几十行,看懂一个就等于看懂了全部,因为画卡通脸的套路就那几招:圆形底、两点眼睛、一道弧线嘴巴、两块腮红。状态页型则是使用频率最高的,任何一个后台系统、任何一个活动页,都需要一个“加载中”和一个“出错了”,而这俩页面的成本极低,改个插画和文案就是新的。
整页动效型和单页交互型改造难度大,不是因为代码难,而是因为要读懂别人写的动画节奏。一套飘落雪花的动画,雪花数量、下落速度、透明度变化曲线全在 CSS 或者 JS 里写死,你直接换个颜色没问题,但想改成“飘落花瓣”就得动参数了。我的建议是这类模板优先整套复用,少做大改,除非你确实有时间把逻辑捋一遍。
1.3 为什么这套东西值得单独攒一个库
自己攒模板库这件事,收益会随项目数量线性放大。第一个项目你花两小时找模板、三小时改,第三个项目你花十分钟找、一小时改,到第十个项目,你可能只是把上次的文件复制一份、换文案、换主色就交付了。这不是偷懒,是把重复劳动压缩掉,把时间留给真正需要动脑的部分。
另一个隐性好处是风格统一。一个模板库用久了,你自己会形成一套配色习惯和间距习惯,做出来的东西放在一起是协调的,这在做系列页面时特别值钱。我给自己定的规矩是:主色不超过三个,圆角只用两档(大圆角和小圆角),描边只用一档粗细。规矩定死了,改模板的速度就上来了,因为你不需要每次重新做决策。
注意:模板库不要盲目扩容。看到好看的存下来却不读代码,这个库就是无效的。我自己的筛选标准是——存进来之前必须打开改过一次,改不动的不存。
2. 从六十套里挑出能直接用的那一套
2.1 按使用场景反推技术栈
挑模板的顺序我一般是反着来的:先定场景,再定技术,最后才看颜值。因为颜值是可以改的,技术栈改起来很费劲。
举个具体例子。需求是“做一个班级活动报名页,只有链接,没有服务器,扫码打开”。这个场景倒推出三条硬约束:第一,必须是纯静态,不能有后端;第二,必须手机优先,因为扫码打开的多半是手机;第三,不能依赖任何外部资源,因为用户网络环境不确定,外链字体和图标库一旦加载失败,页面就废了一半。顺着这三条去筛,能筛掉一大半——所有带构建工具链的、所有用图标字体库的、所有图片走外部链接的,全部排除。
再举个反例。需求是“做一个儿童手工课程的作品展示页,投在教室大屏上”。这个场景的约束完全不同:屏幕大,字要大,图片要清晰,动画可以稍微夸张一点,不需要考虑手机。这时候你可以放心用大尺寸 SVG、用视差滚动、用比较重的阴影,因为渲染压力都在一台固定的机器上。
我把常见的场景约束整理成一张对照表,选模板时对着看,能省很多返工:
| 场景 | 硬约束 | 优先选的类型 | 要避开的 |
|---|---|---|---|
| 扫码即开的移动端活动页 | 无外链、体积小、竖屏友好 | 纯 CSS 造型型、状态页型 | 大图插画型、视差动效型 |
| 教室大屏展示 | 高对比、大字号、可远看 | SVG 插画型、整页动效型 | 信息密度高的卡片列表 |
| 后台系统的空状态/错误页 | 与既有设计语言融合、加载快 | 状态页型 | 带完整布局的整页模板 |
| 系列课程页 | 结构可复用、易批量换内容 | 卡片列表型 | 每套都长不一样的插画型 |
| 个人作品集 | 独占性强、能体现个性 | 整页动效型、单页交互型 | 通用卡片型 |
提示:凡是模板里出现 CDN 链接的,第一步先把它本地化。图标库、字体、动画库,全部下载到自己的目录里。这一步花五分钟,能避免上线后某天外部服务调整导致页面崩掉。
2.2 我自己的三分钟筛选法
拿到一套陌生的卡通模板,我会按固定顺序看四个地方,全程不超过三分钟。这个流程能过滤掉九成不合适的候选。
第一分钟看目录结构。打开文件夹,看根目录下有几个 HTML 文件,有没有 node_modules,有没有 package.json,assets 目录里是什么。如果只有一两个 HTML、一个 css、一个 img,这套就是纯静态,改造门槛最低。如果出现 package.json 和一串配置,说明它需要构建,你得先装环境,这时候我会直接放弃,除非真的非它不可。
第二分钟看 HTML 的头部。重点看三处:meta viewport 有没有写,charset 是不是 utf-8,有没有一堆外部 link。viewport 缺了,这套模板在手机上基本没法用,得自己补;charset 缺了,中文可能乱码;外部 link 越多,越要花时间本地化。
第三分钟看 CSS 的组织方式。搜索一下:root,看有没有定义 CSS 变量。有变量的模板,改色是几秒钟的事;没有变量的模板,色值散落在几十个地方,改一遍要半小时。再看一眼有没有写死像素宽度(比如width: 1200px),大面积写死宽度的模板,改成响应式的成本会很高。
三分钟看完,基本能判断这套模板是“五分钟能上手”还是“一下午都改不完”。我个人的经验是,这三分钟投入的回报率极高,因为很多返工就是因为前期没看清就动手了。
2.3 纯静态和需要构建的模板,怎么取舍
这个问题值得单独说,因为它直接决定你的工作流。纯静态模板的好处是所见即所得,双击 HTML 就能看效果,改一行刷新马上看到,不需要等编译,也不需要理解构建配置。缺点是没有模块化,样式和结构容易重复,页面多了以后维护成本上升。
需要构建的模板好处是工程化完整,变量、组件、自动前缀一应俱全,适合做长期迭代的项目。缺点是启动成本高,你得先装依赖,还得能看懂构建配置,出了问题排查路径变长。
我的取舍原则很简单:一次性交付、页面数量小于五个、交付后我不再维护的,一律选纯静态;需要长期迭代、页面数量超过十个、或者要交给别人接手维护的,才考虑构建型。卡通风格的东西绝大多数是一次性交付的活动页、课程页、贺卡页,所以纯静态的性价比高得多。这也是为什么我攒的六十套里,八成以上都是纯静态。
倒是有个折中做法我常用:拿纯静态模板当起点,自己往里加一层极简的变量体系,不引入任何构建工具,只用原生 CSS 自定义属性加上一点点组织约定。这样既保留了双击即开的便利,又拿到了变量化的可维护性。
3. 卡通感的来源:造型、配色、字体与动效
3.1 造型:CSS 画形状和 SVG 插画的边界在哪
卡通风格最核心的东西是造型。而造型这件事在网页上有两条路:一条是用 CSS 拼,一条是用 SVG 画。这两条路的边界,我用一句话总结:能用三五个几何形状拼出来的,用 CSS;需要曲线自由、需要复杂轮廓的,用 SVG。
CSS 拼形状的本事其实不小。圆形靠border-radius: 50%,椭圆形靠不对称的圆角百分比,水滴形、叶片形靠border-radius: 60% 40% 55% 45% / 50% 60% 40% 50%这种八值写法,三角形靠 border 技巧,弧线靠伪元素加旋转遮罩。人脸、云朵、小房子、冰淇淋这些,纯 CSS 完全够用,而且好处是体积为零、可无损缩放、颜色改一个变量就全变。
我把最常用的一段卡通脸代码贴出来,这段代码我至少复用过三十次:
:root{ --ink: #2f2a26; --skin: #ffd9bf; --blush: #ff9d9d; --bg: #fff8ea; } .face{ position: relative; width: 160px; height: 160px; margin: 40px auto; background: var(--skin); border: 4px solid var(--ink); border-radius: 50%; } /* 两只眼睛 */ .face::before, .face::after{ content: ""; position: absolute; top: 52px; width: 14px; height: 18px; background: var(--ink); border-radius: 50%; } .face::before{ left: 44px; } .face::after { right: 44px; } /* 嘴巴用一条下弧线 */ .face .mouth{ position: absolute; left: 50%; top: 92px; width: 48px; height: 24px; margin-left: -24px; border: 4px solid var(--ink); border-top: none; border-radius: 0 0 48px 48px; }这段代码里最关键的不是形状,是那圈 4px 的深色描边。这是卡通感的开关。同样一个圆,去掉描边就是“扁平设计”,加上描边立刻变成“绘本插画”。原因在于手绘和印刷的视觉记忆里,角色轮廓总是被线条强调过的。所以我在改卡通模板时,只要觉得“不够卡通”,第一反应就是给主要形状补描边,通常一补就对了。
SVG 那边的用武之地是复杂轮廓,比如一只猫的侧影、一片带锯齿的叶子、一个手绘质感的星星。SVG 的优势是路径可以任意扭曲,还能做路径动画、能配合currentColor跟随文字颜色变化。代价是体积和可读性,一段复杂路径的 d 属性基本没法手改,只能当黑盒用。
实操心得:改 SVG 不要试图直接读路径数据。在代码编辑器里把 SVG 内联进 HTML,然后用浏览器开发者工具选中那个
<path>,在样式面板里试fill和stroke,找到满意的值再写回代码。这比在几百个数字里猜快十倍。
3.2 配色:卡通感有八成来自调色板
我见过太多模板,造型做得很好,但颜色一塌糊涂,整体看起来像廉价宣传单。卡通风格对配色的要求,其实比很多人想的更严格。
第一个原则是限制色数。一套卡通页面,主色三个、点缀色一个,足够了。超过五个颜色就开始乱。具体怎么分:一个底色(通常是极浅的暖色,米白、浅黄、淡天蓝,不用纯白),一个主色(角色的衣服、按钮),一个辅色(搭主色,通常是对比色或者邻近色),一个点缀色(腮红、小星星、高亮标记,饱和度最高,用量最少)。
第二个原则是描边色永远比填充色深。卡通里的描边不是纯黑,而是把填充色压暗、提饱和之后的深色。比如皮肤是#ffd9bf,描边用#2f2a26这种偏暖的深褐,比纯黑柔和得多,整张图的质感会立刻不一样。纯黑描边在小尺寸下会显得脏。
第三个原则是亮度和饱和度要成组变化。卡通配色常见两种路子:一种是高明度低饱和的糖果色,适合儿童、治愈、手作风;另一种是高饱和撞色的活泼风,适合活动、促销、游戏风。这两种不要混着用,混了就会显得脏。
批量改色我用过一个小脚本,放在这里,扫一遍 CSS 文件就能把旧色值换掉:
import pathlib import re # 旧色值 -> 新色值,统一小写对比 MAP = { "#ff6b6b": "#7fb069", "#4ecdc4": "#e6aace", "#ffe66d": "#fdf0d5", } root = pathlib.Path("./css") for f in root.rglob("*.css"): text = f.read_text(encoding="utf-8") for old, new in MAP.items(): text = re.sub(re.escape(old), new, text, flags=re.I) f.write_text(text, encoding="utf-8") print("done:", f)这个脚本很粗糙,只能做精确匹配,遇到 rgb 写法和压缩过的三位色值会漏。但卡通模板的色值通常是手写六位的,命中率很高。跑完再用开发者工具全局搜一遍#,看有没有漏网的。
3.3 字体和排版:卡通页最容易翻车的地方
字体这件事,我的态度非常明确:中文卡通页不要试图用花体字。中文花体字库动辄几兆,加载慢,而且很多免费字体的字形质量不行,标题还行,正文一塌糊涂。更麻烦的是,这类字体在低分辨率设备上渲染会糊。
稳妥的做法是标题和正文用同一套无衬线字体,靠字重和字号拉开层次。中文用系统默认的黑体族就够了:
body{ font-family: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif; -webkit-font-smoothing: antialiased; } h1.title{ font-size: clamp(1.6rem, 5vw, 2.6rem); font-weight: 800; letter-spacing: 1px; line-height: 1.4; }标题上做卡通感,我更愿意用装饰而不是换字体:给标题加一层同色的偏移描边,加一个手绘感的下划线,或者加一个文字阴影。这几种做法既便宜又稳。
h1.title{ color: #fff; text-shadow: 3px 3px 0 var(--ink), -3px 3px 0 var(--ink), 3px -3px 0 var(--ink), -3px -3px 0 var(--ink); }注意这个四向阴影的做法,在字号很大的时候会把笔画糊在一起,中文尤其明显。所以它只适合短标题,而且字号最好控制在两行以内。正文绝对不要用。
行高方面,卡通页面因为有很多装饰元素,正文行高建议给到1.8,比常规的 1.6 更松。段落之间留白也要大一些,因为周围都是圆滚滚的图形,文字太密会显得挤。
3.4 动效:让东西动起来,也让它别卡
卡通风格的动效,追求的是“弹性”和“缓”,不是“快”和“锐”。一个按钮按下去,弹一下再回位,比瞬间变色更像卡通。这背后的技术点就两个:transition-timing-function和transform-origin。
弹性效果最省事的写法是用cubic-bezier做一点回弹:
.btn{ transition: transform .28s cubic-bezier(.34, 1.56, .64, 1); } .btn:active{ transform: scale(.92); }cubic-bezier(.34, 1.56, .64, 1)里那个大于 1 的值就是回弹的来源,它在动画末尾会超过目标值再折回来,形成“弹一下”的手感。这个参数我调过很多次,1.56 是个比较舒服的位置,再大会显得浮夸。
性能方面的事情,说三条就够用。第一,动画只动transform和opacity,这两者不触发重排重绘,其他属性(left、top、width、box-shadow)一旦动起来就容易掉帧。第二,需要持续动画的元素提前加will-change: transform,但不要滥用,元素多了反而更卡。第三,使用@media (prefers-reduced-motion: reduce)给个退路:
@media (prefers-reduced-motion: reduce){ *, *::before, *::after{ animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } }这段代码看着不起眼,但在课堂大屏、老旧设备、以及开启省电模式的手机上,能救你一命。
4. 从一套模板到能交付的页面:完整改造流程
4.1 第一步:目录清理与结构重建
下载来的模板,目录往往是一团。常见的是 css 里堆了七八个文件,img 里混着没用的图,根目录还有个 demo 文件。我拿到手第一件事是重建结构,固定成这个样子:
project/ ├── index.html ├── assets/ │ ├── css/ │ │ ├── base.css # reset、变量、字体、通用类 │ │ └── page.css # 当前页面专属样式 │ ├── img/ │ └── js/ │ └── main.js这么分的原因是可预测。base.css 放所有会复用的东西,page.css 只放这一个页面的事。下次做新页面,base.css 直接复制过去,改 page.css 就行。很多人做第二个页面时发现样式打架,问题就出在当初把所有东西混在一个文件里。
清理时还有个动作:把没用到的文件全删掉。模板作者常常会附赠十几种按钮样式、二十种图标,你可能只用三个。留着它们不会让项目变好,只会让后面接手的人困惑,也会让排查样式冲突变难。删的时候不要心软,删错了从备份里找回来就行,所以动刀之前先把原包压缩包留一份。
4.2 第二步:变量化,把散落的颜色收拢
不管原模板有没有 CSS 变量,我都会自己建一套。通常在 base.css 顶部:
:root{ /* 颜色 */ --bg: #fff8ea; --ink: #2f2a26; --primary: #f59f00; --secondary: #4dabf7; --accent: #ff8787; --surface: #ffffff; /* 圆角 */ --r-lg: 24px; --r-sm: 10px; /* 描边 */ --line: 3px solid var(--ink); /* 间距 */ --gap-s: 8px; --gap-m: 16px; --gap-l: 32px; }建完之后要做一次“收网”:把 page.css 里所有硬编码的色值、圆角、间距,逐个替换成对应的变量。这活儿枯燥但必须做,因为它是后面所有批量调整的前提。做完之后,你想换整套色调,只改--primary一行就够了;想调整体圆润度,改--r-lg一行就够了。
做收网的时候有个技巧:先搜#,把颜色全部干掉;再搜border-radius,把圆角干掉;最后搜padding和margin,把间距干掉。三遍下来基本就干净了。我一般会用编辑器的全局搜索配合正则,比如border-radius:\s*\d+px,批量替换成border-radius: var(--r-sm),然后再人工确认一遍哪些地方该用大圆角。
4.3 第三步:内容替换与插画处理
内容是最好换的,但有两个坑。第一个坑是文字长度。模板作者写的是几个字的短标题,你换成一句长文案,布局立刻塌。所以换内容之前,先按最长的可能文案去试。我通常会在标题里塞一句特别长的话,看会不会撑破,撑破了就加overflow-wrap: break-word或者调整字号。
第二个坑是图片规格。模板里的占位图可能是 800×600,你换的图是 1200×900,不注意就会溢出或者变形。稳妥的做法是先统一容器比例,再让图片自适应:
.card-img{ aspect-ratio: 4 / 3; width: 100%; overflow: hidden; border-radius: var(--r-sm); } .card-img img{ width: 100%; height: 100%; object-fit: cover; display: block; }aspect-ratio这个属性现在支持度已经很好了,用它来锁比例比自己算 padding-top 省事很多。object-fit: cover保证图片不变形,多出来的部分裁掉。
插画替换要分情况。如果是位图,裁成统一尺寸、压成 webp、控制单张在 100KB 以内,基本就到位了。如果是内联 SVG,我只改两处:fill换色,viewBox保持不动。改viewBox风险很大,它会连同内部所有坐标一起缩放,改完比例就乱了。想调大小,用 CSS 的width和height,不要动viewBox。
4.4 第四步:响应式,手机上别横向滚动
卡通模板在手机上翻车,八成是三个原因:固定宽度、大圆角元素溢出、绝对定位的装饰物飞出屏幕。
固定宽度好解决,把所有写死的width: 1200px换成max-width,外面套一层容器:
.container{ width: 100%; max-width: 1100px; margin-inline: auto; padding-inline: var(--gap-m); }溢出问题,先给 body 加一道保险,但不要当成常规手段:
html, body{ overflow-x: hidden; }这道保险能遮住症状,但治不了根。真正的修法是找出那个超宽的元素。定位方法:在开发者工具里给所有元素临时加 outline,横向滚动出现时能一眼看到是谁超了。十次里有八次是某个绝对定位的装饰圆,left: 120%或者right: -200px,在窄屏上就顶出去了。解法是把它包进一个overflow: hidden的父级,或者在小屏下直接display: none。
绝对定位的装饰物还有个通病:它们常常按桌面尺寸的百分比写,到了手机上看不见或者太小。我习惯给每个装饰物都定义一套小屏规则,能砍就砍,卡通页面在小屏上元素少一点反而清爽。
4.5 第五步:上线前的四项检查
交付之前我会固定跑一遍四件事,这套流程帮我挡掉了很多尴尬。
第一项,断网检查。把网络禁用,刷新页面,看有没有元素消失。消失的就是外链资源。图标没了、字体变了、动画不动了,都在这一步暴露。
第二项,窄屏检查。开发者工具里把宽度拉到 320px,从上滚到下,看有没有横向滚动条、有没有文字被截断、有没有按钮小到点不中。320 是个很苛刻的宽度,能过这一关,主流手机基本没问题。
第三项,对比度检查。卡通配色喜欢用浅黄配白、浅蓝配白,这类组合在户外光线下几乎看不清。我用的标准很简单:正文文字和背景的对比度,肉眼看过去要能一眼分辨。拿不准就把背景压深一档,或者给文字加深色描边。卡通风格里文字描边是完全可接受的,甚至更协调。
第四项,可访问性最小集。给所有图片加alt,给所有按钮用<button>而不是可点击的<div>,给装饰性图片加aria-hidden="true"。这三件事花不了十分钟,但对于用读屏软件的人来说是决定性的差别。
注意:装饰性的 SVG 一定要加
aria-hidden="true"。卡通页面装饰元素多,如果不加,读屏软件会把一堆无意义的路径念出来,体验极差。
5. 常见问题与排查实录
5.1 高频问题速查表
这些年改模板遇到的问题,翻来覆去就那么几个。我把它们整理成一张表,遇到问题先对一下,多数能直接定位。
| 现象 | 最可能的原因 | 排查与解决 |
|---|---|---|
| 中文全是乱码 | HTML 头部缺 charset 声明 | 在<head>最前面加<meta charset="utf-8">,并确保文件本身以 UTF-8 保存 |
| 图标变成方块或消失 | 图标字体或外链 SVG 加载失败 | 本地化字体文件与 SVG,改用内联<svg> |
| 手机上可以左右滑动 | 某个元素超出视口宽度 | 给全部元素加临时 outline 定位超宽元素,或给父级加overflow: hidden |
| 动画一卡一卡 | 动画属性触发重排,或元素太多 | 改transform/opacity,减少同时动画的元素数量 |
| SVG 比例变形 | 改动了viewBox | 恢复viewBox,用 CSS 控制尺寸 |
| 图片和容器不贴合 | 容器没有定比例,图片没设object-fit | 容器加aspect-ratio,图片加object-fit: cover |
| 移动端底部被遮挡 | 用了height: 100vh | 改min-height: 100dvh,兼容动态地址栏 |
| 圆角在旧设备上失效 | 老内核不支持某些写法 | 保留浏览器前缀,或退回单一圆角值 |
| 文字在深色背景上看不清 | 对比度不足 | 提高文字亮度、加浅色描边,或加深背景 |
| 打印出来一片空白 | 打印样式没处理背景色 | 加@media print规则,关键内容用纯黑文字 |
这张表里我最想多说一句的是100vh那条。这个问题在移动端出现得非常频繁,因为浏览器地址栏收起和展开会改变可视高度,100vh取的是最大值,导致底部内容被挡住。100dvh是动态视口高度,能跟随变化,现在的支持度已经足够。如果确实要兼容很老的设备,那就用min-height: 100vh加上内部滚动,别指望一个单位解决所有问题。
5.2 几个只有踩过才知道的坑
第一个坑是模板自带的 reset 和你自己的 reset 打架。很多模板会在 CSS 顶部塞一段* { margin: 0; padding: 0; box-sizing: border-box; },你如果再复制一份自己的 reset 进去,虽然不报错,但顺序不对会覆盖掉后面的规则。我现在的做法是只保留一份,放在 base.css 最前面,后面所有文件都不再写 reset。
第二个坑是字体回退链写得太死。有些人写font-family: "某特定字体",只有一项。这个字体在用户机器上不存在时,浏览器会用默认的宋体渲染,整个页面的气质瞬间垮掉。永远要写完整的回退链,最后一定落到sans-serif。
第三个坑是伪元素被overflow: hidden裁掉。卡通页面大量使用::before和::after做装饰角、做小尾巴,这些伪元素如果用负定位跑到父元素外面,父元素一旦有overflow: hidden就全没了。遇到装饰消失,先去检查祖先元素有没有设 overflow。
第四个坑是transform造成的模糊。给一个元素加了transform: scale(1.02)或者translateZ(0),在某些设备上文字会变模糊。原因是元素被提升到独立图层后按非整数像素渲染。解决办法是尽量用整数位移,或者只对图片类元素用 transform,文字类元素改用其他方式做动画。
第五个坑是 SVG 里的<style>污染。有些模板把样式写在 SVG 内部,如果这个 SVG 是内联的,里面的选择器会作用到整个页面。我自己遇到过 SVG 里写了circle { fill: red },结果页面上所有的圆圈都变红了。解决办法是把 SVG 内部的样式选择器加上前缀,或者干脆用属性形式(fill="...")写在元素上。
第六个坑是复制粘贴带进来的不可见字符。从某些网页或者文档里复制代码,会带上零宽字符或者不换行空格,导致 CSS 规则不生效,但看代码完全正常。遇到“明明写了却不生效”的情况,最省事的办法是手动重敲一遍那一行,或者用编辑器的“显示不可见字符”功能筛一遍。
5.3 一个模板库的长期维护习惯
最后说说维护。模板库如果只是往里堆,两年后就是一堆垃圾。我自己用了三个习惯来保持它可用。
一是给每套模板写一行说明,就一行,写清楚它是什么风格、什么场景、改起来难不难。比如“糖果色 / 移动活动页 / 改色即可”。这一行字的价值在两年后体现——你打开文件夹,能一眼知道哪套该用。
二是改过的版本单独存,不覆盖原版。我吃过这个亏,把一套模板改成了项目专用,原版就没了,下次想用同样的风格得重新找。现在的做法是原版保留在_raw目录里,改过的放进work目录,目录名后面带上用途。
三是每半年清一次。超过一年没被打开过的模板,直接删。删掉并不心疼,因为真正好用的那十套会一直被用到。库的价值不在于数量,在于你闭着眼睛都能想起哪一套适合哪种需求。
个人体会:模板是起点,不是终点。真正让你做得快的,不是手上存了多少套,而是你对“这套东西由哪几层构成”这件事有多清楚。看清楚了,随便给我一套陌生的卡通页面,我也能在半小时内把它变成我想要的样子。
6. 几段可以直接抄的改造片段
6.1 一个通用的卡通卡片骨架
这套结构我几乎每个项目都会用到,卡的边框、圆角、阴影都走变量,换个主题只改变量就行。
<div class="card"> <div class="card-img"> <img src="assets/img/demo.webp" alt="课程示例图"> </div> <div class="card-body"> <h3 class="card-title">手工课第一讲</h3> <p class="card-desc">用纸黏土做一只小动物,四十分钟完成。</p> </div> </div>.card{ background: var(--surface); border: var(--line); border-radius: var(--r-lg); box-shadow: 6px 6px 0 var(--ink); overflow: hidden; transition: transform .25s cubic-bezier(.34,1.56,.64,1), box-shadow .25s ease; } .card:hover{ transform: translate(-2px, -2px); box-shadow: 8px 8px 0 var(--ink); } .card-body{ padding: var(--gap-m) var(--gap-m) var(--gap-l); } .card-title{ margin: 0 0 var(--gap-s); font-size: 1.15rem; font-weight: 700; } .card-desc{ margin: 0; line-height: 1.8; color: #5b534d; }这里的box-shadow: 6px 6px 0 var(--ink)是一个关键手法——硬阴影,不带模糊半径。这是手绘和贴纸感的重要来源。你把它改成0 6px 16px rgba(0,0,0,.2),整个卡片的性格就变了,从卡通变成商务。
6.2 一个纯 CSS 的飘落装饰
节日页和活动页常用飘落效果,用 CSS 动画就能做,不需要 JS 逐帧。做法是把若干个装饰元素绝对定位,给不同的动画延迟和时长。
.deco-layer{ position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; } .flake{ position: absolute; top: -40px; width: 14px; height: 14px; background: var(--accent); border-radius: 50%; opacity: .85; animation: fall linear infinite; } .flake:nth-child(1){ left: 8%; animation-duration: 9s; animation-delay: 0s; } .flake:nth-child(2){ left: 26%; animation-duration: 12s; animation-delay: 1.5s; } .flake:nth-child(3){ left: 47%; animation-duration: 10s; animation-delay: 3s; } .flake:nth-child(4){ left: 68%; animation-duration: 14s; animation-delay: .8s; } .flake:nth-child(5){ left: 86%; animation-duration: 11s; animation-delay: 2.2s; } @keyframes fall{ 0% { transform: translate3d(0, 0, 0) rotate(0deg); } 100% { transform: translate3d(24px, 110vh, 0) rotate(240deg); } }几个要点值得记一下。第一,父级必须有overflow: hidden,不然元素落下后会撑出滚动条。第二,pointer-events: none必须加,否则这层会挡住底下的按钮点击,这是个非常隐蔽的 bug。第三,translate3d比translate更容易被提升到独立图层,动画更顺,但元素数量要控制,超过二十个就开始掉帧。第四,110vh而不是100vh,是为了让元素完全落出视口再循环,否则会在底部突然消失。
6.3 一套可切换的主题变量
如果同一套模板要复用成多个主题,最省事的做法是把变量按主题分组,通过切换根节点上的 data 属性来换肤。
:root{ --bg: #fff8ea; --ink: #2f2a26; --primary: #f59f00; --accent: #ff8787; } [data-theme="mint"]{ --bg: #f0fbf6; --ink: #263a33; --primary: #37b24d; --accent: #74c0fc; } [data-theme="dusk"]{ --bg: #f6f2ff; --ink: #2e2a3a; --primary: #7048e8; --accent: #f783ac; }<html>const themes = ["default", "mint", "dusk"]; let i = 0; document.querySelector("#theme-btn").addEventListener("click", () => { i = (i + 1) % themes.length; document.documentElement.dataset.theme = themes[i] === "default" ? "" : themes[i]; });这套做法的好处是,所有颜色相关的样式只写一遍,主题切换是零成本的,不产生任何额外的样式定义。缺点是它对前期变量收网的完整度要求很高,一旦有漏网的硬编码颜色,切换主题时那个元素就不变色,会显得特别突兀。所以每次做完主题之后,我都会专门切一遍所有主题,从头滚到尾,找那个“不合群”的元素。
6.4 一个不到三十行的加载态
状态页是我用得最多的模板类型,这里给一个极简版本,改改颜色和文案就能直接用于任何项目。
<div class="loading" role="status" aria-live="polite"> <div class="loading-dots"> <span></span><span></span><span></span> </div> <p class="loading-text">正在准备中,请稍候</p> </div>.loading{ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; min-height: 40vh; } .loading-dots{ display: flex; gap: 10px; } .loading-dots span{ width: 16px; height: 16px; border-radius: 50%; background: var(--primary); border: 3px solid var(--ink); animation: bounce 1s ease-in-out infinite; } .loading-dots span:nth-child(2){ animation-delay: .15s; } .loading-dots span:nth-child(3){ animation-delay: .3s; } @keyframes bounce{ 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } } .loading-text{ margin: 0; color: #6b6259; letter-spacing: .5px; }role="status"和aria-live="polite"这两句别省。加了之后,读屏用户会知道页面正在加载,而不是对着一片空白发呆。这类细节在卡通模板里几乎没人写,但它是专业和不专业的分界线。
改造这套模板的整个过程中,我最深的感受是:卡通风的“便宜感”和“精致感”之间,差的就是那一点点克制。描边粗细统一、阴影方向统一、圆角档位统一、颜色数量控制在四个以内,做到这四点,哪怕结构再简单,看起来也是认真做过的。反过来,同样是六十套模板,每套都用不同的圆角、不同的阴影模糊、不同的配色数量,堆在一起就是杂乱的。所以我后期的做法是,与其继续收集第六十一套,不如把手上的十套调到能反复用。