注册
web

React + Tailwind CSS 实战:打造一个“会呼吸”的登录页面

哈喽,各位掘金的“打工人”们,大家好!👋


还记得咱们上一篇聊过的 Tailwind CSS 入门(在这里详细讲解了如何配置TailwindCss) 吗?当时我们不仅揭开了原子化 CSS 的神秘面纱,还稍微带了一嘴“受控组件”的概念。


今天,咱们不玩虚的,直接实战!🚀


我们要用 React 配合 Tailwind CSS,从零打造一个现代、优雅、且交互细腻的登录页面


别担心,虽然说是“实战”,但我的风格你懂的:轻松愉快,知识硬核。我会把代码掰开了、揉碎了讲给你听,保证你不仅能学会写,还能懂得为什么要这么写。


准备好了吗?系好安全带,老司机要发车了!🚌💨




🎯 我们的目标


我们要做的不是一个死板的 HTML 页面,而是一个有灵魂的 React 组件。它包含:



  1. 响应式布局:手机、平板、电脑通吃。
  2. 优雅的 UI:圆角、阴影、柔和的配色(Tailwind 拿手好戏)。
  3. 极致的交互:聚焦时图标变色、平滑的过渡动画。
  4. React 逻辑:受控组件、状态管理、密码显隐切换。
  5. 图标库:使用 lucide-react 这一当下最火的图标库。

最终效果?就像你每天用的那些大厂 App 一样丝滑。✨




🛠️ 准备工作:兵马未动,粮草先行


首先,确保你的环境里有 React 和 Tailwind CSS。如果你是 Vite 用户,这简直是分分钟的事。


在这个项目中,我们还需要一个特别好用的图标库:lucide-react


npm install lucide-react
# 或者
pnpm add lucide-react

它体积小、图标全、风格统一,绝对是开发利器。




🏗️ 第一步:骨架与画布 —— 布局的艺术


一切从 App.jsx 开始。


我们先看最外层的结构。想象一下,你是个画家,得先铺好画布。


export default function App() {
// ... 逻辑部分稍后讲 ...

return (
// 1. 外层容器:全屏背景,居中布局
<div className="min-h-screen bg-slate-50 flex items-center justify-center p-4">
{/* ... 卡片 ... */}
</div>

)
}

📝 代码详解



  • min-h-screen: 核心! 这让容器的高度至少为屏幕高度(100vh)。如果内容不够多,背景也能铺满全屏;内容多了,它能自动延伸。告别尴尬的“白底漏出”。
  • bg-slate-50: 给背景来点极其淡雅的灰。纯白(#fff)太刺眼,Slate-50 刚刚好,高级感这就来了。
  • flex items-center justify-center: Flexbox 三连。这是最经典的垂直水平居中方案。不管你的屏幕多大,登录框永远稳坐 C 位。
  • p-4: 给四周留点余地,防止在小屏幕手机上内容贴边。



📦 第二步:卡片设计 —— 拟物感的回归


接下来是那个漂浮在屏幕中央的白色卡片。


<div className="relative z-10 w-full max-w-md bg-white rounded-3xl shadow-xl shadow-slate-200/60 border-slate-100 p-8 md:p-10">
{/* ... 内容 ... */}
</div>

📝 代码详解


这里面的学问可大了:



  1. 尺寸控制

    • w-full: 宽度占满父容器(但在 padding 的作用下不会贴边)。
    • max-w-md: 关键限制。在大屏幕上,我们不希望登录框无限拉长,max-w-md (28rem / 448px) 是一个非常舒适的阅读宽度。


  2. 质感营造

    • bg-white: 卡片主体白色。
    • rounded-3xl: 超大圆角!现在流行这种亲和力强的设计,比直角或小圆角更 Modern。
    • shadow-xl shadow-slate-200/60: Tailwind 的黑魔法shadow-xl 给出一个大投影,而 shadow-slate-200/60 则是修改了这个投影的颜色!默认的黑色投影太脏了,用带点蓝紫调的灰色(slate),并且设置透明度(/60),会让卡片看起来像是“悬浮”在空气中,通透感满分。
    • border-slate-100: 极淡的边框,增强边界感,细节决定成败。


  3. 响应式内边距

    • p-8: 默认情况(手机)内边距是 2rem。
    • md:p-10: Mobile First 策略。当屏幕宽度大于 md(768px)时,内边距增加到 2.5rem。大屏大留白,呼吸感就有了。





🧠 第三步:注入灵魂 —— React 状态管理


界面写得再好看,不能动也是白搭。我们要用 React 的 Hooks 来赋予它生命。


import { useState } from 'react';

export default function App() {
// 1. 表单数据状态:单一数据源
const [formData, setFormData] = useState({
email: '',
password: '',
remember: false // 虽然 UI 里没画,但逻辑我们要预留好
});

// 2. UI 交互状态
const [showPassword, setShowPassword] = useState(false); // 密码显隐
const [isLoading, setIsLoading] = useState(false); // 加载中状态

// ...
}

💡 为什么这么设计?


我们没有为 email 和 password 分别创建 state(比如 email, setEmail),而是用一个对象 formData 统一管理。
这样做的好处是:当表单字段变多时(比如注册页有10个空),我们不需要写10个 useState,代码更整洁,扩展性更强。




⚡ 第四步:抽象事件处理 —— 优雅的 handleChange


这是很多新手容易写乱的地方。看仔细了,这一段代码非常通用,建议背诵!


  // 抽象的表单变更处理函数
const handleChange = (e) => {
// 解构出我们需要的信息
// name: 哪个输入框变了?
// value: 变成了什么值?
// type/checked: 专门处理 checkbox
const { name, value, type, checked } = e.target;

// 状态更新
setFormData((prev) => ({
...prev, // 保留之前的其他字段
// 动态属性名:[name]
// 如果是 checkbox 用 checked,否则用 value
[name]: type === 'checkbox' ? checked : value,
}))
}

📝 深度解析



  1. 对象解构const {name, value, ...} = e.target 让代码更清晰。
  2. 函数式更新setFormData((prev) => ...)注意! 永远推荐用这种回调函数的方式更新依赖于旧状态的新状态。这能确保在复杂的异步更新中,你拿到的 prev 永远是最新的。
  3. 计算属性名[name]: ...。ES6 的语法糖,让我们可以用变量 name 作为对象的 key。这意味着这一个函数,可以同时处理 email、password、username 等无数个输入框!这就叫复用



🎨 第五步:表单组件 —— 细节狂魔


接下来是重头戏:输入框。这里我们用到了 Tailwind 极其强大的 grouppeer 特性。


邮箱输入框


<div className="space-y-2">
<label className="text-sm font-medium text-slate-700">Email:</label>

{/* group: 父容器标记 */}
<div className="relative group">

{/* 图标:绝对定位 */}
<div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none text-slate-400 group-focus-within:text-indigo-600 transition-colors">
<Mail size={18} />
</div>

{/* 输入框 */}
<input
type="email"
name="email"
required
value={formData.email}
onChange={handleChange}
placeholder="name@company.com"
className="block w-full pl-11 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl text-slate-900 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-indigo-600/20 focus:border-indigo-600 transition-all"
/>
</div>
</div>

🤯 这里的 CSS 技巧太炸裂了!



  1. 图标变色魔法 (group-focus-within)

    • 我们在父级 div 加了 group 类。
    • 在图标 div 加了 group-focus-within:text-indigo-600
    • 效果:当子元素(input)被聚焦(focus)时,父级检测到 focus-within,通知图标改变颜色!
    • 体验:用户一点输入框,前面的小信封瞬间变成亮紫色,这种交互反馈极大地提升了用户的掌控感。


  2. Input 的精细打磨

    • pl-11: 左边距留大点(2.75rem),因为那里放了图标。
    • focus:ring-2 focus:ring-indigo-600/20: 聚焦时,不要浏览器默认的丑边框,我们要一个 2px 宽、带透明度的紫色光环。
    • focus:border-indigo-600: 同时边框颜色变深。
    • transition-all: 所有的变化(颜色、阴影)都要有过渡动画,拒绝生硬。





🔐 第六步:密码框与显隐切换


密码框多了一个“眼睛”按钮,逻辑稍微复杂一点点。


<div className="relative group">
{/* 左侧锁图标 (同上,略) */}

<input
// 动态类型:根据状态决定是明文还是密文
type={showPassword ? "text" : "password"}
name="password"
// ...
/>

{/* 右侧切换按钮 */}
<button
type="button" // 必须写!否则默认是 submit 会触发表单提交
onClick={() => setShowPassword(!showPassword)}
className="absolute inset-y-0 right-0 pr-4 flex items-center text-slate-400 hover:text-slate-600 transition-colors"
>
{/* 根据状态切换图标 */}
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>

📝 关键点



  1. 动态 Typetype={showPassword ? "text" : "password"}。这是 React 控制 DOM 属性最直接的体现。数据驱动视图,我们不需要手动去操作 DOM 节点的 type 属性。
  2. Button Type:在 <form> 内部的 <button>,如果没有指定 type,默认行为是 submit。如果你点击眼睛图标,页面突然刷新了,肯定是因为你忘了写 type="button"
  3. 图标切换:利用三元运算符 {showPassword ? <EyeOff /> : <Eye />} 在两个图标组件间切换。



🚀 总结


看到这里,你应该已经发现,使用 Tailwind CSS + React 开发界面,实际上是一种搭积木的体验。



  • Tailwind 提供了极其丰富的原子积木(Utility Classes),让你不用写一行 CSS 就能堆砌出精美的样式。
  • React 提供了胶水和传动装置(State & Props),让这些积木动起来,响应用户的操作。

我们学到了什么?



  1. 布局min-h-screen, flex, justify-center 是万能起手式。
  2. 美学:利用 shadow-slate-200/60 这种带颜色的透明阴影制造高级感。
  3. 交互group-focus-within 是处理父子联动交互的神器。
  4. 逻辑:单个 handleChange 处理多个输入框,高效且优雅。
  5. 细节ring, transition, placeholder 等伪类修饰符的组合使用。

课后作业 📝


现在的登录点击后还没有实际效果。你可以尝试完善 handleSubmit 函数,加一个 setTimeout 模拟网络请求,把 isLoading 状态用起来,给按钮加一个“加载中”的转圈圈动画。


前端开发很有趣,Tailwind 让它变得更有趣。希望这篇文章能让你感受到原子化 CSS 的魅力!


喜欢的话,点个赞再走吧!我们下期见!👋




本文代码基于 React 18 + Tailwind CSS 3.x + Lucide React 编写。


作者:神秘的猪头
来源:juejin.cn/post/7591708519449198601

0 个评论

要回复文章请先登录注册