cs

styleX

ssoonn 2024. 4. 17. 22:34

Meta에서 출시한 CSS-in-JS 라이브러리.

컴파일러 기반의 라이브러리로, 컴파일 시 CSS 파일로 변환함 (전통적인 CSS에 가까운 성능 제공)

스타일 관리와 성능 최적화 면에서 전통적인 CSS의 강점을 유지하고, CSS-in-JS의 특징인 유연성과 확장성 제공

또 다른 특징인 Atomic CSS

  • CSS 출력 최소화. 컴포넌트 수가 많아지고 크기가 커지더라도 CSS 크기가 일정하게 유지됨

특징

  • 빠른 속도
    • 런타임 스타일 인젝션*이 없음
    • 모든 스타일은 컴파일 시 정적 css 파일에 번들로 제공됨

*런타임 스타일 인젝션: 웹 애플리케이션이 런타임 중에 스타일을 동적으로 추가하거나 변경하는 기술

  • 확장성
    • CSS 출력 최소화. 컴포넌트 수가 많아지고 크기가 커지더라도 CSS 크기가 일정하게 유지됨
  • 예측 가능성
    • element의 클래스 네임이 동일한 요소에만 직접 스타일 지정 가능 ???
    • ( Class names on an element can only directly style that same element.)
  • 합성 가능성
    • 조건부로 스타일 적용
    • 컴포넌트 파일, 경계에서 임의의 스타일을 병합하고 작성할 수 있음
    • 스타일 반복 가능
  • 타입 안정성
    • type-safe API, style, theme
  • colocation
    • 스타일을 로컬에서 작성, 적용, reasoning할 수 있도록 설계됨
  • 저비용 추상화
    1. 로컬에서 생성, 적용되는 스타일

동일한 파일 내에서 스타일을 작성하고 사용할 때 styleX의 비용은 0임

스타일X가 stylex.create 호출을 컴파일하는 것 외에도, stylex.props 콜도 컴파일함

import * as stylex from 'stylex';
const styles = stylex.create({
  red: {color: 'red'},
});
let a = stylex.props(styles.red);

// compiles down to:

//JS output
import * as stylex from 'stylex';

let a = {className: 'x1e2nbdu'};
// CSS output
.x1e2nbdu { color: red; }
  1. using styles across files

stylex.create 호출은 완전히 없어지지 않고 클래스 네임에 object mapping key를 남김. 그리고 stylex.props() 호출은 런타임에 실행됨

import * as stylex from '@stylexjs/stylex';

const styles = stylex.create({
  foo: {
    color: 'red',
  },
  bar: {
    backgroundColor: 'blue',
  },
});

function MyComponent({style}) {
  return <div {...stylex.props(styles.foo, styles.bar, style)} />;
}

// compiles down to
// js output
import * as stylex from '@stylexjs/stylex';

const styles = {
  foo: {
    color: 'x1e2nbdu',
    $$css: true,
  },
  bar: {
    backgroundColor: 'x1t391ir',
    $$css: true,
  },
};

function MyComponent({style}) {
  return <div {...stylex.props(styles.foo, styles.bar, style)} />;
}
//css output
.x1e2nbdu { color: red; }
.x1t391ir { background-color: blue; }

사용하기

핵심 기능은 두가지:

stylex.create: 스타일 생성

stylex.props: 스타일을 element에 적용

// 1. configure the compiler
import plugin from '@stylexjs/rollup-plugin';

const config = () => ({
  plugins: [
    plugin({ ...options })
  ]
})

export default config;
---

// 2. define styles
import * as stylex from '@stylexjs/stylex';

const styles = stylex.create({
  root: {
    width: '100%',
    maxWidth: 800,
    minHeight: 40,
  },
  child: {
    backgroundColor: 'black',
    marginBlock: '1rem',
  },
});

const colorStyles = stylex.create({
  red: { // namespace
    backgroundColor: 'red',
    borderColor: 'darkred',
  },
  green: { // namespace
    backgroundColor: 'lightgreen',
    borderColor: 'darkgreen',
  },
});
// 3. use styles
// props() function으로 스타일 전달
function ReactDiv({ color, isActive, style }) {
  return <div {...stylex.props(
    styles.main,
    // apply styles conditionally
    isActive && styles.active,
    // choose a style variant based on a prop
    colorStyles[color],
    // styles passed as props
    style,
  )} />;
}
  • type-safe 적용: 스타일 타입화를 통해 컴포넌트 스타일 커스터마이징에 대한 정교한 규칙 적용 가능
import type {StyleXStyles} from '@stylexjs/stylex';

type Props = {
  // 색상과 배경색만 허용하도록 정의 (다른 스타일은 허용하지 않음)
  style?: StyleXStyles<{color?: string; backgroundColor?: string}>;
  //...
};

...

import type {StyleXStylesWithout} from '@stylexjs/stylex';

type Props = {
  // 속성에 여러 스타일을 허용하나, 마진만 비허용
  style?: StyleXStylesWithout<{
    margin: unknown;
    marginBlock: unknown;
    marginInline: unknown;
    marginTop: unknown;
    marginBottom: unknown;
    marginLeft: unknown;
    marginRight: unknown;
    marginBlockStart: unknown;
    marginBlockEnd: unknown;
    marginInlineStart: unknown;
    marginInlineEnd: unknown;
  }>;
  //...
};
  • sharable constant
    • stylex.create는 생성된 클래스 이름들을 완전히 추상화한다. 모호한 자바스크립트 객체들을 strong type으로 처리하여 객체가 나타내는 스타일을 표현함
    • stylex.defineVars는 생성된 CSS 변수들의 이름들을 추상화한다. 상수로 가져와서 스타일 내에서 바로 사용할 수 있음
    • stylex.keyframes는 키프레임 애니메이션들의 이름들을 추상화한다. 대신 상수로 선언되며 참조로 사용됨
  • stylex.props
    • className과 스타일 속성을 가진 객체 반환
    • 다양한 프레임워크에서 작동하게 하려면 wrapper function이 필요할수도 있음
  • 제약
    • 컴파일 시점 이전의 컴파일에 의존하기에, 모든 스타일이 정적으로 분석할 수 있어야 한다
    • = 스타일 객체가 아래만을 포함해야 한다
      • 일반 객체 리터럴
      • 문자열 리터럴
      • 숫자 리터럴
      • 배열 리터럴
      • null 또는 undefined
      • 상수, 간단한 표현, 내장 메서드 (예: .toString())
      • 동적 스타일에 대한 화살표 함수
    • 비허용 항목
      • styleX 함수를 제외한 함수 호출
      • 다른 모듈에서 가져온 값 (.stylex.js 파일을 사용하여 stylex로 생성된 css 변수 제외)

Installation

런타임 패키지

npm install --save @stylexjs/stylex

컴파일러: 빌드 타임 컴파일러 사용

Development: 스타일이 컴파일 타임에 처리되도록 Babel plugin 구성

npm install --save-dev @stylexjs/babel-plugin
// babel.config.js
// Next.js는 기본적으로 babel 제공함. 
import styleXPlugin from '@stylexjs/babel-plugin';

const config = {
  plugins: [
    [
      styleXPlugin,
      {
        dev: true,
        // Set this to true for snapshot testing
        // default: false
        test: false,
        // Required for CSS variable support
        unstable_moduleResolution: {
          // type: 'commonJS' | 'haste'
          // default: 'commonJS'
          type: 'commonJS',
          // The absolute path to the root directory of your project
          rootDir: __dirname,
        },
      },
    ],
  ],
};

export default config;

styleX vs TailwindCSS

styleX Tailwind

자바스크립트 중심의 스타일링 방식 마크업에 직접 적용되는 유틸리티 클래스 활용
JavaScript, CSS-in-JS 개념에 대한 이해 필요 초보자도 쉽게 사용 가능
확장 가능한 솔루션이 필요한 대규모 애플리케이션에 이상적 사용자 정의 가능, 반응성이 뛰어남
성능과 유지보수가 필요한 대규모 애플리케이션에 이상적 빠른 ui 개발, 소규모 프로젝트에 적합
런타임 오버헤드가 없음 사용 편의성, 빠른 개발

난관

stylex.create should never be called. It should be compiled away
  • 컴파일러가 올바르게 구성되지 않아 발생한 오류: 컴파일 타임 접근 방식으로 작동하지 않는 동적인 패턴인 경우

stylex는 컴파일러이므로, stylex.create 호출은 컴파일된다. 즉, stylex.create 호출 내의 스타일은 정적으로 분석할 수 있어야 하며 다른 모듈에서 값을 임의로 가져와서 사용할 수 없다.

동적 스타일에는 stylex.create 내의 함수만 사용해야 한다. 스타일은 컴파일 시점에 알 수 없으므로 런타임에 생성해야 하는데, 이런 경우 값만 동적일 수 있다(키는 정적이어야 함). 또한 함수 구문은 { } 본문을 포함할 수 없고 객체를 직접 반환해야 함

  • import { stylex } from "@stylexjs/stylex” → import * as stylex from "@stylexjs/stylex”

 

'cs' 카테고리의 다른 글

TCP/IP 4계층  (1) 2024.06.30
Quiz: 디자인패턴과 라이브러리/프레임워크  (1) 2024.06.24
라이브러리 vs 프레임워크  (0) 2024.06.24
디자인 패턴  (0) 2024.06.24
앱 디렉터리와 앱 라우터  (0) 2024.04.17