vanilla-extract の Sprinkles について

# はじめに

[vanilla-extract](https://vanilla-extract.style) の [Sprinkles](https://vanilla-extract.style/documentation/packages/sprinkles/) というパッケージについて紹介します。
まず vanilla-extract を軽く説明すると TypeScript でスタイルを記述することができ、ビルド時に静的な CSS ファイルを出力します。公式では「 Zero-runtime Stylesheets-in-TypeScript. 」と表現されております。

# Sprinkles

Sprinkles は vanilla-extract のためのゼロランタイムアトミックCSSフレームワークです。カスタムユーティリティクラスの静的なセットを生成し、ビルド時は静的に、実行時は動的に構成します。つまり Tailwind CSS などのゼロランタイムでタイプセーフなバージョンを自分で作るようなものになります。
この説明を読んでも理解し難いので、公式のコードを参考にして理解していきます。

## defineProperties

まず以下のコードのように [defineProperties](https://vanilla-extract.style/documentation/packages/sprinkles/#defineproperties) と [createSprinkles](https://vanilla-extract.style/documentation/packages/sprinkles/#createsprinkles) をインポートします。
※ createSprinkles は[後ほど](https://qiita.com/takashimelon/items/53cfa674d8539eddb69f#createsprinkles)説明します。

```ts
import {
  defineProperties,
  createSprinkles
} from '@vanilla-extract/sprinkles';
```

defineProperties は [properties](https://vanilla-extract.style/documentation/packages/sprinkles/#properties) / [conditions](https://vanilla-extract.style/documentation/packages/sprinkles/#conditions) / [shorthands](https://vanilla-extract.style/documentation/packages/sprinkles/#shorthands) を持つユーティリティクラスのコレクションを定義します。これらに関しても見ていきます。

### properties

[properties](https://vanilla-extract.style/documentation/packages/sprinkles/#properties) は CSS プロパティとそのプロパティの利用可能な値を定義します。

```ts
const responsiveProperties = defineProperties({
  properties: {
    display: ['none', 'block', 'flex'],
    flexDirection: ['row', 'column'],
    alignItems: [
      'stretch',
      'flex-start',
      'center',
      'flex-end'
    ],
    justifyContent: [
      'stretch',
      'flex-start',
      'center',
      'flex-end'
    ],
    gap: {
      none: 0,
      small: 4,
      medium: 8,
      large: 16
    }
  }
});
```

`gap` のように任意の名前を持つ値を設定することもできます。
また vanilla-extract の [Theming](https://vanilla-extract.style/documentation/theming#theming) で作成した値を用いることもできます。 

```ts
import { vars } from './vars.css.ts';

const responsiveProperties = defineProperties({
  properties: {
    gap: vars.space
  }
});
```

### conditions

[conditions](https://vanilla-extract.style/documentation/packages/sprinkles/#conditions) は properties で定義したプロパティに対する media / feature / container を定義します。
以下のコードはメディアクエリの例になります。

```ts
const responsiveProperties = defineProperties({
  conditions: {
    mobile: {},
    tablet: { '@media': 'screen and (min-width: 768px)' },
    desktop: { '@media': 'screen and (min-width: 1024px)' }
  },
  defaultCondition: 'mobile'
});
```

またセレクタに対して定義することも可能です。

```ts
const properties = defineProperties({
  conditions: {
    default: {},
    hover: { selector: '&:hover' },
    focus: { selector: '&:focus' }
  },
  defaultCondition: 'default'
});
```

### shorthands

[shorthands](https://vanilla-extract.style/documentation/packages/sprinkles/#shorthands) は properties の元となる CSS プロパティにマップします。以下のコードでは `padding` / `paddingX` / `paddingY` を定義します。

```ts
import { vars } from './vars.css.ts';

const responsiveProperties = defineProperties({
  properties: {
    paddingTop: vars.space,
    paddingBottom: vars.space,
    paddingLeft: vars.space,
    paddingRight: vars.space
  },
  shorthands: {
    padding: [
      'paddingTop',
      'paddingBottom',
      'paddingLeft',
      'paddingRight'
    ],
    paddingX: ['paddingLeft', 'paddingRight'],
    paddingY: ['paddingTop', 'paddingBottom']
  }
});
```

## createSprinkles

[createSprinkles](https://vanilla-extract.style/documentation/packages/sprinkles/#createsprinkles) は defineProperties で定義したプロパティにアクセスするためのタイプセーフな関数を作成します。

```ts
const responsiveProperties = defineProperties({
  /* ... */
});
const unconditionalProperties = defineProperties({
  /* ... */
});
const colorProperties = defineProperties({
  /* ... */
});

export const sprinkles = createSprinkles(
  responsiveProperties,
  unconditionalProperties,
  colorProperties
);
```

## 使用

createSprinkles で作成した関数を使用してスタイルを作成します。

```ts
import { sprinkles } from './sprinkles.css.ts';

export const container = sprinkles({
  display: 'flex',
  paddingX: 'small',
  flexDirection: {
    mobile: 'column',
    desktop: 'row'
  },
  background: {
    lightMode: 'blue-50',
    darkMode: 'gray-700'
  }
});
```

実際に defineProperties で定義したプロパティ名やメディアクエリなどが使用されていることが分かります。また、これらの `paddingX` や `small` 、 `mobile` などはエディタ（ VSCode ）で補完されるため、かなり便利です。

vanilla-extract の公式サイトでも Sprinkles は使用されていますので、拝見してみると理解が深まると思います。

https://github.com/vanilla-extract-css/vanilla-extract/blob/master/site/src/system/styles/sprinkles.css.ts

# おわりに

[Why We're Breaking Up with CSS-in-JS](https://dev.to/srmagura/why-were-breaking-up-wiht-css-in-js-4g9b) の記事をお読みになられた方は CSS-in-JS の今後について一度は考えたのではないでしょうか。
この記事をきっかけに、 CSS-in-JS の次の候補として vanilla-extract を考えてみてはいかがでしょうか。
あなたのきっかけになれれば幸いです。