vanilla extract - styling

이보경·2024년 4월 22일
1

Vanilla Extract란?

타입스크립트로 작성하는 제로 런타임 스타일시트 라이브러리

Zero-runtime Stylesheets in TS

  • 타입스크립트를 전처리기로 사용하여 모든 CSS 코드를 .css.ts 파일에 작성하고 빌드타임에 CSS로 변환
  • 변수 이름 오타 등의 정적 체크 및 타입 추론이 가능함
  • 기존의 CSS-in-JS 라이브러리인 styled-components나 emotion의 경우 런타임에 JS를 CSS로 변환

Type-safe static CSS

빌드 시 생성되는 모든 스타일 - Sass , LESS 등과 유사하지만 TypeScript의 강력한 기능을 사용합니다.

First-class theming

type-safe token contracts을 사용하여 단일 글로벌 테마를 생성하거나 여러 테마를 생성합니다.

Framework agnostic

webpack, esbuild, Vite 및 Next.js에 대한 공식 통합이 제공됩니다.

Built for extension

Sprinkles, Recipes, Dessert Box와 같은 라이브러리를 사용하거나 직접 만들어보세요!

API

style

styleVariants

createVar

createTheme

createThemeContract

assignVars

fontFace

keyframes

createContainer

layer

addFunctionSerializer

Global API

globalStyle

createGlobalTheme

createGlobalThemeContract

globalFontFace

globalKeyframes

globalLayer

Packages

Sprinkles

Recipes

Dynamic

CSS Utils

Styling

CSS Properties

kabab-case가 아니라 camelCase를 사용한다.

Unitless Properties

원래 단위가 없는 속성(flexGrow, opacity 등)말고는 px로 변환한다.

Vendor Prefixes

-를 지운 PascalCase를 사용한다.

-webkit-tap-highlight-colorWebkitTapHighlightColor

CSS Variables

vanilla extract에서 선언되는 css variables는 vars 키워드 내부에 선언되어야 합니다.

createVar api를 통해 임의의 변수명을 생성해 참조할 수도 있습니다.

// styles.css.ts
import { style, createVar } from '@vanilla-extract/css';

const myVar = createVar();

const myStyle = style({
  vars: {
    '--my-global-variable': 'purple',
    [myVar]: 'pink'
  }
});

동일한 변수명을 참조하더라도 값은 다르게 선언될 수 있다. 아래 예시에서는 동일한 accentVar를 참조하나 클래스 명에 따라 다르게 동작하는 color 값을 볼 수 있다.

export const accentVar = createVar();

export const blue = style({
	vars: {
    	[accentVar]: 'blue'
    },
  	"@media": {
		'(prefers-color-scheme: dark)': {
			vars: {
            	[accentVar]: 'lightblue'
            }
      }
		}
	}
})

export const pink = style({
	vars: {
    	[accentVar]: 'pink'
    },
  	"@media": {
		'(prefers-color-scheme: dark)': {
			vars: {
            	[accentVar]: 'lightpink'
            }
		}
	}
})
/* 전처리된 css variable. 각 클래스에서 --accentVar__l3kgsb2라는 동일한 변수 명을 참조하고 있으나 다른 값이 전용됨을 볼 수 있다. */

Media Queries

@media 키 값에 미디어 쿼리를 한 곳에 모아 작성할 수 있다.

파일의 제일 마지막에 추가되기 때문에 다른 스타일보다 우선선위가 높게 적용된다.

  • 예제 코드
    // styles.css.ts
    import { style } from '@vanilla-extract/css';
    
    const myStyle = style({
      '@media': {
        'screen and (min-width: 768px)': {
          padding: 10
        },
        '(prefers-reduced-motion)': {
          transitionProperty: 'color'
        }
      }
    });

Selectors

두 가지 방법 (globalStyle에서는 사용할 수 없다.)

  1. hover, first-of-type, before과 같은 simple pseudo selector는 style 함수 내부에서 1-depth level로 선언할 수 있습니다.

    • CSS Properties, CSS Variables만 포함할 수 있다.
    const myStyle = style({
      ':hover': {
        color: 'pink'
      },
  1. 더 복잡한 규칙을 구성할 수 있는 Seletors 옵션

    • 현재 요소에 대한 참조인 &문자를 대상으로 해야 한다.
    • 다른 범위의 클래스 이름을 참조할 수도 있습니다.
    export const parent = style({});
    
    const link = style({
      selectors: {
        '&:hover:not(:active)': {
          border: '2px solid aquamarine'
        },
        'nav li > &': {
          textDecoration: 'underline'
        }
        [`${parent}:focus &`]: {
          background: '#fafafa'
        }
      }
    });
    • 현재 클래스가 아닌 요소를 대상으로 삼으려고 시도하는 선택자는 유효하지 않다.
      대신 해당 클래스의 스타일 블록 내에 정의해야 한다.
      ```tsx
      import { style } from '@vanilla-extract/css';
      
      // Invalid example:
      export const child = style({});
      export const parent = style({
        selectors: {
          // ❌ ERROR: Targetting `child` from `parent`
          [`& ${child}`]: {...}
        }
      });
      
      // Valid example:
      export const parent = style({});
      export const child = style({
        selectors: {
          [`${parent} &`]: {...}
        }
      });
      ```
    • 현재 요소 내의 하위 노드(예: '& a[href]'를 전역적으로 대상으로 지정해야 하는 경우 globalStyle을 사용해야 한다.
      /* styles.css.ts */
      import { style, globalStyle } from '@vanilla-extract/css';
      
      export const parent = style({});
      
      globalStyle(`${parent} a[href]`, {
        color: 'pink'
      });
      
      /* after transpile : CSS*/
      .styles_parent__1hiof570 a[href] {
        color: pink;
      }
  2. Circular Selectors

    • 선택기가 서로 종속되어 있는 경우 getter를 사용하여 정의할 수 있다.
      /* style.css.ts */
      import { style } from '@vanilla-extract/css';
      
      export const child = style({
        background: 'blue',
        get selectors() {
          return {
            [`${parent} &`]: {
              color: 'red'
            }
          };
        }
      });
      
      export const parent = style({
        background: 'yellow',
        selectors: {
          [`&:has(${child})`]: {
            padding: 10
          }
        }
      });
      
      /* CSS */
      .styles_child__1hiof570 {
        background: blue;
      }
      .styles_parent__1hiof571 .styles_child__1hiof570 {
        color: red;
      }
      .styles_parent__1hiof571 {
        background: yellow;
      }
      .styles_parent__1hiof571:has(.styles_child__1hiof570) {
        padding: 10px;
      }

Container Queries

@Container

Layers

@layer (브라우저 호환 주의)

다른 레이어와의 이름 충돌을 방지하기 위해 단일 범위의 레이어를 만들 수 있다.

// layer.css.ts
import { layer } from '@vanilla-extract/css';

export const reset = layer('reset');
export const framework = layer('framework');
export const app = layer('app');

Supports Queries

@supports

Fallback Styles

일부 브라우저에 존재하지 않는 CSS 속성 값을 사용할 때, 배열을 사용하여 대체 값을 정의합니다.

import { style } from '@vanilla-extract/css';

export const myStyle = style({
  // In Firefox and IE the "overflow: overlay" will be
  // ignored and the "overflow: auto" will be applied
  overflow: ['auto', 'overlay']
});

0개의 댓글