목차
1. StyleX 세팅 및 기초
2. 설치
3. 오류
4. styleX의 핵심 기능
5. 핵심 조건
6. 기본적인 사용법
- 6.1 stylex.create
- 6.2 Pseudo-classes
- 6.3 media queries (반응형)
- 6.4 여러 스타일 지정하기
- 6.5 stylex.props
7. 예시 코드
StyleX 세팅 및 기초
공식문서 : https://stylexjs.com/
현재 프론트엔드 개발에서 React는 중요한 역할을 하고 있다. React를 개발한 Metark StyleX 라는 새로운 CSS-in-JS 라이브러리를 만들었는데, 이는 React와 잘 호환될 것으로 추측된다. Next.js 또한 React 기반 프레임워크이므로 Next.js와 React를 사용하는 프로젝트에서는 StyleX를 쓰게 될 것이라 생각하고 StyleX의 세팅 및 기초에 대해서 알아봤다.
웹 앱 스타일링을 위한 간단하고 사용하기 쉬운 JS 구문 및 컴파일러
인라인스타일과 정적 CSS의 장점을 결합한 방식이다.
스타일을 정의하고 사용하기 위해서는 컴포넌트 내의 로컬 지식만 있으면 되고, 미디어 쿼리와 같은 기능을 유지하면서 특정성 문제를 피할 수 있다.
StyleX는 충돌이 없는 Atomic CSS를 사용하여 최적화된 스타일을 빌드한다. (요즘 Atomic이 트랜드인건가..?)
확장성
- Atomic CSS로 CSS 파일 크기를 최소화한다.
- 컴포넌트 수가 증가하더라도 CSS 크기가 균등하게 유지된다.
- 커지는 코드베이스 내에서도 스타일은 읽기 쉽고 유지보수가 가능하다.
각 스타일 규칙을 가장 작은 단위로 분해하여 개별 클래스로 만드는 방식이다.
프로젝트의 컴포넌트 수가 증가하더라도 CSS 파일 크기가 균등하게 유지된다.
결과적으로 이를 통해서 중복을 줄이고 CSS 파일 크기를 최소화할 수 있다.
예측 가능성
- 요소에 적용된 클래스 이름은 해당 요소에만 직접 스타일을 적용할 수 있다.
- 특정성 문제가 없다.
- 마지막으로 적용된 스타일이 항상 승리한다!
클래스 이름은 각 요소에만 적용되기 때문에 다른 요소에는 영향을 주지 않으며 이로 인해 CSS의 특정성 문제가 발생하지 않는다.
또한, 마지막에 작성된 스타일이 항상 우선적으로 적용된다.
재조합 (요소를 분해 조합하여 재사용 가능하다)
- 조건부 스타일 적용
- 컴포넌트와 파일 경계를 넘어 임의의 스타일을 병합하고 조합한다.
- 컴포넌트 내의 로컬 상수와 표현식을 사용하여 스타일을 DRY하게 유지하거나 성능에 대해 걱정하지 않고 스타일을 반복한다.
조건부 스타일을 통해서 상황에 맞는 다양한 스타일링이 가능하고, 스타일을 자유롭게 병합하고 조합할 수 있다. 그리고 여러 컴포넌트에서 공통으로 사용되는 색상이나 글꼴과 같은 상수를 로컬 상수로 선언하여 재사용할 수 있다.
빠름
- 런타임에 스타일 주입이 없다.
- 모든 스타일은 컴파일 타임에 정적 CSS 파일에 번들링된다.
- 클래스 이름을 병합하는 런타임을 최적화한다.
런타임에서 스타일을 주입하는 것이 아닌 컴파일 타임에 정적 CSS 파일에 번들링 되기 때문에 빠르다. 또한, 클래스 이름을 병합하는 런타임을 최적화하여 빠르게 렌더링 할 수 있다.
타입 안정성
- 타입 안정성 API
- 타입 안정성 스타일
- 타입 안정성 테마
설치
$ npm install --save @stylexjs/stylex
$ npm install --save-dev @stylexjs/babel-plugin
# next.js
$ npm install --save-dev @stylexjs/nextjs-plugin
Next에서 사용하기 위해서 .babelrc.js 파일을 생성하고 아래와 같이 작성한다.
module.exports = {
presets: ["next/babel"],
plugins: [
[
"@stylexjs/babel-plugin",
{
dev: process.env.NODE_ENV === "development",
runtimeInjection: false,
genConditionalClasses: true,
treeshakeCompensation: true,
unstable_moduleResolution: {
type: "commonJS",
rootDir: __dirname,
},
},
],
],
}
그리고 next.config.js 파일에 아래와 같이 작성한다.
/** @type {import('next').NextConfig} */
const styleXPlugin = require("@stylexjs/nextjs-plugin")
const nextConfig = {}
module.exports = styleXPlugin({
rootDir: __dirname,
})(nextConfig)
dev-runtime 설치
컴파일러 및 빌드 프로세서를 이용하지 않고 styleX를 사용하기 위해선 dev-runtime을 설치해야 한다.
$ npm install --save-dev @stylexjs/dev-runtime
이렇게 설치를 하면 프로덕션에 배포할 준비가 될 때까지 추가 설정 없이 @stylexjs/stylex를 가져와서 사용할 수 있다.
이 부분은 아직 설정하는 방법을 못 찾았다...
아시는 분 공유 좀...
ESLint 플러그인
StyleX 컴파일러는 스타일의 유효성을 검사하지 않으며 많은 유효하지 않은 스타일을 컴파일할 수 있다. 스타일을 작성할 때 스타일의 유효성을 검사하기 위해서는 ESLint 플러그인을 설치해야 한다.
$ npm install --save-dev @stylexjs/eslint-plugin
그리고 .eslintrc.js 파일에 아래와 같이 작성한다.
module.exports = {
extends: "next/core-web-vitals",
plugins: ["@stylexjs"],
rules: {
// The Eslint rule still needs work, but you can
// enable it to test things out.
"@stylexjs/valid-styles": "error",
"ft-flow/space-after-type-colon": 0,
"ft-flow/no-types-missing-file-annotation": 0,
"ft-flow/generic-spacing": 0,
},
}
오류
Syntax error: "next/font" requires SWC although Babel is being used due to a custom babel config being present.
styleX에서 직접 언급한 내용을 따르면
This is an issue that we cannot fix on our end. StyleX is dependent on a Babel plugin. Using it within NextJS means using Webpack+Babel and not SWC.
next/font only works if you use SWC. Sorry, there's nothing we can do about this.
라고 했다.
마지막줄에 "SWC를 사용하는 경우에만 작동합니다. 죄송하지만 이 문제에 대해서는 저희가 할 수 있는 일이 없습니다."
라고 하는 것을 보면 StyleX에서 할 수 있는 건 없는 것 같고 이 오류가 발생하지 않게 하기 위해서
next/font를 사용하지 않는 방법으로 가야 할 것 같다.
기존
import type { Metadata } from "next"
import { Inter } from "next/font/google"
import "./globals.css"
const inter = Inter({ subsets: ["latin"] })
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
)
}
아래와 같이 수정한다.
수정
import type { Metadata } from "next"
import "./globals.css"
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body className={""}>{children}</body>
</html>
)
}
font를 사용하지 않고 className을 빈 문자열로 설정한다.
styleX의 핵심 기능
styleX의 핵심은 두 가지 기능으로 요약될 수 있다.
- stylex.create
- stylex.props
스타일을 생성하는 데 사용되는 create 함수와 스타일을 적용하는 데 사용되는 props 함수이다.
핵심 조건
StyleX는 사전 컴파일에 의존하므로 모든 스타일을 정적으로 분석할 수 있어야 한다.
즉, 모든 "원시 스타일 객체"는 오직 하나만 포함해야 한다.
가능한 것
- 객체 리터럴
- 문자열 리터럴
- 숫자 리터럴
- 배열 리터럴
- 'null' or 'undefined'
- 상수, 간단한 표현식, 내장 메서드 (예:.toString())
- 동적 스타일을 위한 화살표 함수
불가능한 것
- 함수호출 ( StyleX 함수 제외)
- 다른 모듈에서 가져온 값 (.stylex.js 파일에서 stylex를 사용하여 만든 CSS 변수 제외)
기본적인 사용법
stylex.create
import * as stylex from "@stylex/stylex"
const styles = stylex.create({
base: {
fontSize: 16,
lineHeight: 1.5,
color: "rgb(60, 60, 60)",
},
highlighted: {
color: "rebeccapurple",
},
})
Pseudo-classes
의사 클래스 (예 : :hover
)는 스타일 객체의 키로 사용할 수 있다.
const styles = stylex.create({
base: {
fontSize: 16,
lineHeight: 1.5,
color:{
default: "rgb(60, 60, 60)",
":hover": "rebeccapurple",
},
}
},
})
원하는 속성에 default를 이용해서 기본값을 지정하고 hover, active, focus 등의 의사 클래스를 키로 스타일을 지정할 수 있다.
media queries (반응형)
styleX를 이용한 반응형 스타일링은 아래와 같이 작성한다.
const styles = stylex.create({
wrapper: {
display: "flex",
width: {
default: "200px",
"@media (min-width: 600px)": "400px",
},
},
})
조금 더 편하게 쓰기 위해서 범위를 지정하고 사용할 수 있다.
const MEDIA_MOBILE = '@media (min-width: 600px)' as const;
const styles = stylex.create({
wrapper: {
display: "flex",
width: {
default: "200px",
[MEDIA_MOBILE]: "400px",
},
},
})
이를 이용해서 활용하는 방법은 아래와 같다.
const styles = stylex.create({
button: {
backgroundColor: {
default: "blue",
[MEDIA_MOBILE]: "red",
":hover": {
default: "skyblue",
[MEDIA_MOBILE]: "black",
},
},
},
})
여러 스타일 지정하기
const styles = stylex.create({
// ...지정 스타일
})
const text = stylex.create({
// ...지정 스타일
})
stylex.props
지정한 스탕을 적용하기 위해서는 stylex.props를 사용한다.
import * as stylex from "@stylex/stylex"
const styles = stylex.create({
base: {
fontSize: 16,
lineHeight: 1.5,
color: "rgb(60, 60, 60)",
},
highlighted: {
color: "rebeccapurple",
},
})
function MyComponent({ highlighted }) {
return (
<div {...stylex.props(styles.wrapper)}>
<div {...stylex.props(styles.container)}>
<p {...stylex.props(styles.button, text.base)}>Hello world</p>
</div>
</div>
)
}
예시 코드
create-next-app을 이용해서 예시 코드를 작성해 보았다.
// layout.tsx
import type { Metadata } from "next"
import "./globals.css"
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
// page.tsx
import * as stylex from "@stylexjs/stylex"
export default function Home() {
return (
<div {...stylex.props(styles.wrapper)}>
<div {...stylex.props(styles.container)}>
<p {...stylex.props(styles.button, text.base)}>Hello world</p>
</div>
</div>
)
}
const MEDIA_MOBILE = "@media (min-width: 600px)"
const styles = stylex.create({
wrapper: {
width: "100vw",
height: "100vh",
},
container: {
display: "flex",
width: "100%",
height: "100%",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgb(255, 171, 171)",
},
button: {
backgroundColor: {
default: "blue",
[MEDIA_MOBILE]: "red",
":hover": {
default: "skyblue",
[MEDIA_MOBILE]: "black",
},
},
width: {
default: "200px",
[MEDIA_MOBILE]: "400px",
},
height: "50px",
justifyContent: "center",
alignItems: "center",
display: "flex",
cursor: "pointer",
},
})
const text = stylex.create({
base: {
fontSize: 16,
lineHeight: 1.5,
cursor: "pointer",
color: {
default: "rgb(255, 255, 255)",
":hover": "rgb(0, 0, 0)",
},
},
highlighted: {
color: "rebeccapurple",
},
})
'시작 > TIL(Today I Learned)' 카테고리의 다른 글
Mockoon을 활용한 프론트엔드 개발 효율성 향상: 가상서버 및 Proxy 설정 (1) | 2024.03.03 |
---|---|
브라우저 탭간 통신하기 : BroadCast Channel API ( feat. Next.js) (0) | 2024.01.16 |
231110 - 카카오 로그인 (feat. KOE010) (0) | 2023.11.10 |
231109 - Monorepo를 이용한 Next.js 프로젝트 구성하기 (feat. pnpm) (1) | 2023.11.09 |
231010 - NextJS 클라이언트에서 S3 업로드 (1) | 2023.10.11 |
댓글