1. 先搞清楚这个登录页到底要复刻什么
Instagram 登录页是前端练手最经典的页面之一:左边一台手机样机展示 App 界面,右边一张白色卡片放 logo、两个输入框、登录按钮、分隔线、Facebook 登录入口和注册引导,底部再压一排页脚链接。结构不复杂,但细节密度高,特别适合拿来练 Flex 布局、伪元素画分隔线、媒体查询做响应式。
这篇要交付的不是「讲原理」,而是你复制粘贴就能跑起来的一整套东西:一份完整的 HTML 结构、一份带 CSS 变量的样式表、一份config.toml和一份settings.json骨架,最后用本地静态服务器打开验证,再顺手检查 900px 和 450px 两个断点。适合刚学完 HTML+CSS、想找一个真实页面练手的前端学习者,也适合需要快速搭一个登录页壳子的同学。
我试过直接双击index.html打开,样式能出来,但一旦你后面要接接口、要跑本地服务,双击就不够用了。所以这篇会带你走「本地静态服务器 + 配置文件」的完整链路,顺便把 TaoToken 的配置骨架也一起搭好,方便你后面接模型对话或者做页面文案生成。
核心检索词先摆出来:HTML 复刻 Instagram 登录页、CSS 登录页面、Instagram 登录页源码、登录页响应式布局、config.toml 配置、settings.json 骨架。下面按「问题场景 → 前置准备 → 可复制配置 → 验证 → 排障 → 收尾」的顺序走。
2. 动手前先把 TaoToken 这条链路准备好
复刻页面本身不需要联网,但你在做这类练手项目时,经常会遇到两个需求:一是想用模型帮你生成或改写页面文案、占位内容;二是想把页面接进一个本地小工具里做自动化截图或校验。这时候有一个稳定的 API 入口会省很多事。
TaoToken 在这里扮演的角色就是「统一的模型调用入口」。你注册后在控制台拿到 API Key,就能用同一套地址去调不同模型,不用为每个模型单独记一套域名和鉴权方式。对前端练手来说,最直接的用法是:页面写完了,用模型对话帮你检查 HTML 语义、生成更贴近真实的占位文案,或者把一段 CSS 解释清楚。
具体要准备的东西不多:
先去官网注册账号,地址是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册完进控制台。
控制台里创建 API Key,入口在 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,Key 生成后只显示一次,记得先复制存好。
API 的基础地址是 https://taotoken.net/api ,注意这个地址不带任何查询参数,配置里直接写它就行。
如果你只是想验证模型能不能通,用模型对话页面最快:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,在里面发一句话看有没有正常回复。
如果你打算长期用模型辅助写代码、跑 Agent 流程,那更适合看 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,它面向的是持续编码场景,不是一次性问答。
接入细节和参数说明都在文档里:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,遇到字段不确定就翻这里。
Key 管理页面单独记一下:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,后面轮换或删除 Key 都在这。
如果你用的是 Claude Code 这类工具,对应的接入说明在:https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,按文档填地址和 Key 即可。
注意:API Key 属于敏感信息,不要写进前端页面里,也不要提交到公开仓库。练手项目里建议放在本地配置文件,并且把配置文件加进
.gitignore。
3. 可复制的页面结构、样式变量与配置文件
这一节是全文最重的部分,直接给可复制的代码。先建目录,结构建议这样:
instagram-login/ ├── index.html ├── css/ │ └── style.css ├── config.toml ├── settings.json └── .gitignore3.1 index.html 完整结构
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Instagram 登录页复刻</title> <meta name="description" content="HTML+CSS 复刻 Instagram 登录页"> <link rel="stylesheet" href="css/style.css"> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet"> </head> <body> <div id="wrapper"> <div class="container main-container"> <div class="phone-app-demo" aria-hidden="true"></div> <div class="form-data"> <form action="#" method="post" novalidate> <div class="logo"> <h1>Instagram.</h1> </div> <label class="sr-only" for="username">手机号、用户名或邮箱</label> <input id="username" name="username" type="text" placeholder="手机号、用户名或邮箱" autocomplete="username"> <label class="sr-only" for="password">密码</label> <input id="password" name="password" type="password" placeholder="密码" autocomplete="current-password"> <button class="form-btn" type="submit">登录</button> <span class="has-separator">或</span> <a href="#" class="facebook-login"> <i class="fab fa-facebook" aria-hidden="true"></i> 使用 Facebook 登录 </a> <a class="password-reset" href="#">忘记密码?</a> </form> <div class="sign-up"> 还没有账号?<a href="#">注册</a> </div> <div class="get-the-app"> <span>下载 App</span> <div class="badge"> <img src="https://www.instagram.com/static/images/appstore-install-badges/badge_android_english-en.png/e9cd846dc748.png" alt="Android 应用"> <img src="https://www.instagram.com/static/images/appstore-install-badges/badge_ios_english-en.png/180ae7a0bcf7.png" alt="iOS 应用"> </div> </div> </div> </div> <footer> <div class="container footer-container"> <nav class="footer-nav" aria-label="页脚导航"> <ul> <li><a href="#">关于我们</a></li> <li><a href="#">支持</a></li> <li><a href="#">招聘</a></li> <li><a href="#">隐私</a></li> <li><a href="#">条款</a></li> <li><a href="#">语言</a></li> </ul> </nav> <div class="copyright-notice">© 2024 页面复刻练习</div> </div> </footer> </div> </body> </html>这里比原始版本多了几个东西:label配sr-only做无障碍,autocomplete让浏览器能正确识别输入框,aria-hidden处理纯装饰元素。这些不影响视觉,但页面质量会高一档。
3.2 style.css 带 CSS 变量
:root { --bg-body: #fafafa; --bg-card: #ffffff; --border-color: #eeeeee; --text-main: #262626; --text-muted: #999999; --link-blue: #003569; --btn-blue: #3897f0; --fb-blue: #385185; --radius: 0.4rem; --font-base: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; } *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } html { font-size: 10px; } body { background-color: var(--bg-body); font-family: var(--font-base); color: var(--text-main); } img { max-width: 100%; height: auto; display: block; } a { text-decoration: none; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } #wrapper { width: 100%; min-height: 100vh; display: flex; flex-direction: column; justify-content: space-between; padding-top: 2rem; } .container { width: 100%; max-width: 93.5rem; margin: 0 auto; } .main-container { display: flex; padding: 0 8rem; margin-top: 3rem; } .phone-app-demo { flex: 1; height: 61.8rem; background: url('https://uditsolutions.in/babamachine/wp-content/uploads/2019/08/phone.png') center / contain no-repeat; } .form-data { width: 100%; max-width: 35rem; margin-top: 3rem; } .form-data form { background-color: var(--bg-card); border: 2px solid var(--border-color); display: flex; flex-direction: column; padding: 2rem 4rem; text-align: center; } .logo h1 { font-family: 'Pacifico', cursive; font-size: 4rem; margin-bottom: 2.5rem; } .form-data form input { padding: 0.8rem; margin-bottom: 0.5rem; border: 2px solid var(--border-color); border-radius: var(--radius); font-size: 1.4rem; background-color: var(--bg-body); } .form-data form input:focus { outline: none; border-color: var(--text-muted); } .form-btn { margin: 1rem 0 1.5rem; height: 3rem; background-color: var(--btn-blue); font-size: 1.4rem; color: #fff; font-weight: 700; border: none; border-radius: var(--radius); cursor: pointer; } .form-btn:hover { background-color: #2f86dd; } .has-separator { font-size: 1.3rem; color: var(--text-muted); text-transform: uppercase; position: relative; margin-bottom: 2.5rem; } .has-separator::before, .has-separator::after { content: ''; position: absolute; background-color: var(--border-color); width: 40%; height: 1.5px; top: 50%; transform: translateY(-50%); } .has-separator::before { left: 0; } .has-separator::after { right: 0; } .facebook-login { font-size: 1.4rem; font-weight: 700; color: var(--fb-blue); margin-bottom: 2.5rem; } .password-reset { font-size: 1.2rem; color: var(--link-blue); } .sign-up { text-align: center; font-size: 1.4rem; color: var(--text-main); padding: 2rem 0; background-color: var(--bg-card); border: 2px solid var(--border-color); margin: 1rem 0; } .sign-up a { color: var(--btn-blue); font-weight: 700; } .get-the-app { font-size: 1.4rem; text-align: center; } .get-the-app span { display: block; margin: 2rem 0; color: var(--text-main); } .badge { display: flex; justify-content: center; gap: 1rem; } .badge img { max-width: 14rem; cursor: pointer; } footer { padding: 5rem 0; } .footer-container { display: flex; justify-content: space-between; flex-wrap: wrap; font-size: 1.3rem; font-weight: 700; text-transform: uppercase; padding: 0 2rem; } .footer-nav ul { display: flex; flex-wrap: wrap; list-style: none; gap: 1.6rem; } .footer-nav ul li a { color: var(--link-blue); } .copyright-notice { color: var(--text-muted); } @media (max-width: 900px) { .phone-app-demo { display: none; } .form-data { margin: 0 auto; } .main-container { padding: 0; } .footer-container { justify-content: center; } .footer-nav ul { justify-content: center; } } @media (max-width: 450px) { #wrapper { padding-top: 0; } .form-data form { background-color: transparent; border: none; padding: 0 2rem; } .sign-up { background-color: transparent; border: none; margin: 4rem 0; } footer { padding-bottom: 2rem; } }注意Pacifico字体我在 HTML 里没引,如果你要 logo 用这个手写体,在<head>里补一行 Google Fonts 的Pacifico就行,或者直接用系统字体也够看。
3.3 config.toml 骨架
这个文件用来放本地工具或脚本的配置,比如你后面想用脚本调模型检查页面:
# config.toml [app] name = "instagram-login-clone" version = "0.1.0" port = 5500 [api] base_url = "https://taotoken.net/api" api_key = "sk-替换成你自己的Key" model = "gpt-4o-mini" timeout = 30 [build] input_dir = "." output_dir = "dist" minify = falsebase_url固定写https://taotoken.net/api,不要加斜杠后缀。api_key从控制台拿,本地测试用,别提交。
3.4 settings.json 骨架
如果你用的是 VS Code 或者某个前端工具,settings.json可以这样写:
{ "liveServer.settings.port": 5500, "liveServer.settings.root": "/", "files.associations": { "*.toml": "toml" }, "editor.formatOnSave": true, "editor.tabSize": 2, "html.format.wrapLineLength": 120, "css.lint.unknownAtRules": "ignore" }liveServer.settings.port和config.toml里的port保持一致,避免端口冲突。
3.5 .gitignore
node_modules/ dist/ config.toml settings.json .env把带 Key 的文件都挡在仓库外面。
4. 本地打开验证与响应式检查
代码写完了,别急着双击index.html。用本地静态服务器打开,路径和相对引用才和真实部署一致。
如果你装了 Node,最省事的是:
npx serve . -l 5500或者用 Python:
python3 -m http.server 5500然后浏览器访问http://localhost:5500。你应该看到:左边手机样机图,右边白色登录卡片,底部页脚链接。如果手机图没出来,多半是那张外链图挂了,换成你自己的本地图片即可。
响应式检查用浏览器开发者工具的设备模拟:
把宽度拖到 900px 以下,手机样机应该消失,登录卡片居中,页脚链接居中排列。
再拖到 450px 以下,登录卡片的白色背景和边框应该去掉,变成透明背景,注册引导区域也去掉边框。
如果你要验证 TaoToken 这条链路通不通,用模型对话页面发一句「帮我把这段 HTML 的语义问题列出来」,看有没有正常返回。返回正常说明 Key 和地址都对。想验证具体模型,就在模型对话里切换不同模型各发一句,对比响应速度。
5. 本篇常见错排查
手机样机图不显示:外链图片有防盗链或已失效。打开控制台 Network 面板看那张图的请求状态,403 或 404 就换成本地图片,放到assets/目录下,CSS 里改成url('../assets/phone.png')。
分隔线「或」两边横线位置不对:.has-separator的::before和::after用了position: absolute,父元素必须有position: relative。检查这一条,漏了就横线会跑到页面左上角。
输入框聚焦时边框跳动:默认outline和自定义border叠加会撑开尺寸。我在样式里加了outline: none并改border-color,如果你自己改过,确认这两条还在。
450px 断点没生效:媒体查询顺序要从大到小写,900px 在前,450px 在后。顺序反了会被覆盖。
config.toml 里的 Key 报 401:先确认 Key 有没有多余空格,再确认base_url是不是https://taotoken.net/api,不要写成带/v1或其他后缀的地址。还不行就去 API Keys 页面重新生成一个。
settings.json 端口冲突:liveServer.settings.port和config.toml的port改成不一样的值,或者把占用端口的进程关掉。
页面在手机上横向滚动:多半是某个元素宽度超出视口。给body加overflow-x: hidden只是遮丑,正确做法是检查.badge img的max-width和footer的padding,把固定宽度改成百分比或max-width。
6. 接下来怎么用这套骨架
页面跑起来之后,你可以做三件事让它更有用。第一,把config.toml里的api_key换成你自己的,写个小脚本调模型批量生成不同风格的占位文案,替换页面里的中文提示。第二,把settings.json里的端口固定下来,配合 Live Server 做实时预览,改 CSS 立刻看到效果。第三,如果你要长期做这类页面练手,直接上 Coding Plan 把模型接进你的编码流程,比每次手动复制粘贴省事。
接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,Key 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,模型对话验证在 https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。页面本身不依赖这些也能跑,但配上之后,你练手的速度会快不少。