構文チュートリアル
Master CSS のクラスを読み、組み合わせ、操作状態や条件を加え、利用可能な空間に応答するガイドカードを作ります。
このページの内容
ガイドカードには余白、読みやすい文字、操作に応答するリンクが必要です。Master CSS では HTML 内でこれらを決められます。スタイルを選び、適用先や適用時を決めるセレクターや条件を加えます。
まずインストールを済ませてください。この例は標準プリセットを使い、独自のユーティリティ定義はありません。連携ツールが走査するソースファイルにマークアップを置き、アプリのスタイルシートを読み込みます。
@import '@master/css';各生成 CSS の折りたたみには、添えたクラスのコンパイル結果が表示されます。開くとブラウザーに届く内容を確認できます。この出力をスタイルシートにコピーする必要はありません。
#クラスを読む
プリセットの sm クエリーにビューポートが一致するときだけ、ホバーで青くなるリンクから始めます。
<a href="/guide/reusing-styles" class="fg-blue-60:hover@sm"> Read the guide</a>このクラスは3つの部分からなります。fg-blue-60 がスタイル、:hover が状態を選び、@sm が条件を加えます。セレクターと条件の両方に一致したときだけ適用されます。接尾辞がなければ、fg-blue-60 はホバーやビューポート幅に関係なく適用されます。
スタイル、セレクター、条件を読む
ビューポートが sm に一致する間だけ、ホバーした要素に色が適用されます。どちらの接尾辞もこのクラスに属します。
テキストによる説明
- スタイル. fg-blue-60 は color を var(--color-blue-60) に設定します。
- セレクター. :hover はクラスを持つ要素のホバー時に一致します。
- 条件. @sm はルールをプリセットの名前付き sm メディアクエリーで囲みます。セレクターと条件が両方一致すると適用されます。
生成された CSS
@layer theme { :root, :host { --color-blue-60: oklch(51.83% .2687 266.1) }}@layer utilities { @media (width >= 52.125rem) { .fg-blue-60\:hover\@sm:hover { color: var(--color-blue-60) } }}生成ルールは CSS 変数で color を設定し、メディアクエリー内に置かれます。セレクターの記号は、HTML のクラス名に一致するようエスケープされます。HTML には読みやすい形を書き、エスケープした CSS セレクターは生成出力に任せます。
接尾辞は 1 つの完全なクラスに属する
括弧は正確な文字範囲を指します。空白で最初のクラスは終わり、その状態、条件、重要度は r-md に及びません。
テキストによる説明
- p(12) は内側の余白を設定します。:hover はクラスを持つ要素を検査し、@sm は名前付きメディア条件に制限し、末尾の ! は宣言を重要にします。
- 別の r-md クラスにはこれらの接尾辞はありません。ホバーや sm 条件がなくても適用でき、宣言は重要ではありません。
#値の指定方法を選ぶ
よくある判断にはプリセットの名前を使います。局所的に正確な値が必要なら、対応する関数に数値を渡すか、ネイティブ宣言を書きます。
| 書き方 | 判断 |
|---|---|
flex | display: flex の固定ユーティリティ |
p-md | 名前付き間隔のトークンクラス |
p(12) | 直接値:12ピクセルのパディング |
(max-width:32rem) | 完全な CSS プロパティと値を持つネイティブ宣言 |
text-sm | 文字サイズ、行高、字間をまとめるレシピ |
生成された CSS
@layer theme { :root, :host { --font-size-sm: .875rem; --text-sm: var(--font-size-sm); --text-sm--line-height: 1.5; --text-sm--letter-spacing: 0; --spacing-md: 1rem }}@layer utilities { .text-sm { font-size: var(--text-sm); line-height: var(--text-sm--line-height, normal); letter-spacing: var(--text-sm--letter-spacing, normal) } .flex { display: flex } .p-md { padding: var(--spacing-md) } .p\(12\) { padding: calc(12 * 1px) } .\(max-width\:32rem\) { max-width: 32rem }}items-center や text-center などの固定ユーティリティは一般的な宣言をカバーします。m(4)、b(1)、grow(1) などの直接値関数は数値を受け取り、長さはピクセル、grow(1) は単位なしの flex 係数です。ネイティブ宣言は括弧内に完全なプロパティ名を保ちます。text-sm や grid-cols(2) などのレシピは複数の宣言をまとめられます。どの形式にもセレクターと条件の接尾辞を付けられます。
#1つのクラス内で空白を表す
HTML は空白でクラスを区切ります。引用符付き文字列の外では、CSS 値に必要な空白を | で表し、カンマ、スラッシュ、括弧はそのまま残します。
<div class="(transform:translate(0,1rem)) (color:rgb(30|60|90/80%))"> <span class="(content:'New|collection')::before"></span></div>生成された CSS
@layer utilities { .\(color\:rgb\(30\|60\|90\/80\%\)\) { color: rgb(30 60 90/80%) } .\(transform\:translate\(0\,1rem\)\) { transform: translate(0, 1rem) } .\(content\:\'New\|collection\'\)\:\:before::before { content: 'New|collection' }}色は rgb(30 60 90 / 80%) になります。引用符内の content 文字列ではパイプはそのまま残り、文字は New|collection です。Master CSS は calc(100%-2rem) などの演算子の空白も正規化し、計算はブラウザーが行います。ネイティブ宣言の境界、引用値、エスケープは宣言と値を参照してください。
パイプは引用符内で意味が変わる
空白の復元は引用文字列の外で行います。出力の文字列は1つのリテラル値のままです。
テキストによる説明
2つのリテラル入力と出力を対応付けています。rgb(30|60|90) は rgb(30 60 90) になり、引用文字列 New|collection はパイプを保ちます。
#共有する値にトークンを使う
トークンクラスは接頭辞と名前付きの値を結び付けます。p-md と gap-md はどちらも間隔トークン --spacing-md を読み、別々のプロパティを設定します。
| クラス | 宣言 |
|---|---|
p-md | padding: var(--spacing-md) |
gap-md | gap: var(--spacing-md) |
fg-text-muted | color: var(--color-text-muted) |
生成された CSS
@layer theme { :root, :host { --color-zinc-40: oklch(68.6% .0028 265); --color-zinc-60: oklch(48.6% .003 265) } :root, :host { --color-text-muted: light-dark(var(--color-zinc-60), var(--color-zinc-40)) } :root, :host { --spacing-md: 1rem }}@layer utilities { .fg-text-muted { color: var(--color-text-muted) } .gap-md { gap: var(--spacing-md) } .p-md { padding: var(--spacing-md) }}以下の間隔値はプリセットから直接取得しています。
--spacing-sm:.75rem(:root,:host)--spacing-md:1rem(:root,:host)--spacing-lg:1.5rem(:root,:host)
p-md と (padding:1rem) は同じ見た目でも、判断が異なります。--spacing-md を変えるとトークンのパディングは変わりますが、リテラルは 1rem のままです。共有値に従わせたいスタイルにはトークンを使います。独自の値の定義とスコープはテーマトークン、接頭辞と名前空間はファミリー索引を参照してください。
#独立した判断を組み合わせる
判断ごとにクラスを分け、小さな面を組み立てます。
<article class="p-md r-lg b(1) b-solid b-line-divider bg-surface-raised"> <h2 class="m(0) text-lg fg-text-strong">Field notes</h2> <p class="mt-sm mb(0) text-sm fg-text-muted">Keep useful ideas close.</p></article>生成された CSS
@layer theme { :root, :host { --color-zinc-40: oklch(68.6% .0028 265); --color-zinc-60: oklch(48.6% .003 265); --color-zinc-90: oklch(24.6% .0022 265); --color-zinc-100: oklch(15% .0016 265) } :root, :host { --color-surface-raised: light-dark(var(--color-white), var(--color-zinc-90)); --color-line-divider: light-dark(oklch(0% 0 none / .12), oklch(100% 0 none / .12)); --color-text-strong: light-dark(var(--color-zinc-100), var(--color-white)); --color-text-muted: light-dark(var(--color-zinc-60), var(--color-zinc-40)) } :root, :host { --color-white: oklch(100% 0 none) } :root, :host { --font-size-sm: .875rem; --font-size-lg: 1.125rem; --text-sm: var(--font-size-sm); --text-sm--line-height: 1.5; --text-sm--letter-spacing: 0; --text-lg: var(--font-size-lg); --text-lg--line-height: 1.5; --text-lg--letter-spacing: 0; --radius-lg: .5rem; --spacing-sm: .75rem; --spacing-md: 1rem }}@layer utilities { .text-lg { font-size: var(--text-lg); line-height: var(--text-lg--line-height, normal); letter-spacing: var(--text-lg--letter-spacing, normal) } .text-sm { font-size: var(--text-sm); line-height: var(--text-sm--line-height, normal); letter-spacing: var(--text-sm--letter-spacing, normal) } .bg-surface-raised { background-color: var(--color-surface-raised) } .b-line-divider { border-color: var(--color-line-divider) } .r-lg { border-radius: var(--radius-lg) } .b-solid { border-style: solid } .b\(1\) { border-width: calc(1 * 1px) } .fg-text-muted { color: var(--color-text-muted) } .fg-text-strong { color: var(--color-text-strong) } .m\(0\) { margin: calc(0 * 1px) } .mt-sm { margin-top: var(--spacing-sm) } .mb\(0\) { margin-bottom: calc(0 * 1px) } .p-md { padding: var(--spacing-md) }}ここでは b(1) が境界線の幅、b-solid がスタイル、b-line-divider が色を設定します。1つを変えてもほかを書き直す必要はありません。パディング、角丸、組版設定も独立しているため、後の変更を小さくできます。広いレイアウトでパディングだけ変え、面と文字は保てます。
#クラスの順序は優先順位を決めない
p-md p(12) と書くと、このプロパティでは直接値がトークンに優先します。HTML のクラス順を逆にしても同じ CSS になります。
生成された CSS
@layer theme { :root, :host { --spacing-md: 1rem }}@layer utilities { .p-md { padding: var(--spacing-md) } .p\(12\) { padding: calc(12 * 1px) }}意図的な上書きでなければ、基本のパディングは1つにします。ネイティブのコンポーネント CSS とユーティリティを組み合わせる場合はレイヤー順も重要です。プリセットでは通常の utilities 宣言を components の後に置きます。競合はカスケードレイヤー、共通の外観を保ちながら1つのインスタンスを変える方法はスタイルの再利用を参照してください。
#状態とセレクターの関係を加える
動作を変えるクラスにセレクターを付けます。このボタンはホバーで面を変え、キーボードで移動してきたときに輪郭線を表示します。
面の変化とフォーカスの輪郭線
<button type="button" class="px-md py-xs text-sm r-md b(1) b-solid b-line-divider bg-surface-raised fg-text-strong shadow-sm bg-surface-inset:hover outline-offset(3) outline(2):focus-visible outline-solid:focus-visible">Save changes</button>生成された CSS
@layer theme { :root, :host { --color-zinc-5: oklch(96.5% .0011 265); --color-zinc-90: oklch(24.6% .0022 265); --color-zinc-100: oklch(15% .0016 265) } :root, :host { --color-surface-inset: light-dark(var(--color-zinc-5), var(--color-zinc-100)); --color-surface-raised: light-dark(var(--color-white), var(--color-zinc-90)); --color-line-divider: light-dark(oklch(0% 0 none / .12), oklch(100% 0 none / .12)); --color-text-strong: light-dark(var(--color-zinc-100), var(--color-white)) } :root, :host { --color-white: oklch(100% 0 none) } :root, :host { --font-size-sm: .875rem; --text-sm: var(--font-size-sm); --text-sm--line-height: 1.5; --text-sm--letter-spacing: 0; --radius-md: .375rem; --spacing-xs: .5rem; --spacing-md: 1rem } :root, :host { --shadow-sm: 0 0 0 1px light-dark(oklch(0% 0 none / .04), oklch(100% 0 none / .05)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24)) }}@layer utilities { .text-sm { font-size: var(--text-sm); line-height: var(--text-sm--line-height, normal); letter-spacing: var(--text-sm--letter-spacing, normal) } .bg-surface-raised { background-color: var(--color-surface-raised) } .b-line-divider { border-color: var(--color-line-divider) } .r-md { border-radius: var(--radius-md) } .b-solid { border-style: solid } .b\(1\) { border-width: calc(1 * 1px) } .shadow-sm { box-shadow: var(--shadow-sm) } .fg-text-strong { color: var(--color-text-strong) } .outline-offset\(3\) { outline-offset: calc(3 * 1px) } .py-xs { padding-block: var(--spacing-xs) } .px-md { padding-inline: var(--spacing-md) } .bg-surface-inset\:hover:hover { background-color: var(--color-surface-inset) } .outline-solid\:focus-visible:focus-visible { outline-style: solid } .outline\(2\)\:focus-visible:focus-visible { outline-width: calc(2 * 1px) }}ホバーの接尾辞は bg-surface-inset:hover だけに属し、隣のクラスを条件付きにはしません。:disabled、:nth-child(2)、::before などネイティブの疑似クラスと疑似要素も使えます。セレクター関数も使え、fg-blue-60:is(:hover,:focus-visible) はどちらかの状態で適用されます。
セレクターは別の要素も対象にできます。この例では article にクラスを付けていますが、article のホバー時に色が付くのは直下のリンクです。
<article class="fg-blue-60:hover>a"> <h3>Field notes</h3> <a href="/guide/reusing-styles">Read the guide</a></article>生成された CSS
@layer theme { :root, :host { --color-blue-60: oklch(51.83% .2687 266.1) }}@layer utilities { .fg-blue-60\:hover\>a:hover > a { color: var(--color-blue-60) }}直接の子には >、子孫には _ を使います。fg-blue-60_a は子孫のリンクに届きます。クラスを持つ要素が接尾辞の基準である点は変わりません。兄弟結合子、属性、セレクターリスト、詳細度はセレクターを参照してください。
#条件を加える
条件はクラスが適用されるタイミングを決めます。@sm を加えると、ビューポートがその名前付きクエリーに一致する間だけ変わります。このプレビューで冒頭の例の動作を確認できます。
ホバー色はビューポート幅で決まる
<div class="p-sm p-lg@sm"> <a href="/guide/responsive-design" target="_top" class="inline-flex py-xs text-sm fg-text-body underline fg-blue-60:hover@sm">Explore responsive design</a></div>生成された CSS
@layer theme { :root, :host { --color-zinc-30: oklch(78% .0025 265); --color-zinc-70: oklch(39.8% .0028 265); --color-blue-60: oklch(51.83% .2687 266.1) } :root, :host { --color-text-body: light-dark(var(--color-zinc-70), var(--color-zinc-30)) } :root, :host { --font-size-sm: .875rem; --text-sm: var(--font-size-sm); --text-sm--line-height: 1.5; --text-sm--letter-spacing: 0; --spacing-xs: .5rem; --spacing-sm: .75rem; --spacing-lg: 1.5rem }}@layer utilities { .text-sm { font-size: var(--text-sm); line-height: var(--text-sm--line-height, normal); letter-spacing: var(--text-sm--letter-spacing, normal) } .fg-text-body { color: var(--color-text-body) } .inline-flex { display: inline-flex } .p-sm { padding: var(--spacing-sm) } .py-xs { padding-block: var(--spacing-xs) } .underline { text-decoration-line: underline } @media (width >= 52.125rem) { .p-lg\@sm { padding: var(--spacing-lg) } } @media (width >= 52.125rem) { .fg-blue-60\:hover\@sm:hover { color: var(--color-blue-60) } }}@sm はプリセットのカスタムメディアクエリーを指します。1か所だけのビューポートしきい値には p-lg@media(width>=48rem) などのネイティブクエリーを使えます。ほかの条件も同じ形です。
| クラス | 条件 |
|---|---|
hidden@media(print) | 印刷時 |
grid@supports(display:grid) | 宣言に対応している |
grid@sm@supports(display:grid) | 名前付きビューポートクエリーと宣言への対応の両方 |
生成された CSS
@layer theme { :root, :host { --spacing-lg: 1.5rem }}@layer utilities { @media print { .hidden\@media\(print\) { display: none } } @supports (display:grid) { .grid\@supports\(display\:grid\) { display: grid } } @media (width >= 52.125rem) { @supports (display:grid) { .grid\@sm\@supports\(display\:grid\) { display: grid } } } @media (width >= 48rem) { .p-lg\@media\(width\>\=48rem\) { padding: var(--spacing-lg) } }}複数の条件は左から右に入れ子になるため、すべてのラッパーに一致する必要があります。カスタムメディアなどのラッパーは条件を参照してください。
両方の条件が一致する必要がある
grid@sm@supports(display:grid) は、片方の条件だけでは宣言を有効にしません。
テキストによる説明
2行2列の表で、ビューポートが sm に一致し、display:grid に対応する交差部分だけを強調しています。
#コンポーネントが使える空間に合わせる
同じビューポート幅でも、サイドバーのカードはメイン領域のカードより狭くなります。親に container-inline を付け、カードのレイアウトクラスにコンテナー条件を加えます。最後の例は (container-name:card) で親に名前を付け、@container(card|width>=28rem) で問い合わせます。
親がコンテナーを作り、子がレイアウトを変えます。28rem は明示的なクエリー値で、--container-md などのサイズトークンとは別です。レスポンシブデザインでは、名前付きと入れ子のコンテナーでこの違いを詳しく扱います。
#組み合わせて完成させる
このカードはここまでの判断を組み合わせています。最初は1列で、名前付きの親が 28rem に達するとリンクが文字の隣へ移り、パディングも増えます。リンクのホバーとフォーカスのスタイルは、レイアウトから独立したままです。
空間に収まるガイドカード
<section class="container-inline (container-name:card)"> <article class="grid gap-lg p-md r-lg b(1) b-solid b-line-divider bg-surface-raised shadow-sm (grid-template-columns:1fr|auto)@container(card|width>=28rem) p-lg@container(card|width>=28rem)"> <div> <h3 class="m(0) text-lg font-regular fg-text-strong">Reusable styles</h3> <p class="mt-sm mb(0) text-sm fg-text-muted (max-width:38ch)">Keep shared appearance together and local choices in markup.</p> </div> <a href="/guide/reusing-styles" target="_top" class="inline-flex items-center justify-center self-start self-center@container(card|width>=28rem) gap-xs px-md py-xs text-sm (text-decoration-line:none) r-md b(1) b-solid b-line-divider bg-surface-raised fg-text-strong shadow-sm bg-surface-inset:hover outline-offset(3) outline(2):focus-visible outline-solid:focus-visible">Read the guide <span aria-hidden="true">→</span> </a> </article></section>生成された CSS
@layer theme { :root, :host { --color-zinc-5: oklch(96.5% .0011 265); --color-zinc-40: oklch(68.6% .0028 265); --color-zinc-60: oklch(48.6% .003 265); --color-zinc-90: oklch(24.6% .0022 265); --color-zinc-100: oklch(15% .0016 265) } :root, :host { --color-surface-inset: light-dark(var(--color-zinc-5), var(--color-zinc-100)); --color-surface-raised: light-dark(var(--color-white), var(--color-zinc-90)); --color-line-divider: light-dark(oklch(0% 0 none / .12), oklch(100% 0 none / .12)); --color-text-strong: light-dark(var(--color-zinc-100), var(--color-white)); --color-text-muted: light-dark(var(--color-zinc-60), var(--color-zinc-40)) } :root, :host { --color-white: oklch(100% 0 none) } :root, :host { --font-weight-regular: 400; --font-size-sm: .875rem; --font-size-lg: 1.125rem; --text-sm: var(--font-size-sm); --text-sm--line-height: 1.5; --text-sm--letter-spacing: 0; --text-lg: var(--font-size-lg); --text-lg--line-height: 1.5; --text-lg--letter-spacing: 0; --radius-md: .375rem; --radius-lg: .5rem; --spacing-xs: .5rem; --spacing-sm: .75rem; --spacing-md: 1rem; --spacing-lg: 1.5rem } :root, :host { --shadow-sm: 0 0 0 1px light-dark(oklch(0% 0 none / .04), oklch(100% 0 none / .05)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24)) }}@layer utilities { .text-lg { font-size: var(--text-lg); line-height: var(--text-lg--line-height, normal); letter-spacing: var(--text-lg--letter-spacing, normal) } .text-sm { font-size: var(--text-sm); line-height: var(--text-sm--line-height, normal); letter-spacing: var(--text-sm--letter-spacing, normal) } .items-center { align-items: center } .self-start { align-self: start } .bg-surface-raised { background-color: var(--color-surface-raised) } .b-line-divider { border-color: var(--color-line-divider) } .r-lg { border-radius: var(--radius-lg) } .r-md { border-radius: var(--radius-md) } .b-solid { border-style: solid } .b\(1\) { border-width: calc(1 * 1px) } .shadow-sm { box-shadow: var(--shadow-sm) } .fg-text-muted { color: var(--color-text-muted) } .fg-text-strong { color: var(--color-text-strong) } .container-inline { container-type: inline-size } .grid { display: grid } .inline-flex { display: inline-flex } .font-regular { font-weight: var(--font-weight-regular) } .gap-lg { gap: var(--spacing-lg) } .gap-xs { gap: var(--spacing-xs) } .justify-center { justify-content: center } .m\(0\) { margin: calc(0 * 1px) } .mt-sm { margin-top: var(--spacing-sm) } .mb\(0\) { margin-bottom: calc(0 * 1px) } .outline-offset\(3\) { outline-offset: calc(3 * 1px) } .p-md { padding: var(--spacing-md) } .py-xs { padding-block: var(--spacing-xs) } .px-md { padding-inline: var(--spacing-md) } .\(container-name\:card\) { container-name: card } .\(max-width\:38ch\) { max-width: 38ch } .\(text-decoration-line\:none\) { text-decoration-line: none } .bg-surface-inset\:hover:hover { background-color: var(--color-surface-inset) } .outline-solid\:focus-visible:focus-visible { outline-style: solid } .outline\(2\)\:focus-visible:focus-visible { outline-width: calc(2 * 1px) } @container card (width >= 28rem) { .self-center\@container\(card\|width\>\=28rem\) { align-self: center } } @container card (width >= 28rem) { .p-lg\@container\(card\|width\>\=28rem\) { padding: var(--spacing-lg) } } @container card (width >= 28rem) { .\(grid-template-columns\:1fr\|auto\)\@container\(card\|width\>\=28rem\) { grid-template-columns: 1fr auto } }}バリエーションを選ぶときは、'p-md' と 'p-lg' のような完全なクラス名をソースに残します。'p-' + size のような断片からの組み立てでは、スキャナーは完全な名前を得られません。連続値にはネイティブのカスタムプロパティと (width:var(--progress)) のような固定クラスを使います。ソース探索はクラス名の走査、生成のタイミングはレンダリングモードを参照してください。
想定したスタイルが出ない場合は、まず生成 CSS を開いてクラスを確認します。次にセレクターの対象要素、条件の一致、解決後のカスタムプロパティ、ブラウザー内の競合ルールを確認します。HTML のクラスを並べ替えてもカスケードは直りません。
共有値の定義はテーマトークン、コンポーネントクラス、ユーティリティ、mixin の使い分けはスタイルの再利用へ進んでください。完全な契約は Reference にあります。







