全局样式 #
基本概念 #
虽然 Emotion 鼓励组件化的样式管理,但在某些场景下仍需要全局样式,如 CSS 重置、字体导入、基础样式等。Emotion 提供了 Global 组件来处理全局样式。
基本用法 #
使用 Global 组件 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const globalStyles = css`
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 16px;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
line-height: 1.5;
color: #333;
}
`
function App() {
return (
<>
<Global styles={globalStyles} />
<div>App Content</div>
</>
)
}
多个全局样式 #
可以添加多个 Global 组件:
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const resetStyles = css`
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
`
const baseStyles = css`
html {
font-size: 16px;
scroll-behavior: smooth;
}
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
`
const typographyStyles = css`
h1, h2, h3, h4, h5, h6 {
line-height: 1.2;
font-weight: 600;
}
p {
margin-bottom: 1rem;
}
a {
color: #007bff;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
`
function App() {
return (
<>
<Global styles={resetStyles} />
<Global styles={baseStyles} />
<Global styles={typographyStyles} />
<AppContent />
</>
)
}
CSS 重置 #
简单重置 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const resetStyles = css`
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 100%;
-webkit-text-size-adjust: 100%;
}
body {
min-height: 100vh;
line-height: 1.5;
text-rendering: optimizeSpeed;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
ul, ol {
list-style: none;
}
a {
text-decoration: none;
color: inherit;
}
button {
background: none;
border: none;
cursor: pointer;
}
`
现代 CSS 重置 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const modernReset = css`
*, *::before, *::after {
box-sizing: border-box;
}
* {
margin: 0;
}
html {
scroll-behavior: smooth;
}
body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
#root, #__next {
isolation: isolate;
}
`
字体导入 #
Google Fonts #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const fontStyles = css`
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code&display=swap');
body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
code, pre {
font-family: 'Fira Code', monospace;
}
`
本地字体 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const localFontStyles = css`
@font-face {
font-family: 'CustomFont';
src: url('/fonts/CustomFont-Regular.woff2') format('woff2'),
url('/fonts/CustomFont-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'CustomFont';
src: url('/fonts/CustomFont-Bold.woff2') format('woff2'),
url('/fonts/CustomFont-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
body {
font-family: 'CustomFont', sans-serif;
}
`
动态全局样式 #
基于主题的全局样式 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css, useTheme } from '@emotion/react'
function GlobalStyles() {
const theme = useTheme()
return (
<Global
styles={css`
body {
background-color: ${theme.colors.background};
color: ${theme.colors.text};
transition: background-color 0.3s ease, color 0.3s ease;
}
::selection {
background-color: ${theme.colors.primary};
color: white;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: ${theme.colors.surface};
}
::-webkit-scrollbar-thumb {
background: ${theme.colors.textMuted};
border-radius: 4px;
&:hover {
background: ${theme.colors.primary};
}
}
`}
/>
)
}
function App() {
return (
<ThemeProvider theme={theme}>
<GlobalStyles />
<AppContent />
</ThemeProvider>
)
}
条件全局样式 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
function App() {
const [isModalOpen, setIsModalOpen] = useState(false)
return (
<>
{isModalOpen && (
<Global
styles={css`
body {
overflow: hidden;
}
`}
/>
)}
<AppContent />
</>
)
}
工具类 #
布局工具类 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const utilityClasses = css`
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.justify-between {
justify-content: space-between;
}
.gap-1 { gap: 4px; }
.gap-2 { gap: 8px; }
.gap-3 { gap: 16px; }
.gap-4 { gap: 24px; }
.p-1 { padding: 4px; }
.p-2 { padding: 8px; }
.p-3 { padding: 16px; }
.p-4 { padding: 24px; }
.m-0 { margin: 0; }
.m-auto { margin: auto; }
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.w-full { width: 100%; }
.h-full { height: 100%; }
`
响应式工具类 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const responsiveUtilities = css`
.hidden { display: none; }
@media (min-width: 576px) {
.sm\\:block { display: block; }
.sm\\:flex { display: flex; }
.sm\\:hidden { display: none; }
}
@media (min-width: 768px) {
.md\\:block { display: block; }
.md\\:flex { display: flex; }
.md\\:hidden { display: none; }
}
@media (min-width: 992px) {
.lg\\:block { display: block; }
.lg\\:flex { display: flex; }
.lg\\:hidden { display: none; }
}
`
打印样式 #
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const printStyles = css`
@media print {
*, *::before, *::after {
background: transparent !important;
color: black !important;
box-shadow: none !important;
text-shadow: none !important;
}
a, a:visited {
text-decoration: underline;
}
a[href]::after {
content: " (" attr(href) ")";
}
abbr[title]::after {
content: " (" attr(title) ")";
}
img {
page-break-inside: avoid;
}
p, h2, h3 {
orphans: 3;
widows: 3;
}
h2, h3 {
page-break-after: avoid;
}
.no-print {
display: none !important;
}
}
`
组织全局样式 #
分离文件 #
jsx
import { Global, css } from '@emotion/react'
import resetStyles from './styles/reset'
import typographyStyles from './styles/typography'
import utilityStyles from './styles/utilities'
const globalStyles = css`
${resetStyles}
${typographyStyles}
${utilityStyles}
`
function GlobalStyles() {
return <Global styles={globalStyles} />
}
样式文件示例 #
styles/reset.js:
jsx
import { css } from '@emotion/react'
export default css`
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
}
body {
min-height: 100vh;
line-height: 1.5;
}
`
styles/typography.js:
jsx
import { css } from '@emotion/react'
export default css`
body {
font-family: system-ui, -apple-system, sans-serif;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
line-height: 1.2;
font-weight: 600;
}
h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.75rem; }
h4 { font-size: 1.5rem; }
h5 { font-size: 1.25rem; }
h6 { font-size: 1rem; }
`
最佳实践 #
1. 最小化全局样式 #
尽量减少全局样式,优先使用组件级样式:
jsx
const Container = styled.div`
max-width: 1200px;
margin: 0 auto;
padding: 0 16px;
`
2. 使用 CSS 变量 #
结合 CSS 变量实现动态主题:
jsx
/** @jsxImportSource @emotion/react */
import { Global, css } from '@emotion/react'
const cssVariables = css`
:root {
--color-primary: #007bff;
--color-secondary: #6c757d;
--spacing-unit: 8px;
--border-radius: 4px;
}
[data-theme="dark"] {
--color-primary: #4da3ff;
--color-secondary: #adb5bd;
}
`
3. 避免过度使用 #
全局样式应该只用于:
- CSS 重置
- 字体导入
- 基础排版
- 滚动条样式
- 打印样式
下一步 #
掌握了全局样式后,继续学习 动画与Keyframes,了解如何在 Emotion 中创建 CSS 动画。
最后更新:2026-03-28