コンテンツにスキップ

HTML CLI

AI エージェントや自動化スクリプト向けに設計された CLI ツールです。
SpiraCSS の HTML 解析と SCSS 生成のコアロジックを提供します。

  • 主な用途: AI エージェント(Codex、Claude Code など)が HTML から SCSS を生成する際に呼び出す
  • 副次的な用途: VS Code 拡張がエンジンとして内部で使用

人間がターミナルから直接使うことは想定していません。
手動で SCSS を生成する場合は VS Code 拡張を使用してください。

インストール

Node.js >= 20 が必要です。

Terminal window
yarn add -D @spiracss/html-cli
Terminal window
npm install -D @spiracss/html-cli

コマンド一覧

コマンド説明
spiracss-html-to-scssHTML から SCSS を自動生成
spiracss-html-lintHTML 構造が SpiraCSS ルールに従っているか検証
spiracss-html-formatプレースホルダクラス(既定 block-box / element)を付与

spiracss-html-to-scss

HTML / テンプレートから SCSS ファイルを自動生成します。デフォルトで構造 lint を実行し、エラーがある場合は終了します(--ignore-structure-errors で無視)。

基本的な使い方

Terminal window
# ルートモード(コンポーネントのルートを 1 つの Block として扱う)
yarn spiracss-html-to-scss --root path/to/file.html
# 選択モード(選択断片として扱う)
yarn spiracss-html-to-scss --selection path/to/fragment.html
# 標準入力から
cat file.html | yarn spiracss-html-to-scss --selection --stdin --base-dir src/components/sample

オプション

オプション説明
--rootコンポーネントのルートを 1 つの Block として扱う(デフォルト)
--selection選択断片として扱う
--stdin標準入力から HTML を読み取る
--base-dir PATHSCSS 出力先ディレクトリ(入力ファイル指定時は上書き先、--stdin 使用時は省略でカレント)
--dry-runファイルを書き込まず、出力パスだけを表示
--json生成結果を JSON で標準出力(ファイル作成なし、AI/スクリプト向け)
--ignore-structure-errors構造 lint のエラーを無視して生成を続行

補足:

  • モードは --selection の有無で判定されるため、--root は互換維持のためのオプションです(両方指定時は --selection が優先)
  • エラーメッセージの文言は変更される可能性があるため、文字列一致に依存しないでください

注意:

  • --root モードは入力から 1 要素をルートとして解釈し、その要素を起点に配下を含めて生成します。該当要素に class が無い場合は生成できずエラーになります。コンポーネント単位の入力に合わせて使ってください
  • --selection モードで class 属性を持つ要素が 1 つも無い場合はエラーになります
  • --ignore-structure-errors を付けても、ルート要素が存在しない / 複数ある / class が無いなど、生成に必要な前提を満たさない場合はエラー終了します

出力例

your-component/
├─ hero-section.scss ← ルート Block
├─ scss/
│ ├─ feature-card.scss ← 子 Block
│ └─ index.scss ← @use 自動マージ
└─ your-template.html

出力先のルール

  • 入力ファイル指定: 入力ファイルと同じディレクトリ(--base-dir 指定時は上書き)
  • --stdin: --base-dir(省略時はカレントディレクトリ)
  • 子 Block は childScssDir(既定 scss

生成仕様の補足

  • 同一 base class が複数ある場合、modifier / variant / state / aria は重複排除して統合されます
  • 予約キーは selectorPolicy で変更できます(例: data-theme, data-status, aria-hidden, aria-expanded
  • data モードでは variant は基本構造に出力されます(インタラクション初期値は手動で interaction に移してよい)

spiracss-html-lint

HTML 構造が SpiraCSS の Block / Element / Modifier ルールに従っているかを検証します。

基本的な使い方

Terminal window
# コンポーネントのルート要素として検証
yarn spiracss-html-lint --root path/to/file.html
# 標準入力から断片を検証
cat fragment.html | yarn spiracss-html-lint --selection --stdin
# JSON 形式で結果を取得
yarn spiracss-html-lint --root path/to/file.html --json

オプション

オプション説明
--rootコンポーネントのルートを 1 つの Block として扱う(デフォルト)
--selection複数の選択断片として扱う
--stdin標準入力から HTML を読み取る
--json{ file, mode, ok, errors[] } 形式で JSON 出力

注意:

  • --root モードは入力から 1 要素をルートとして解釈し、その要素を起点に配下も検証対象とします
  • 該当要素に class が無い場合は INVALID_BASE_CLASS になります
  • コンポーネント単位の入力に合わせて使ってください。複数のルート Block を検証する場合は --selection モードを使用してください

補足:

  • モードは --selection の有無で判定されるため、--root は互換維持のためのオプションです(両方指定時は --selection が優先)

エラーコード

コード説明
INVALID_BASE_CLASSBlock/Element の命名規則に違反、またはルート要素に class が無い/検証対象要素が見つからない
MODIFIER_WITHOUT_BASEModifier クラスのみで基底クラスがない
DISALLOWED_MODIFIERdata モード(variant/state 両方 data)で Modifier クラスが使われている
UTILITY_WITHOUT_BASEユーティリティクラスのみで基底クラスがない
MULTIPLE_BASE_CLASSES複数の基底クラスが存在
ROOT_NOT_BLOCKルート要素が Block ではない
ELEMENT_WITHOUT_BLOCK_ANCESTORElement が Block の子孫ではない
ELEMENT_PARENT_OF_BLOCKElement が Block の親になっている
DISALLOWED_VARIANT_ATTRIBUTEclass モードで data-variant などのバリアント属性が使われている
DISALLOWED_STATE_ATTRIBUTEclass モードで data-state/aria-* などの状態属性が使われている
INVALID_VARIANT_VALUEdata モードのバリアント値が valueNaming に違反
INVALID_STATE_VALUEdata モードの状態値が valueNaming に違反
UNBALANCED_HTMLHTML の開始・終了タグが不整合
MULTIPLE_ROOT_ELEMENTSルートモードで複数のルート要素が検出された

補足:

  • --selection モードでは class を持つ要素のみ検証(該当要素が無い場合は INVALID_BASE_CLASS
  • data-* / aria-* は予約キー(variant.dataKeys / state.dataKey / state.ariaKeys)のみ対象
  • data 値の命名は selectorPolicy.valueNamingvariant/state.valueNaming で検証
  • stylelint.base.external.classes / stylelint.base.external.prefixes に一致するクラスは外部扱い(base 判定から除外)
  • 先頭トークンが外部クラスで、外部以外のクラスが後続にある場合は INVALID_BASE_CLASS(Block/Element を先頭に置く必要あり)。外部クラスのみの要素は許容
  • variant.mode=class + state.mode=class は state 分離不可のため 全 modifier を variant 扱い
  • エラーメッセージの文言は変更される可能性があるため、文字列一致に依存しないでください

spiracss-html-format

HTML にプレースホルダクラスを付与して SpiraCSS 構造を整形します。

基本的な使い方

Terminal window
# ファイルから読み取り、別ファイルに出力
yarn spiracss-html-format path/to/file.html -o formatted.html
# ファイルから読み取り、標準出力
yarn spiracss-html-format path/to/file.html
# 標準入力から読み取り、ファイルに出力
cat file.html | yarn spiracss-html-format --stdin -o formatted.html

オプション

オプション説明
--stdin標準入力から HTML を読み取る
-o, --output PATH出力先ファイルパス(省略時は標準出力)

出力属性は spiracss.config.jshtmlFormat.classAttribute に従います(既定は class)。
className を使う場合は className を指定してください。
内部では class / className を一時的に data-spiracss-classname に変換して処理し、出力時に設定値へ戻します。

付与されるクラス

すべての子孫要素を再帰的に走査し、SpiraCSS の Block > Element 構造に整形します。

  • 子要素を持つ要素 → Block プレースホルダを先頭に付与
  • 子要素を持たない要素(葉ノード) → Element プレースホルダを先頭に付与
  • 既に Block 名がある要素 → そのまま維持し、内部を再帰処理
  • Element 名があるが子を持つ要素 → Block 形式に変換(例: titletitle-box

※ ルート要素が Element の場合は Element → Block 変換を行わず、block-box title のように Block プレースホルダを先頭に付与します(Block/Element 以外でも同様)。

プレースホルダ名は blockCase / elementCase 設定に応じて変わります(両者は独立して設定可能):

caseBlock プレースホルダElement プレースホルダ
kebab(既定)block-boxelement
camelblockBoxelement
pascalBlockBoxElement
snakeblock_boxelement

注意:

  • Element は 常に 1 語です。elementCase=camel は小文字 1 語(例: element)、elementCase=pascal は大文字始まり 1 語(例: Element)のみ許可され、bodyText / BodyText のような内部大文字は使えません
  • customPatterns を指定している場合、プレースホルダ(block-box / element)が命名規則に合わない可能性があるため整合性を確認してください

制限事項

テンプレート構文(EJS <% %>, Nunjucks {{ }} {% %} {# #}, Astro frontmatter 等)を含む HTML は処理をスキップします。
これらの構文は HTML パース時に破壊される恐れがあるため、静的な HTML 断片に対してのみ使用してください。

プレースホルダ付与では JSX の class / className は静的に判定できる場合のみ対応します(文字列リテラル、${} 内が静的 member access または文字列リテラルのテンプレートリテラル、styles.foo / styles['foo'] / styles["foo"] のような member access)。jsxClassBindings.memberAccessAllowlist を設定している場合は、許可されたベース識別子のみクラスとして扱い、styles.layout.hero のようなチェーンは動的扱いにします。props.className や条件式、関数呼び出し、非静的な ${} を含む場合は HTML 全体の処理をスキップします。JSX/TSX をフォーマットする場合はバインディングをクラス文字列に正規化してプレースホルダを付与するため、CSS Modules のソースは上書きしない前提で使ってください。

テンプレート構文が検出された場合:

  • 標準出力モード-o 未指定): 元の HTML をそのまま出力し、標準エラーに警告を表示
  • ファイル出力モード-o 指定): ファイルへの書き込みをスキップし、標準エラーに警告のみ表示(mtime 更新を防止)
  • 断片 HTML で <template> / <textarea> の閉じタグが欠けている場合は挙動保証なし(明示検知は行いません)

変換仕様

変換例

<!-- 選択した HTML -->
<section class="hero-section">
<h1 class="title">Welcome</h1>
<p class="body">Introduction text...</p>
<div class="feature-card">
<h2 class="heading">Feature</h2>
</div>
<div class="cta-box">
<a class="link" href="#">Learn more</a>
</div>
</section>

↓ SpiraCSS 形式の SCSS を自動生成

hero-section.scss(ルート Block):

@use "@styles/partials/global" as *;
@use "sass:meta";
// @assets/css/index.scss
.hero-section {
@include meta.load-css("scss");
@include breakpoint-up(md) {
// layout mixin
}
> .title {
@include breakpoint-up(md) {
// layout mixin
}
}
> .body {
@include breakpoint-up(md) {
// layout mixin
}
}
> .feature-card {
// @rel/scss/feature-card.scss
@include breakpoint-up(md) {
// child component layout
}
}
> .cta-box {
// @rel/scss/cta-box.scss
@include breakpoint-up(md) {
// child component layout
}
}
// --shared ----------------------------------------
// --interaction -----------------------------------
// @at-root & {
// }
}

scss/feature-card.scss(子 Block):

@use "@styles/partials/global" as *;
// @rel/../hero-section.scss
.feature-card {
@include breakpoint-up(md) {
// layout mixin
}
> .heading {
@include breakpoint-up(md) {
// layout mixin
}
}
// --shared ----------------------------------------
// --interaction -----------------------------------
// @at-root & {
// }
}

scss/cta-box.scss(子 Block):

@use "@styles/partials/global" as *;
// @rel/../hero-section.scss
.cta-box {
@include breakpoint-up(md) {
// layout mixin
}
> .link {
@include breakpoint-up(md) {
// layout mixin
}
}
// --shared ----------------------------------------
// --interaction -----------------------------------
// @at-root & {
// }
}

scss/index.scss(子 Block 集約):

@use "cta-box";
@use "feature-card";
your-component/
├─ hero-section.scss ← ルート Block
├─ scss/
│ ├─ cta-box.scss ← 子 Block
│ ├─ feature-card.scss ← 子 Block
│ └─ index.scss ← @use 自動マージ
└─ your-template.html

対応テンプレート

※ このセクションは SCSS 生成 の対応範囲です。
プレースホルダ付与の制限は上記「制限事項」を参照してください。

テンプレート対応状況
プレーン HTML完全対応
AstroFront-matter を自動削除
EJS<% ... %> を自動削除
Nunjucks{{ }} / {% %} / {# #} を自動削除
JSXclass / className から静的クラスを抽出
Vue / Sveltev-* / :prop などの属性を削除(v-bind:class などは残留する場合あり)

テンプレート構文は正規表現で除去します。

  • <%...%>(EJS)
  • {{...}} / {%...%} / {#...#}(Nunjucks)
  • JSX コメント {/*...*/}
  • JSX フラグメント <>...</>
  • <script>...</script> / <style>...</style> ブロック
  • dangerouslySetInnerHTML 属性
  • 属性スプレッド {...foo}
  • テンプレートリテラルの ${...}class / className 内は静的 member access や文字列リテラルを抽出する場合あり)

一般的な {...} は除去対象ではありません。class / className 属性の静的クラス名を抽出します。

Block/Element 判定

class 属性の先頭トークンを base class として判定します。
先頭トークンが blockCase(または customPatterns.block)に一致する場合のみ Block として扱います。
Block/Element として扱いたいクラスは先頭に置いてください。

blockCaseBlock の例elementCaseElement の例
kebab(既定)hero-section, feature-cardkebab(既定)title, body
camelheroSection, featureCardcameltitle, body
pascalHeroSection, FeatureCardpascalTitle, Body
snakehero_section, feature_cardsnaketitle, body

注意:

  • Element は 常に 1 語です。elementCase=camel は小文字 1 語(例: title)、elementCase=pascal は大文字始まり 1 語(例: Title)のみ許可され、bodyText / BodyText のような内部大文字は使えません
  • customPatterns を指定した場合は該当パターンが優先されます
  • customPatterns を指定している場合、プレースホルダ(block-box / element)が命名規則に合わない可能性があるため整合性を確認してください
  • modifierPrefix(デフォルトは -)で始まるクラスは Modifier 扱い、u- で始まるクラスは Utility 扱いになり、base class にはなりません
  • - / _ / u- 始まりのクラスは customPatterns でも base 判定できません

動的クラスの制限

  • Vue/Svelte の :class バインド内の静的クラスは抽出しない(class="..." 側に書くことを推奨)。v-bind:class:class が残る場合があるため注意
  • 動的に出現するクラス名は追跡しない
  • JSX の class / className は SCSS 生成では静的クラスを抽出します(文字列リテラル、テンプレートリテラルの静的部分 + ${} 内の静的 member access や文字列リテラル、member access 例: styles.foo, styles['foo'], styles["foo"])。jsxClassBindings.memberAccessAllowlist を設定している場合は、許可されたベース識別子のみクラスとして扱い、チェーン(例: styles.layout.hero)は動的扱いになります。動的な式は除去されます
  • プレースホルダ付与は動的式を含む JSX バインディングをスキップします(props.classNamefoo && 'bar'、非静的な ${} など)

設定

すべての CLI は spiracss.config.js の設定を参照します。
package.json"type": "module" がある場合は export default を使用してください。

HTML CLI は CJS バンドルのため、Node を --disallow-code-generation-from-strings で起動している場合は ESM 設定を読み込めません。

  • CJS 運用へ切り替え可能な場合は module.exports に切り替えるか、フラグを外してください
  • "type": "module" のプロジェクトでは spiracss.config.js を CJS にできないため、実質フラグを外す必要があります(.cjs / .mjs の自動探索はありません)
  • spiracss.config.js が存在するのに読み込みできない場合は、CLI がエラー終了します(設定の記法や type: "module" を確認してください)