CSS属性 #

基本概念 #

css prop 是 Emotion 提供的一种在 JSX 元素上直接添加样式的方式。它比传统内联样式更强大,支持完整的 CSS 语法。

启用 css prop #

方式一:Pragma 注释 #

在文件顶部添加 jsx pragma:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

function App() {
  return <div css={css`color: red;`}>Hello</div>
}

方式二:Babel 配置 #

.babelrc 中配置:

json
{
  "presets": ["@babel/preset-react"],
  "plugins": ["@emotion"]
}

方式三:TypeScript 配置 #

tsconfig.json 中配置:

json
{
  "compilerOptions": {
    "jsxImportSource": "@emotion/react"
  }
}

样式语法 #

模板字符串语法 #

使用模板字符串编写标准 CSS:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

function App() {
  return (
    <div
      css={css`
        padding: 20px;
        background-color: #f5f5f5;
        border-radius: 8px;
        
        &:hover {
          background-color: #e0e0e0;
        }
      `}
    >
      Hover me
    </div>
  )
}

对象语法 #

使用 JavaScript 对象定义样式:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const containerStyle = css({
  padding: '20px',
  backgroundColor: '#f5f5f5',
  borderRadius: '8px',
  ':hover': {
    backgroundColor: '#e0e0e0',
  },
})

function App() {
  return <div css={containerStyle}>Hover me</div>
}

属性命名规则 #

对象语法使用驼峰命名:

CSS 属性 对象属性
background-color backgroundColor
border-radius borderRadius
font-size fontSize
margin-top marginTop
padding-left paddingLeft

动态样式 #

基于 Props #

根据组件 props 动态设置样式:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

function Alert({ type, children }) {
  const alertStyles = css`
    padding: 12px 16px;
    border-radius: 4px;
    margin-bottom: 12px;
    
    background-color: ${type === 'success' ? '#d4edda' : 
                       type === 'warning' ? '#fff3cd' : 
                       type === 'error' ? '#f8d7da' : '#cce5ff'};
    
    color: ${type === 'success' ? '#155724' : 
             type === 'warning' ? '#856404' : 
             type === 'error' ? '#721c24' : '#004085'};
    
    border: 1px solid ${type === 'success' ? '#c3e6cb' : 
                        type === 'warning' ? '#ffeeba' : 
                        type === 'error' ? '#f5c6cb' : '#b8daff'};
  `
  
  return <div css={alertStyles}>{children}</div>
}

function App() {
  return (
    <>
      <Alert type="success">操作成功!</Alert>
      <Alert type="warning">请注意!</Alert>
      <Alert type="error">发生错误!</Alert>
      <Alert type="info">提示信息</Alert>
    </>
  )
}

基于 State #

根据组件 state 动态设置样式:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import { useState } from 'react'

function ToggleButton() {
  const [isOn, setIsOn] = useState(false)
  
  return (
    <button
      onClick={() => setIsOn(!isOn)}
      css={css`
        padding: 12px 24px;
        border: none;
        border-radius: 20px;
        cursor: pointer;
        font-size: 16px;
        font-weight: bold;
        transition: all 0.3s ease;
        
        background-color: ${isOn ? '#28a745' : '#dc3545'};
        color: white;
        
        &:hover {
          opacity: 0.9;
        }
      `}
    >
      {isOn ? 'ON' : 'OFF'}
    </button>
  )
}

基于 Theme #

使用主题中的值:

jsx
/** @jsxImportSource @emotion/react */
import { css, useTheme } from '@emotion/react'

function ThemedButton({ children }) {
  const theme = useTheme()
  
  return (
    <button
      css={css`
        padding: 12px 24px;
        background-color: ${theme.colors.primary};
        color: ${theme.colors.white};
        border: none;
        border-radius: ${theme.borderRadius.medium};
        cursor: pointer;
        
        &:hover {
          background-color: ${theme.colors.primaryDark};
        }
      `}
    >
      {children}
    </button>
  )
}

样式组合 #

数组组合 #

使用数组组合多个样式:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const baseButton = css`
  padding: 8px 16px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-weight: bold;
  transition: all 0.2s ease;
`

const primaryButton = css`
  background-color: #007bff;
  color: white;
  
  &:hover {
    background-color: #0056b3;
  }
`

const largeButton = css`
  padding: 12px 24px;
  font-size: 18px;
`

function Button({ variant, size, children }) {
  return (
    <button
      css={[
        baseButton,
        variant === 'primary' && primaryButton,
        size === 'large' && largeButton,
      ]}
    >
      {children}
    </button>
  )
}

条件组合 #

根据条件动态组合样式:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const inputBase = css`
  padding: 10px 14px;
  border: 2px solid #ddd;
  border-radius: 4px;
  font-size: 14px;
  outline: none;
  transition: border-color 0.2s ease;
  
  &:focus {
    border-color: #007bff;
  }
`

const inputError = css`
  border-color: #dc3545;
  
  &:focus {
    border-color: #dc3545;
  }
`

const inputDisabled = css`
  background-color: #f5f5f5;
  cursor: not-allowed;
  opacity: 0.6;
`

function Input({ error, disabled, ...props }) {
  return (
    <input
      css={[
        inputBase,
        error && inputError,
        disabled && inputDisabled,
      ]}
      disabled={disabled}
      {...props}
    />
  )
}

提取样式 #

提取为常量 #

将样式提取为可复用的常量:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const styles = {
  container: css`
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 20px;
  `,
  
  section: css`
    padding: 40px 0;
  `,
  
  title: css`
    font-size: 32px;
    font-weight: bold;
    color: #333;
    margin-bottom: 20px;
  `,
  
  text: css`
    font-size: 16px;
    line-height: 1.6;
    color: #666;
  `,
}

function App() {
  return (
    <div css={styles.container}>
      <section css={styles.section}>
        <h1 css={styles.title}>Welcome</h1>
        <p css={styles.text}>Hello World</p>
      </section>
    </div>
  )
}

提取为函数 #

创建动态样式函数:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const getFlexStyles = (direction = 'row', justify = 'center', align = 'center') => css`
  display: flex;
  flex-direction: ${direction};
  justify-content: ${justify};
  align-items: ${align};
`

const getSpacingStyles = (padding = 16, margin = 0) => css`
  padding: ${padding}px;
  margin: ${margin}px;
`

function FlexContainer({ direction, justify, align, padding, margin, children }) {
  return (
    <div css={[getFlexStyles(direction, justify, align), getSpacingStyles(padding, margin)]}>
      {children}
    </div>
  )
}

嵌套选择器 #

伪类选择器 #

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const linkStyle = css`
  color: #007bff;
  text-decoration: none;
  
  &:hover {
    text-decoration: underline;
  }
  
  &:visited {
    color: #6c757d;
  }
  
  &:active {
    color: #0056b3;
  }
`

伪元素选择器 #

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const clearfixStyle = css`
  &::after {
    content: '';
    display: table;
    clear: both;
  }
`

const customListStyle = css`
  list-style: none;
  
  & li::before {
    content: '✓';
    margin-right: 8px;
    color: #28a745;
  }
`

子元素选择器 #

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'

const cardStyle = css`
  padding: 20px;
  background: white;
  border-radius: 8px;
  
  & > h2 {
    margin-top: 0;
    color: #333;
  }
  
  & > p {
    color: #666;
    line-height: 1.6;
  }
  
  & > button {
    margin-top: 16px;
  }
`

性能优化 #

避免内联对象 #

❌ 不推荐:每次渲染创建新对象

jsx
function Component() {
  return (
    <div css={{ padding: 20, margin: 10 }}>
      Content
    </div>
  )
}

✅ 推荐:提取样式常量

jsx
const containerStyle = css`
  padding: 20px;
  margin: 10px;
`

function Component() {
  return <div css={containerStyle}>Content</div>
}

使用 useMemo #

缓存动态样式:

jsx
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import { useMemo } from 'react'

function Component({ size, color }) {
  const dynamicStyle = useMemo(() => css`
    font-size: ${size}px;
    color: ${color};
  `, [size, color])
  
  return <div css={dynamicStyle}>Content</div>
}

下一步 #

掌握了 css prop 的用法后,继续学习 Styled组件,了解 styled API 的更多高级特性。

最后更新:2026-03-28