2019 年 3 月 28 日 星期四
使用 Styled JSX 設計 Next.js 樣式
發布者Styled JSX 是一個 CSS-in-JS 函式庫,讓您能夠編寫封裝且作用域限定的 CSS 來設計元件樣式。您為一個元件引入的樣式不會影響其他元件,讓您能夠新增、變更和刪除樣式,而無需擔心意外的副作用。
開始使用
Next.js 預設包含 Styled JSX,因此開始使用非常簡單,只需將 <style jsx>
標籤新增到現有的 React 元素中,並在其中編寫 CSS 即可
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<p>Let's explore different ways to style Next.js apps</p>
<style jsx>{`
.container {
margin: 50px;
}
p {
color: blue;
}
`}</style>
</div>
);
}
export default Home;
在這個範例中,我們為元件的容器元素和段落加入了樣式。即使我們使用的是通用選擇器,這些樣式也不會影響其他元件中具有 container
類別名稱或 <p>
標籤的元素。這是因為 Styled JSX 確保樣式僅作用於此元件(透過將額外的唯一類別名稱套用至已設計樣式的元素)。
只需將一個 jsx
屬性新增到 <style>
元素,您就可以編寫標準 CSS,它會自動加上前綴並自動限定元件作用域。<style jsx>
元素應放置在元件的根元素內。
新增全域樣式
大多數專案都需要一些全域樣式來設計 body 元素的樣式或提供 CSS 重置。Styled JSX 允許我們使用 <style jsx global>
新增全域樣式。例如
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<p>Let's explore different ways to style Next.js apps</p>
<style jsx>{`
.container {
margin: 50px;
}
p {
color: blue;
}
`}</style>
<style jsx global>{`
p {
font-size: 20px;
}
`}</style>
</div>
);
}
export default Home;
這會將 20px 的字型大小套用至此特定頁面中的所有 <p>
標籤。
若要將全域樣式套用至應用程式中的所有頁面,一個好的方法是先建立一個包含全域樣式的版面配置元件,然後用它包裝所有頁面。
使用版面配置元件提供了彈性,可以將一組特定的樣式套用至某些頁面,同時仍允許其他頁面使用不同的樣式
function Layout(props) {
return (
<div className="page-layout">
{props.children}
<style jsx global>{`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
color: #333;
font-family: sans-serif;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`}</style>
</div>
);
}
export default Layout;
在 Next.js 中,我們可以透過在 pages/_app.js
內建立自訂的 App
元件,匯入 Layout
元件,然後如下所示將其新增至 render 方法,為所有頁面載入版面配置一次
import React from 'react';
import App, { Container } from 'next/app';
import Layout from '../components/Layout';
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<Container>
<Layout>
<Component {...pageProps} />
</Layout>
</Container>
);
}
}
export default MyApp;
在外部檔案中編寫樣式
我們也可以在元件外部的外部檔案中編寫樣式。
例如,我們可以將全域樣式從 Layout
元件移至個別檔案,如下所示
import css from 'styled-jsx/css';
export default css.global`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
color: #333;
font-family: sans-serif;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`;
然後我們可以將樣式匯入回 Layout
元件
import globalStyles from '../styles/global.js';
function Layout(props) {
return (
<div className="page-layout">
{props.children}
<style jsx global>
{globalStyles}
</style>
</div>
);
}
export default Layout;
一次性全域選擇器
我們使用 <style jsx>
新增至元件的樣式僅影響該元件內的元素,但不影響子元件。
有時,我們可能需要覆寫子元件的特定樣式。為此,Styled JSX 提供了 :global()
,讓您可以存取一次性全域選擇器。
例如,假設我們有一個 <Widget>
元件,其中包含一個類別名稱為 btn
的按鈕。如果我們只想在首頁匯入 widget 時變更此按鈕的顏色,我們可以這樣做
import Widget from '../components/Widget';
function Home() {
return (
<div className="container">
<h1>Hello Next.js</h1>
<Widget />
<style jsx>{`
.container {
margin: 50px;
}
.container :global(.btn) {
background: #000;
color: #fff;
}
`}</style>
</div>
);
}
export default Home;
動態樣式
與其他解決方案相比,能夠根據元件的 props 調整元件的樣式是 CSS-in-JS
函式庫的一大優勢。
使用 Styled JSX,我們可以這樣做
function Alert(props) {
return (
<div className="alert">
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
}
`}</style>
<style jsx>{`
.alert {
background: ${props.type == 'warning' ? '#fff3cd' : '#eee'};
}
`}</style>
</div>
);
}
export default Alert;
如果 Alert
元件傳遞了一個 type
prop,其值為 warning
,例如
<Alert type="warning">This is an important message</Alert>
元件將具有橘色的背景。若未指定 type prop,背景將回退為預設的灰色。
請注意,我們將動態樣式放在個別的 <style jsx>
標籤中。這不是必需的,但建議將靜態和動態樣式分開,以便僅在 props 變更時重新計算動態部分。
根據 props 調整樣式的另一種方法是根據 prop 值套用不同的類別名稱,如下所示
function Alert(props) {
return (
<div className={props.type == 'warning' ? 'alert warning' : 'alert'}>
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
background: #eee;
}
.alert.warning {
background: #fff3cd;
}
`}</style>
</div>
);
}
export default Alert;
建立網站主題
主題可以是一個簡單的物件,我們在其中包含應用程式中可能需要的最常見變數
const theme = {
fontFamily: {
sansSerif: '-apple-system, "Helvetica Neue", Arial, sans-serif',
mono: 'Menlo, Monaco, monospace',
},
colors: {
text: '#333',
background: '#fff',
link: '#1eaaf1',
linkHover: '#0d8ecf',
border: '#ddd',
warning: '#fff3cd',
success: '#d4edda',
},
};
export default theme;
然後我們在元件中匯入此主題檔案,並將硬式編碼的值替換為變數
import theme from '../styles/theme';
function Layout(props) {
return (
<div className="page-wrapper">
{props.children}
<style jsx global>{`
body {
background: ${theme.colors.background};
color: ${theme.colors.text};
font-family: ${theme.fontFamily.sansSerif};
}
`}</style>
<style jsx global>{`
body {
margin: 0;
padding: 0;
font-size: 18px;
font-weight: 400;
line-height: 1.8;
}
h1 {
font-weight: 700;
}
p {
margin-bottom: 10px;
}
`}</style>
</div>
);
}
export default Layout;
import theme from '../styles/theme';
function Alert(props) {
return (
<div className="alert">
{props.children}
<style jsx>{`
.alert {
display: inline-block;
padding: 20px;
border-radius: 8px;
}
`}</style>
<style jsx>{`
.alert {
background: ${props.type == 'warning'
? theme.colors.warning
: theme.colors.light};
}
`}</style>
</div>
);
}
export default Alert;
在這篇部落格文章中,我們介紹了如何開始使用 Styled JSX。若要深入了解其他功能,請務必在 GitHub 上查看。