React 樣式語法知識點與案例詳解
作為React初學者,掌握樣式語法是構建美觀UI的關鍵。本文將詳細介紹React中所有主要的樣式方法,并提供詳細注釋的案例代碼。
一、React樣式語法知識點總覽
1. 行內樣式 (Inline Styles)
- 使用
style
屬性,值為JavaScript對象 - 屬性名采用駝峰命名法
- 值為字符串或數字
2. CSS樣式表 (CSS Stylesheets)
- 傳統的CSS文件引入方式
- 使用
className
而非class
3. CSS Modules
- 局部作用域的CSS
- 避免樣式沖突
- 文件名通常為
[name].module.css
4. CSS-in-JS庫 (Styled-components)
- 在JavaScript中編寫CSS
- 動態樣式支持良好
- 組件即樣式
5. 使用Sass/SCSS預處理器
- 支持變量、嵌套、混合等功能
- 文件擴展名為
.scss
或.sass
6. 使用Tailwind CSS
- 實用優先的CSS框架
- 直接在className中使用實用類
7. 條件樣式
- 根據狀態或props動態應用樣式
- 使用三元運算符或邏輯運算符
8. 動態樣式
- 根據數據或用戶交互改變樣式
- 結合狀態管理使用
二、詳細案例代碼
案例1:行內樣式 (Inline Styles)
import React, { useState } from 'react';function InlineStyleExample() {// 定義樣式對象 - 注意駝峰命名法const containerStyle = {padding: '20px',backgroundColor: '#f0f0f0',borderRadius: '8px',margin: '10px 0',fontFamily: 'Arial, sans-serif'};const buttonStyle = {backgroundColor: '#007bff',color: 'white',border: 'none',padding: '10px 20px',borderRadius: '4px',cursor: 'pointer',fontSize: '16px',marginRight: '10px'};const hoverButtonStyle = {...buttonStyle, // 使用展開運算符繼承基礎樣式backgroundColor: '#0056b3', // 覆蓋背景色transform: 'scale(1.05)', // 添加縮放效果transition: 'all 0.3s ease' // 添加過渡效果};// 狀態管理用于動態樣式const [isHovered, setIsHovered] = useState(false);const [fontSize, setFontSize] = useState(16);return (<div style={containerStyle}><h2 style={{ color: '#333', marginBottom: '20px',textAlign: 'center'}}>行內樣式示例</h2>{/* 基礎按鈕樣式 */}<button style={buttonStyle}onMouseEnter={() => setIsHovered(true)}onMouseLeave={() => setIsHovered(false)}>基礎按鈕</button>{/* 動態按鈕樣式 - 根據hover狀態改變 */}<button style={isHovered ? hoverButtonStyle : buttonStyle}>{isHovered ? '鼠標懸停中' : '懸停我'}</button>{/* 根據狀態動態改變字體大小 */}<div style={{ marginTop: '20px' }}><p style={{ fontSize: `${fontSize}px`,transition: 'font-size 0.3s ease'}}>當前字體大小: {fontSize}px</p><button style={buttonStyle}onClick={() => setFontSize(prev => Math.min(prev + 2, 32))}>增大字體</button><button style={buttonStyle}onClick={() => setFontSize(prev => Math.max(prev - 2, 12))}>減小字體</button></div>{/* 復雜的動態樣式示例 */}<div style={{marginTop: '30px',padding: '15px',backgroundColor: isHovered ? '#e9ecef' : '#ffffff',border: `2px solid ${isHovered ? '#007bff' : '#dee2e6'}`,borderRadius: '8px',transition: 'all 0.3s ease',cursor: 'pointer'}} onMouseEnter={() => setIsHovered(true)}onMouseLeave={() => setIsHovered(false)}><p>這個盒子的樣式會根據鼠標懸停狀態動態改變</p><p style={{ fontStyle: 'italic', color: '#6c757d' }}>{isHovered ? '鼠標正在懸停' : '請將鼠標懸停在此處'}</p></div></div>);
}export default InlineStyleExample;
案例2:CSS樣式表 (CSS Stylesheets)
首先創建 StylesheetExample.css
文件:
/* StylesheetExample.css */
.card-container {padding: 20px;background-color: #f8f9fa;border-radius: 8px;margin: 20px 0;box-shadow: 0 2px 4px rgba(0,0,0,0.1);font-family: 'Arial', sans-serif;
}.card-title {color: #343a40;font-size: 24px;margin-bottom: 15px;text-align: center;border-bottom: 2px solid #007bff;padding-bottom: 10px;
}.card-content {background-color: white;padding: 15px;border-radius: 4px;margin: 15px 0;box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}.button-group {display: flex;justify-content: center;gap: 10px;margin-top: 20px;
}.btn {padding: 10px 20px;border: none;border-radius: 4px;cursor: pointer;font-size: 16px;transition: all 0.3s ease;
}.btn-primary {background-color: #007bff;color: white;
}.btn-primary:hover {background-color: #0056b3;transform: translateY(-2px);
}.btn-secondary {background-color: #6c757d;color: white;
}.btn-secondary:hover {background-color: #545b62;transform: translateY(-2px);
}.alert-box {padding: 15px;margin: 15px 0;border-radius: 4px;font-weight: bold;
}.alert-success {background-color: #d4edda;color: #155724;border: 1px solid #c3e6cb;
}.alert-danger {background-color: #f8d7da;color: #721c24;border: 1px solid #f5c6cb;
}/* 響應式設計 */
@media (max-width: 768px) {.card-container {padding: 15px;margin: 10px 0;}.button-group {flex-direction: column;align-items: center;}.btn {width: 100%;max-width: 200px;}
}
然后創建 React 組件:
import React, { useState } from 'react';
import './StylesheetExample.css'; // 導入CSS文件function StylesheetExample() {const [message, setMessage] = useState('');const [messageType, setMessageType] = useState('');const [count, setCount] = useState(0);const handlePrimaryClick = () => {setCount(count + 1);setMessage(`點擊了 ${count + 1} 次主按鈕`);setMessageType('success');};const handleSecondaryClick = () => {setCount(count - 1);setMessage(`點擊了 ${count - 1} 次次按鈕`);setMessageType('danger');};const resetCount = () => {setCount(0);setMessage('計數器已重置');setMessageType('success');};return (<div className="card-container"><h2 className="card-title">CSS樣式表示例</h2><div className="card-content"><p>當前計數: <strong>{count}</strong></p>{/* 條件渲染alert消息 */}{message && (<div className={`alert-box alert-${messageType}`}>{message}</div>)}<div className="button-group"><button className="btn btn-primary" onClick={handlePrimaryClick}>主按鈕 (+1)</button><button className="btn btn-secondary" onClick={handleSecondaryClick}>次按鈕 (-1)</button><button className="btn btn-secondary" onClick={resetCount}>重置</button></div></div>{/* 條件樣式示例 */}<div className="card-content"><h3>條件樣式示例</h3><p>根據計數值改變樣式:</p><div className="alert-box" style={{ backgroundColor: count > 0 ? '#d4edda' : count < 0 ? '#f8d7da' : '#fff3cd',color: count > 0 ? '#155724' : count < 0 ? '#721c24' : '#856404',border: count > 0 ? '1px solid #c3e6cb' : count < 0 ? '1px solid #f5c6cb' : '1px solid #ffeaa7'}}>{count > 0 ? '正數狀態' : count < 0 ? '負數狀態' : '零狀態'}</div></div></div>);
}export default StylesheetExample;
案例3:CSS Modules
首先創建 CSSModulesExample.module.css
文件:
/* CSSModulesExample.module.css */
.container {padding: 20px;background-color: #e9ecef;border-radius: 8px;margin: 20px 0;font-family: 'Arial', sans-serif;
}.title {color: #28a745;font-size: 24px;margin-bottom: 15px;text-align: center;border-bottom: 2px solid #28a745;padding-bottom: 10px;
}.contentBox {background-color: white;padding: 20px;border-radius: 6px;margin: 15px 0;box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}.buttonContainer {display: flex;justify-content: space-around;flex-wrap: wrap;gap: 10px;margin-top: 20px;
}.primaryBtn {padding: 12px 24px;background-color: #28a745;color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 16px;transition: all 0.3s ease;
}.primaryBtn:hover {background-color: #218838;transform: scale(1.05);
}.dangerBtn {padding: 12px 24px;background-color: #dc3545;color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 16px;transition: all 0.3s ease;
}.dangerBtn:hover {background-color: #c82333;transform: scale(1.05);
}.infoBox {padding: 15px;margin: 15px 0;border-radius: 4px;font-weight: 500;
}.info {composes: infoBox;background-color: #cce5ff;color: #004085;border: 1px solid #b8daff;
}.warning {composes: infoBox;background-color: #fff3cd;color: #856404;border: 1px solid #ffeaa7;
}.success {composes: infoBox;background-color: #d4edda;color: #155724;border: 1px solid #c3e6cb;
}/* 動畫效果 */
@keyframes fadeIn {from { opacity: 0; transform: translateY(10px); }to { opacity: 1; transform: translateY(0); }
}.animatedBox {animation: fadeIn 0.5s ease-out;
}/* 響應式設計 */
@media (max-width: 768px) {.container {padding: 15px;margin: 10px 0;}.buttonContainer {flex-direction: column;align-items: center;}.primaryBtn, .dangerBtn {width: 100%;max-width: 250px;}
}
然后創建 React 組件:
import React, { useState, useEffect } from 'react';
import styles from './CSSModulesExample.module.css'; // 導入CSS Modulesfunction CSSModulesExample() {const [count, setCount] = useState(0);const [showMessage, setShowMessage] = useState(false);const [messageType, setMessageType] = useState('info');const [isVisible, setIsVisible] = useState(true);// 模擬組件加載時的動畫效果useEffect(() => {const timer = setTimeout(() => {setShowMessage(true);}, 500);return () => clearTimeout(timer);}, []);const increment = () => {setCount(count + 1);setMessageType('success');setShowMessage(true);// 3秒后隱藏消息setTimeout(() => setShowMessage(false), 3000);};const decrement = () => {setCount(count - 1);setMessageType('warning');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const reset = () => {setCount(0);setMessageType('info');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const toggleVisibility = () => {setIsVisible(!isVisible);};// 根據計數值動態選擇樣式const getDynamicStyle = () => {if (count > 0) return styles.success;if (count < 0) return styles.warning;return styles.info;};return (<div className={styles.container}><h2 className={styles.title}>CSS Modules 示例</h2>{/* 可切換可見性的內容框 */}{isVisible && (<div className={`${styles.contentBox} ${styles.animatedBox}`}><h3>計數器</h3><p>當前值: <strong>{count}</strong></p>{/* 條件渲染的消息框 */}{showMessage && (<div className={styles[messageType]}>{messageType === 'success' && `增加了1,現在是 ${count}`}{messageType === 'warning' && `減少了1,現在是 ${count}`}{messageType === 'info' && `已重置為 ${count}`}</div>)}<div className={styles.buttonContainer}><button className={styles.primaryBtn} onClick={increment}>增加 (+1)</button><button className={styles.dangerBtn} onClick={decrement}>減少 (-1)</button><button className={styles.primaryBtn} onClick={reset}>重置</button></div></div>)}{/* 動態樣式示例 */}<div className={styles.contentBox}><h3>動態樣式示例</h3><div className={getDynamicStyle()}>{count > 0 ? '正數狀態 - 成功樣式' : count < 0 ? '負數狀態 - 警告樣式' : '零狀態 - 信息樣式'}</div></div>{/* 控制組件可見性的按鈕 */}<div className={styles.buttonContainer}><button className={isVisible ? styles.dangerBtn : styles.primaryBtn}onClick={toggleVisibility}>{isVisible ? '隱藏內容' : '顯示內容'}</button></div></div>);
}export default CSSModulesExample;
案例4:Styled-components
首先安裝 styled-components:
npm install styled-components
然后創建組件:
import React, { useState } from 'react';
import styled, { keyframes, css } from 'styled-components';// 定義動畫
const fadeIn = keyframes`from {opacity: 0;transform: translateY(20px);}to {opacity: 1;transform: translateY(0);}
`;// 定義基礎容器樣式
const Container = styled.div`padding: 20px;background-color: ${props => props.theme.backgroundColor || '#f8f9fa'};border-radius: 8px;margin: 20px 0;box-shadow: 0 2px 10px rgba(0,0,0,0.1);font-family: 'Arial', sans-serif;animation: ${fadeIn} 0.6s ease-out;
`;// 標題樣式
const Title = styled.h2`color: ${props => props.theme.titleColor || '#343a40'};font-size: 24px;margin-bottom: 15px;text-align: center;border-bottom: 2px solid ${props => props.theme.accentColor || '#007bff'};padding-bottom: 10px;position: relative;&::after {content: '';position: absolute;bottom: -2px;left: 50%;width: 60px;height: 2px;background-color: ${props => props.theme.accentColor || '#007bff'};transform: translateX(-50%);}
`;// 內容框樣式
const ContentBox = styled.div`background-color: white;padding: 20px;border-radius: 6px;margin: 15px 0;box-shadow: 0 2px 8px rgba(0,0,0,0.05);transition: all 0.3s ease;&:hover {box-shadow: 0 4px 15px rgba(0,0,0,0.1);transform: translateY(-2px);}
`;// 按鈕組容器
const ButtonGroup = styled.div`display: flex;justify-content: center;flex-wrap: wrap;gap: 10px;margin-top: 20px;
`;// 基礎按鈕樣式
const BaseButton = styled.button`padding: 12px 24px;border: none;border-radius: 4px;cursor: pointer;font-size: 16px;font-weight: 500;transition: all 0.3s ease;position: relative;overflow: hidden;&:before {content: '';position: absolute;top: 0;left: -100%;width: 100%;height: 100%;background: rgba(255,255,255,0.2);transition: all 0.5s ease;}&:hover:before {left: 100%;}&:hover {transform: translateY(-2px) scale(1.03);box-shadow: 0 4px 15px rgba(0,0,0,0.2);}&:active {transform: translateY(0) scale(0.98);}
`;// 主按鈕樣式 - 擴展BaseButton
const PrimaryButton = styled(BaseButton)`background-color: ${props => props.theme.primaryColor || '#007bff'};color: white;&:hover {background-color: ${props => props.theme.primaryHoverColor || '#0056b3'};}
`;// 危險按鈕樣式 - 擴展BaseButton
const DangerButton = styled(BaseButton)`background-color: ${props => props.theme.dangerColor || '#dc3545'};color: white;&:hover {background-color: ${props => props.theme.dangerHoverColor || '#c82333'};}
`;// 信息框樣式 - 根據類型動態變化
const InfoBox = styled.div`padding: 15px;margin: 15px 0;border-radius: 4px;font-weight: 500;text-align: center;animation: ${fadeIn} 0.4s ease-out;${props => props.type === 'success' && css`background-color: #d4edda;color: #155724;border: 1px solid #c3e6cb;border-left: 5px solid #28a745;`}${props => props.type === 'warning' && css`background-color: #fff3cd;color: #856404;border: 1px solid #ffeaa7;border-left: 5px solid #ffc107;`}${props => props.type === 'danger' && css`background-color: #f8d7da;color: #721c24;border: 1px solid #f5c6cb;border-left: 5px solid #dc3545;`}${props => props.type === 'info' && css`background-color: #cce5ff;color: #004085;border: 1px solid #b8daff;border-left: 5px solid #17a2b8;`}
`;// 主題配置
const theme = {backgroundColor: '#f8f9fa',titleColor: '#2c3e50',accentColor: '#3498db',primaryColor: '#3498db',primaryHoverColor: '#2980b9',dangerColor: '#e74c3c',dangerHoverColor: '#c0392b'
};function StyledComponentsExample() {const [count, setCount] = useState(0);const [showMessage, setShowMessage] = useState(false);const [messageType, setMessageType] = useState('info');const [themeMode, setThemeMode] = useState('light');// 切換主題const toggleTheme = () => {setThemeMode(themeMode === 'light' ? 'dark' : 'light');};// 自定義暗色主題const darkTheme = {backgroundColor: '#2c3e50',titleColor: '#ecf0f1',accentColor: '#3498db',primaryColor: '#3498db',primaryHoverColor: '#2980b9',dangerColor: '#e74c3c',dangerHoverColor: '#c0392b'};const currentTheme = themeMode === 'dark' ? darkTheme : theme;const increment = () => {setCount(count + 1);setMessageType('success');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const decrement = () => {setCount(count - 1);setMessageType('warning');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const reset = () => {setCount(0);setMessageType('info');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};return (<Container theme={currentTheme}><Title theme={currentTheme}>Styled-components 示例</Title><ContentBox><h3 style={{ color: currentTheme.titleColor, textAlign: 'center' }}>計數器: {count}</h3>{/* 條件渲染的消息框 */}{showMessage && (<InfoBox type={messageType}>{messageType === 'success' && `計數器增加到 ${count}`}{messageType === 'warning' && `計數器減少到 ${count}`}{messageType === 'info' && `計數器重置為 ${count}`}</InfoBox>)}<ButtonGroup><PrimaryButton onClick={increment}>增加 (+1)</PrimaryButton><DangerButton onClick={decrement}>減少 (-1)</DangerButton><PrimaryButton onClick={reset}>重置</PrimaryButton></ButtonGroup></ContentBox>{/* 主題切換示例 */}<ContentBox><h3 style={{ color: currentTheme.titleColor, textAlign: 'center' }}>主題: {themeMode === 'light' ? '淺色模式' : '深色模式'}</h3><ButtonGroup><PrimaryButton onClick={toggleTheme}>切換到 {themeMode === 'light' ? '深色' : '淺色'} 模式</PrimaryButton></ButtonGroup></ContentBox>{/* 動態樣式示例 */}<ContentBox><h3 style={{ color: currentTheme.titleColor, textAlign: 'center' }}>動態樣式示例</h3><InfoBox type={count > 0 ? 'success' : count < 0 ? 'danger' : 'info'}>{count > 0 ? '正數 - 成功樣式' : count < 0 ? '負數 - 危險樣式' : '零 - 信息樣式'}</InfoBox></ContentBox></Container>);
}// 包裝組件以應用主題
const ThemedStyledComponentsExample = () => (<StyledComponentsExample />
);export default ThemedStyledComponentsExample;
案例5:Sass/SCSS 集成
首先安裝 node-sass 或 sass:
npm install sass
創建 SassExample.scss
文件:
// SassExample.scss
// 定義變量
$primary-color: #007bff;
$secondary-color: #6c757d;
$success-color: #28a745;
$danger-color: #dc3545;
$warning-color: #ffc107;
$info-color: #17a2b8;$border-radius: 8px;
$box-shadow: 0 2px 10px rgba(0,0,0,0.1);// 混合
@mixin button-style($bg-color, $hover-color) {padding: 12px 24px;background-color: $bg-color;color: white;border: none;border-radius: 4px;cursor: pointer;font-size: 16px;font-weight: 500;transition: all 0.3s ease;&:hover {background-color: $hover-color;transform: translateY(-2px) scale(1.03);box-shadow: 0 4px 15px rgba(0,0,0,0.2);}&:active {transform: translateY(0) scale(0.98);}
}// 容器樣式
.sass-container {padding: 20px;background-color: #f8f9fa;border-radius: $border-radius;margin: 20px 0;box-shadow: $box-shadow;font-family: 'Arial', sans-serif;// 嵌套樣式.title {color: #343a40;font-size: 24px;margin-bottom: 15px;text-align: center;border-bottom: 2px solid $primary-color;padding-bottom: 10px;position: relative;&::after {content: '';position: absolute;bottom: -2px;left: 50%;width: 60px;height: 2px;background-color: $primary-color;transform: translateX(-50%);}}
}// 內容框
.sass-content-box {background-color: white;padding: 20px;border-radius: 6px;margin: 15px 0;box-shadow: 0 2px 8px rgba(0,0,0,0.05);transition: all 0.3s ease;&:hover {box-shadow: 0 4px 15px rgba(0,0,0,0.1);transform: translateY(-2px);}// 嵌套的標題h3 {color: #343a40;text-align: center;margin-bottom: 15px;}
}// 按鈕組
.sass-button-group {display: flex;justify-content: center;flex-wrap: wrap;gap: 10px;margin-top: 20px;
}// 使用混合定義按鈕
.sass-primary-btn {@include button-style($primary-color, darken($primary-color, 10%));
}.sass-success-btn {@include button-style($success-color, darken($success-color, 10%));
}.sass-danger-btn {@include button-style($danger-color, darken($danger-color, 10%));
}.sass-warning-btn {@include button-style($warning-color, darken($warning-color, 10%));color: #212529; // 警告按鈕文字顏色
}// 信息框
.sass-info-box {padding: 15px;margin: 15px 0;border-radius: 4px;font-weight: 500;text-align: center;&.success {background-color: lighten($success-color, 40%);color: darken($success-color, 20%);border: 1px solid lighten($success-color, 30%);border-left: 5px solid $success-color;}&.warning {background-color: lighten($warning-color, 40%);color: darken($warning-color, 20%);border: 1px solid lighten($warning-color, 30%);border-left: 5px solid $warning-color;}&.danger {background-color: lighten($danger-color, 40%);color: darken($danger-color, 20%);border: 1px solid lighten($danger-color, 30%);border-left: 5px solid $danger-color;}&.info {background-color: lighten($info-color, 40%);color: darken($info-color, 20%);border: 1px solid lighten($info-color, 30%);border-left: 5px solid $info-color;}
}// 響應式設計
@media (max-width: 768px) {.sass-container {padding: 15px;margin: 10px 0;}.sass-button-group {flex-direction: column;align-items: center;}.sass-primary-btn, .sass-success-btn, .sass-danger-btn, .sass-warning-btn {width: 100%;max-width: 250px;}
}// 暗色主題
.dark-theme {.sass-container {background-color: #2c3e50;.title {color: #ecf0f1;border-bottom-color: $info-color;&::after {background-color: $info-color;}}}.sass-content-box {background-color: #34495e;h3 {color: #ecf0f1;}}
}
創建 React 組件:
import React, { useState } from 'react';
import './SassExample.scss'; // 導入SCSS文件function SassExample() {const [count, setCount] = useState(0);const [showMessage, setShowMessage] = useState(false);const [messageType, setMessageType] = useState('info');const [theme, setTheme] = useState('light');const increment = () => {setCount(count + 1);setMessageType('success');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const decrement = () => {setCount(count - 1);setMessageType('warning');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const reset = () => {setCount(0);setMessageType('info');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const toggleTheme = () => {setTheme(theme === 'light' ? 'dark' : 'light');};return (<div className={`sass-container ${theme === 'dark' ? 'dark-theme' : ''}`}><h2 className="title">Sass/SCSS 示例</h2><div className="sass-content-box"><h3>計數器: {count}</h3>{/* 條件渲染的消息框 */}{showMessage && (<div className={`sass-info-box ${messageType}`}>{messageType === 'success' && `計數器增加到 ${count}`}{messageType === 'warning' && `計數器減少到 ${count}`}{messageType === 'info' && `計數器重置為 ${count}`}</div>)}<div className="sass-button-group"><button className="sass-primary-btn" onClick={increment}>增加 (+1)</button><button className="sass-danger-btn" onClick={decrement}>減少 (-1)</button><button className="sass-success-btn" onClick={reset}>重置</button></div></div>{/* 主題切換 */}<div className="sass-content-box"><h3>當前主題: {theme === 'light' ? '淺色模式' : '深色模式'}</h3><div className="sass-button-group"><button className="sass-warning-btn" onClick={toggleTheme}>切換到 {theme === 'light' ? '深色' : '淺色'} 模式</button></div></div>{/* 動態樣式示例 */}<div className="sass-content-box"><h3>動態樣式示例</h3><div className={`sass-info-box ${count > 0 ? 'success' : count < 0 ? 'danger' : 'info'}`}>{count > 0 ? '正數 - 成功樣式' : count < 0 ? '負數 - 危險樣式' : '零 - 信息樣式'}</div></div></div>);
}export default SassExample;
案例6:Tailwind CSS
首先安裝 Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
配置 tailwind.config.js
:
/** @type {import('tailwindcss').Config} */
module.exports = {content: ["./src/**/*.{js,jsx,ts,tsx}",],theme: {extend: {},},plugins: [],
}
在 src/index.css
中添加:
@tailwind base;
@tailwind components;
@tailwind utilities;
創建組件:
import React, { useState } from 'react';function TailwindExample() {const [count, setCount] = useState(0);const [showMessage, setShowMessage] = useState(false);const [messageType, setMessageType] = useState('info');const [darkMode, setDarkMode] = useState(false);const increment = () => {setCount(count + 1);setMessageType('success');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const decrement = () => {setCount(count - 1);setMessageType('warning');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const reset = () => {setCount(0);setMessageType('info');setShowMessage(true);setTimeout(() => setShowMessage(false), 3000);};const toggleDarkMode = () => {setDarkMode(!darkMode);};// 根據消息類型返回相應的Tailwind CSS類const getMessageClasses = () => {switch (messageType) {case 'success':return 'bg-green-100 border border-green-400 text-green-700';case 'warning':return 'bg-yellow-100 border border-yellow-400 text-yellow-700';case 'danger':return 'bg-red-100 border border-red-400 text-red-700';case 'info':return 'bg-blue-100 border border-blue-400 text-blue-700';default:return 'bg-gray-100 border border-gray-400 text-gray-700';}};// 根據計數值返回相應的動態樣式const getDynamicClasses = () => {if (count > 0) return 'bg-green-100 border-l-4 border-green-500 text-green-700';if (count < 0) return 'bg-red-100 border-l-4 border-red-500 text-red-700';return 'bg-blue-100 border-l-4 border-blue-500 text-blue-700';};return (<div className={`p-6 rounded-lg shadow-lg transition-all duration-300 ${darkMode ? 'bg-gray-800 text-white' : 'bg-gray-100'}`}><h2 className={`text-2xl font-bold mb-4 text-center pb-2 border-b-2 ${darkMode ? 'border-blue-400 text-blue-300' : 'border-blue-500 text-gray-800'}`}>Tailwind CSS 示例</h2><div className={`p-6 rounded-lg shadow-md transition-all duration-300 hover:shadow-lg hover:-translate-y-1 ${darkMode ? 'bg-gray-700' : 'bg-white'}`}><h3 className={`text-xl font-semibold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-800'}`}>計數器: <span className="font-bold">{count}</span></h3>{/* 條件渲染的消息框 */}{showMessage && (<div className={`p-4 mb-4 rounded border-l-4 ${getMessageClasses()} animate-fade-in`}><p className="font-medium">{messageType === 'success' && `計數器增加到 ${count}`}{messageType === 'warning' && `計數器減少到 ${count}`}{messageType === 'info' && `計數器重置為 ${count}`}</p></div>)}<div className="flex flex-wrap justify-center gap-3 mt-6"><button className="px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg shadow transition-all duration-300 transform hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"onClick={increment}>增加 (+1)</button><button className="px-6 py-3 bg-red-500 hover:bg-red-600 text-white font-medium rounded-lg shadow transition-all duration-300 transform hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"onClick={decrement}>減少 (-1)</button><button className="px-6 py-3 bg-green-500 hover:bg-green-600 text-white font-medium rounded-lg shadow transition-all duration-300 transform hover:scale-105 active:scale-95 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-opacity-50"onClick={reset}>重置</button></div></div>{/* 主題切換 */}<div className={`mt-6 p-6 rounded-lg shadow-md transition-all duration-300 ${darkMode ? 'bg-gray-700' : 'bg-white'}`}><h3 className={`text-xl font-semibold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-800'}`}>當前主題: <span className="font-bold">{darkMode ? '深色模式' : '淺色模式'}</span></h3><div className="flex justify-center"><button className={`px-6 py-3 font-medium rounded-lg shadow transition-all duration-300 transform hover:scale-105 active:scale-95 focus:outline-none ${darkMode ? 'bg-yellow-500 hover:bg-yellow-600 text-white' : 'bg-gray-800 hover:bg-gray-900 text-white'}`}onClick={toggleDarkMode}>切換到 {darkMode ? '淺色' : '深色'} 模式</button></div></div>{/* 動態樣式示例 */}<div className={`mt-6 p-6 rounded-lg shadow-md transition-all duration-300 ${darkMode ? 'bg-gray-700' : 'bg-white'}`}><h3 className={`text-xl font-semibold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-800'}`}>動態樣式示例</h3><div className={`p-4 rounded border-l-4 ${getDynamicClasses()}`}><p className="font-medium">{count > 0 ? '正數 - 成功樣式' : count < 0 ? '負數 - 危險樣式' : '零 - 信息樣式'}</p></div></div>{/* 響應式設計示例 */}<div className={`mt-6 p-6 rounded-lg shadow-md transition-all duration-300 ${darkMode ? 'bg-gray-700' : 'bg-white'}`}><h3 className={`text-xl font-semibold text-center mb-4 ${darkMode ? 'text-white' : 'text-gray-800'}`}>響應式設計示例</h3><div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"><div className={`p-4 rounded-lg text-center transition-all duration-300 hover:shadow-lg ${darkMode ? 'bg-gray-600 hover:bg-gray-500' : 'bg-blue-50 hover:bg-blue-100'}`}><h4 className="font-bold">移動端</h4><p className="text-sm">單列布局</p></div><div className={`p-4 rounded-lg text-center transition-all duration-300 hover:shadow-lg ${darkMode ? 'bg-gray-600 hover:bg-gray-500' : 'bg-green-50 hover:bg-green-100'}`}><h4 className="font-bold">平板端</h4><p className="text-sm">雙列布局</p></div><div className={`p-4 rounded-lg text-center transition-all duration-300 hover:shadow-lg ${darkMode ? 'bg-gray-600 hover:bg-gray-500' : 'bg-purple-50 hover:bg-purple-100'}`}><h4 className="font-bold">桌面端</h4><p className="text-sm">三列布局</p></div></div></div></div>);
}export default TailwindExample;
案例7:綜合應用 - 完整的樣式實踐
import React, { useState, useEffect } from 'react';
import './ComprehensiveExample.css'; // 基礎CSS
import styles from './ComprehensiveExample.module.css'; // CSS Modules// 行內樣式對象
const inlineStyles = {heroSection: {padding: '60px 20px',textAlign: 'center',background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',color: 'white',borderRadius: '12px',margin: '20px 0',boxShadow: '0 10px 30px rgba(0,0,0,0.2)'},cardGrid: {display: 'grid',gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',gap: '20px',marginTop: '30px'}
};function ComprehensiveExample() {const [activeTab, setActiveTab] = useState('all');const [searchTerm, setSearchTerm] = useState('');const [darkMode, setDarkMode] = useState(false);const [animatedCards, setAnimatedCards] = useState([]);// 模擬數據const projects = [{ id: 1, title: '電商網站', category: 'web', status: 'completed', progress: 100 },{ id: 2, title: '移動應用', category: 'mobile', status: 'in-progress', progress: 65 },{ id: 3, title: '數據分析平臺', category: 'web', status: 'planning', progress: 20 },{ id: 4, title: 'AI聊天機器人', category: 'ai', status: 'in-progress', progress: 80 },{ id: 5, title: '物聯網系統', category: 'iot', status: 'completed', progress: 100 },{ id: 6, title: '區塊鏈應用', category: 'blockchain', status: 'planning', progress: 10 }];// 過濾項目const filteredProjects = projects.filter(project => {const matchesTab = activeTab === 'all' || project.category === activeTab;const matchesSearch = project.title.toLowerCase().includes(searchTerm.toLowerCase());return matchesTab && matchesSearch;});// 切換暗色模式const toggleDarkMode = () => {setDarkMode(!darkMode);};// 動畫效果useEffect(() => {const timer = setTimeout(() => {setAnimatedCards(filteredProjects.map(p => p.id));}, 300);return () => clearTimeout(timer);}, [filteredProjects]);// 獲取狀態相關的樣式類const getStatusClass = (status) => {switch (status) {case 'completed': return styles.completed;case 'in-progress': return styles.inProgress;case 'planning': return styles.planning;default: return '';}};// 獲取進度條樣式const getProgressStyle = (progress) => ({width: `${progress}%`,height: '8px',borderRadius: '4px',backgroundColor: progress === 100 ? '#28a745' : '#007bff',transition: 'width 0.5s ease-in-out'});return (<div className={`${styles.container} ${darkMode ? styles.darkMode : ''}`}>{/* 英雄區域 - 行內樣式 */}<div style={inlineStyles.heroSection}><h1 className={styles.heroTitle}>項目管理系統</h1><p className={styles.heroSubtitle}>管理您的所有項目,跟蹤進度,提高效率</p><button className={styles.toggleButton}onClick={toggleDarkMode}aria-label={darkMode ? '切換到淺色模式' : '切換到深色模式'}>{darkMode ? '?? 淺色模式' : '🌙 深色模式'}</button></div>{/* 搜索和過濾 - CSS Modules */}<div className={styles.controls}><inputtype="text"placeholder="搜索項目..."value={searchTerm}onChange={(e) => setSearchTerm(e.target.value)}className={styles.searchInput}/><div className={styles.tabContainer}>{['all', 'web', 'mobile', 'ai', 'iot', 'blockchain'].map(tab => (<buttonkey={tab}className={`${styles.tabButton} ${activeTab === tab ? styles.activeTab : ''}`}onClick={() => setActiveTab(tab)}>{tab === 'all' ? '全部' : tab}</button>))}</div></div>{/* 項目卡片網格 - 行內樣式 */}<div style={inlineStyles.cardGrid}>{filteredProjects.length === 0 ? (<div className={styles.noResults}><p>沒有找到匹配的項目</p><button className={styles.resetButton}onClick={() => {setSearchTerm('');setActiveTab('all');}}>重置篩選條件</button></div>) : (filteredProjects.map(project => (<divkey={project.id}className={`${styles.card} ${animatedCards.includes(project.id) ? styles.animateIn : ''} ${getStatusClass(project.status)}`}><div className={styles.cardHeader}><h3 className={styles.cardTitle}>{project.title}</h3><span className={`${styles.statusBadge} ${styles[project.status]}`}>{project.status === 'completed' && '已完成'}{project.status === 'in-progress' && '進行中'}{project.status === 'planning' && '規劃中'}</span></div><div className={styles.cardBody}><p className={styles.category}><strong>類別:</strong> {project.category}</p><div className={styles.progressContainer}><div className={styles.progressLabel}><span>進度</span><span>{project.progress}%</span></div><div className={styles.progressBarBackground}><div style={getProgressStyle(project.progress)}></div></div></div></div><div className={styles.cardFooter}><button className={styles.actionButton}>查看詳情</button><button className={styles.secondaryButton}>編輯</button></div></div>)))}</div>{/* 統計信息 - CSS */}<div className="stats-container"><div className="stat-card"><h4>總項目數</h4><p className="stat-number">{projects.length}</p></div><div className="stat-card"><h4>已完成</h4><p className="stat-number">{projects.filter(p => p.status === 'completed').length}</p></div><div className="stat-card"><h4>進行中</h4><p className="stat-number">{projects.filter(p => p.status === 'in-progress').length}</p></div><div className="stat-card"><h4>平均進度</h4><p className="stat-number">{Math.round(projects.reduce((sum, p) => sum + p.progress, 0) / projects.length)}%</p></div></div></div>);
}export default ComprehensiveExample;
對應的 ComprehensiveExample.css
:
/* ComprehensiveExample.css */
.stats-container {display: grid;grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));gap: 20px;margin: 40px 0;
}.stat-card {background: white;padding: 20px;border-radius: 8px;text-align: center;box-shadow: 0 2px 10px rgba(0,0,0,0.05);transition: all 0.3s ease;
}.stat-card:hover {transform: translateY(-5px);box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}.stat-card h4 {color: #666;font-size: 16px;margin-bottom: 10px;
}.stat-number {font-size: 32px;font-weight: bold;color: #333;
}/* 響應式設計 */
@media (max-width: 768px) {.stats-container {grid-template-columns: 1fr;}.stat-card {padding: 15px;}.stat-number {font-size: 24px;}
}
對應的 ComprehensiveExample.module.css
:
/* ComprehensiveExample.module.css */
.container {max-width: 1200px;margin: 0 auto;padding: 20px;font-family: 'Arial', sans-serif;
}.container.darkMode {background-color: #1a1a1a;color: #f0f0f0;
}.heroTitle {font-size: 2.5rem;margin: 0 0 15px 0;font-weight: 700;text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}.heroSubtitle {font-size: 1.2rem;margin: 0 0 30px 0;opacity: 0.9;
}.toggleButton {background: rgba(255,255,255,0.2);border: 2px solid rgba(255,255,255,0.3);color: white;padding: 12px 24px;border-radius: 30px;font-size: 16px;cursor: pointer;transition: all 0.3s ease;backdrop-filter: blur(10px);
}.toggleButton:hover {background: rgba(255,255,255,0.3);transform: translateY(-2px);
}.controls {display: flex;flex-wrap: wrap;gap: 20px;margin: 30px 0;padding: 20px;background: white;border-radius: 12px;box-shadow: 0 2px 15px rgba(0,0,0,0.05);
}.searchInput {flex: 1;min-width: 250px;padding: 12px 16px;border: 2px solid #e0e0e0;border-radius: 8px;font-size: 16px;transition: border-color 0.3s ease;
}.searchInput:focus {outline: none;border-color: #007bff;box-shadow: 0 0 0 3px rgba(0,38,155,0.1);
}.tabContainer {display: flex;flex-wrap: wrap;gap: 10px;
}.tabButton {padding: 8px 16px;background: #f0f0f0;border: none;border-radius: 20px;cursor: pointer;font-size: 14px;font-weight: 500;transition: all 0.3s ease;
}.tabButton:hover {background: #e0e0e0;
}.tabButton.activeTab {background: #007bff;color: white;
}.tabButton.activeTab:hover {background: #0056b3;
}.card {background: white;border-radius: 12px;overflow: hidden;box-shadow: 0 4px 20px rgba(0,0,0,0.08);transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);opacity: 0;transform: translateY(30px);
}.card.animateIn {opacity: 1;transform: translateY(0);
}.card:hover {transform: translateY(-10px) scale(1.02);box-shadow: 0 10px 30px rgba(0,0,0,0.15);
}.cardHeader {padding: 20px;border-bottom: 1px solid #eee;display: flex;justify-content: space-between;align-items: center;
}.cardTitle {margin: 0;font-size: 1.3rem;color: #333;font-weight: 600;
}.statusBadge {padding: 6px 12px;border-radius: 20px;font-size: 12px;font-weight: 600;text-transform: uppercase;letter-spacing: 0.5px;
}.completed {background: #d4edda;color: #155724;border-left: 4px solid #28a745;
}.inProgress {background: #cce5ff;color: #004085;border-left: 4px solid #007bff;
}.planning {background: #fff3cd;color: #856404;border-left: 4px solid #ffc107;
}.cardBody {padding: 20px;
}.category {color: #666;margin: 0 0 20px 0;font-size: 14px;
}.progressContainer {margin: 20px 0;
}.progressLabel {display: flex;justify-content: space-between;align-items: center;margin-bottom: 8px;font-size: 14px;color: #555;
}.progressBarBackground {width: 100%;height: 8px;background: #f0f0f0;border-radius: 4px;overflow: hidden;
}.cardFooter {padding: 20px;display: flex;gap: 10px;border-top: 1px solid #eee;
}.actionButton {flex: 1;padding: 10px;background: #007bff;color: white;border: none;border-radius: 6px;cursor: pointer;font-size: 14px;font-weight: 500;transition: all 0.3s ease;
}.actionButton:hover {background: #0056b3;transform: translateY(-2px);
}.secondaryButton {padding: 10px 15px;background: #f0f0f0;color: #555;border: none;border-radius: 6px;cursor: pointer;font-size: 14px;font-weight: 500;transition: all 0.3s ease;
}.secondaryButton:hover {background: #e0e0e0;transform: translateY(-2px);
}.noResults {grid-column: 1 / -1;text-align: center;padding: 60px 20px;background: #f8f9fa;border-radius: 12px;border: 2px dashed #dee2e6;
}.noResults p {font-size: 1.2rem;color: #666;margin: 0 0 20px 0;
}.resetButton {background: #007bff;color: white;border: none;padding: 12px 24px;border-radius: 6px;cursor: pointer;font-size: 16px;font-weight: 500;transition: all 0.3s ease;
}.resetButton:hover {background: #0056b3;transform: translateY(-2px);
}/* 暗色模式樣式 */
.container.darkMode .controls {background: #2a2a2a;box-shadow: 0 2px 15px rgba(0,0,0,0.2);
}.container.darkMode .searchInput {background: #333;color: #fff;border-color: #444;
}.container.darkMode .searchInput:focus {border-color: #007bff;box-shadow: 0 0 0 3px rgba(0,123,255,0.2);
}.container.darkMode .tabButton {background: #333;color: #ccc;
}.container.darkMode .tabButton:hover {background: #444;
}.container.darkMode .tabButton.activeTab {background: #007bff;color: white;
}.container.darkMode .card {background: #2a2a2a;box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}.container.darkMode .cardHeader {border-bottom-color: #444;
}.container.darkMode .cardTitle {color: #fff;
}.container.darkMode .category {color: #aaa;
}.container.darkMode .progressLabel {color: #ccc;
}.container.darkMode .progressBarBackground {background: #444;
}.container.darkMode .cardFooter {border-top-color: #444;
}.container.darkMode .secondaryButton {background: #333;color: #ccc;
}.container.darkMode .secondaryButton:hover {background: #444;
}/* 響應式設計 */
@media (max-width: 768px) {.container {padding: 15px;}.heroTitle {font-size: 2rem;}.controls {flex-direction: column;padding: 15px;}.searchInput {min-width: auto;}.tabContainer {justify-content: center;}.cardHeader {flex-direction: column;gap: 10px;text-align: center;}.cardFooter {flex-direction: column;}.actionButton, .secondaryButton {width: 100%;}
}
三、總結與最佳實踐
1. 選擇合適的樣式方法
- 小型項目/組件: 行內樣式或CSS樣式表
- 中大型項目: CSS Modules或Styled-components
- 團隊協作: CSS Modules(避免命名沖突)
- 設計系統: Styled-components(主題支持好)
- 快速原型: Tailwind CSS
2. 最佳實踐
- 命名規范: 使用有意義的類名,遵循BEM或類似規范
- 組件化: 每個組件有自己的樣式,避免全局污染
- 性能考慮: 避免過度使用行內樣式,特別是大型列表
- 可維護性: 將樣式與邏輯分離,使用CSS變量或主題
- 響應式設計: 始終考慮不同屏幕尺寸的適配
3. 常見問題解決
- 樣式不生效: 檢查className拼寫、CSS文件是否正確導入
- 樣式沖突: 使用CSS Modules或更具體的類名
- 性能問題: 避免在render中創建樣式對象,使用useMemo
- 動態樣式: 使用狀態管理,避免直接操作DOM
4. 學習建議
- 從CSS樣式表開始,熟悉基礎
- 學習CSS Modules,理解局部作用域
- 嘗試Styled-components,體驗CSS-in-JS
- 掌握Tailwind CSS,提高開發效率
- 學習Sass/SCSS,增強CSS能力
通過以上全面的介紹和詳細的案例代碼,你應該能夠掌握React中各種樣式方法的使用。記住,選擇適合自己項目和團隊的樣式方案最重要,不必追求最新最酷的技術,而要注重可維護性和團隊協作效率。