HTML CLI
AI エージェントや自動化スクリプト向けに設計された CLI ツールです。
SpiraCSS の HTML 解析と SCSS 生成のコアロジックを提供します。
- 主な用途: AI エージェント(Codex、Claude Code など)が HTML から SCSS を生成する際に呼び出す
- 副次的な用途: VS Code 拡張がエンジンとして内部で使用
人間がターミナルから直接使うことは想定していません。
手動で SCSS を生成する場合は VS Code 拡張を使用してください。
インストール
Node.js >= 20 が必要です。
yarn add -D @spiracss/html-clinpm install -D @spiracss/html-cliコマンド一覧
| コマンド | 説明 |
|---|---|
spiracss-html-to-scss | HTML から SCSS を自動生成 |
spiracss-html-lint | HTML 構造が SpiraCSS ルールに従っているか検証 |
spiracss-html-format | プレースホルダクラス(既定 block-box / element)を付与 |
spiracss-html-to-scss
HTML / テンプレートから SCSS ファイルを自動生成します。デフォルトで構造 lint を実行し、エラーがある場合は終了します(--ignore-structure-errors で無視)。
基本的な使い方
# ルートモード(コンポーネントのルートを 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 PATH | SCSS 出力先ディレクトリ(入力ファイル指定時は上書き先、--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 ルールに従っているかを検証します。
基本的な使い方
# コンポーネントのルート要素として検証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_CLASS | Block/Element の命名規則に違反、またはルート要素に class が無い/検証対象要素が見つからない |
MODIFIER_WITHOUT_BASE | Modifier クラスのみで基底クラスがない |
DISALLOWED_MODIFIER | data モード(variant/state 両方 data)で Modifier クラスが使われている |
UTILITY_WITHOUT_BASE | ユーティリティクラスのみで基底クラスがない |
MULTIPLE_BASE_CLASSES | 複数の基底クラスが存在 |
ROOT_NOT_BLOCK | ルート要素が Block ではない |
ELEMENT_WITHOUT_BLOCK_ANCESTOR | Element が Block の子孫ではない |
ELEMENT_PARENT_OF_BLOCK | Element が Block の親になっている |
DISALLOWED_VARIANT_ATTRIBUTE | class モードで data-variant などのバリアント属性が使われている |
DISALLOWED_STATE_ATTRIBUTE | class モードで data-state/aria-* などの状態属性が使われている |
INVALID_VARIANT_VALUE | data モードのバリアント値が valueNaming に違反 |
INVALID_STATE_VALUE | data モードの状態値が valueNaming に違反 |
UNBALANCED_HTML | HTML の開始・終了タグが不整合 |
MULTIPLE_ROOT_ELEMENTS | ルートモードで複数のルート要素が検出された |
補足:
--selectionモードではclassを持つ要素のみ検証(該当要素が無い場合はINVALID_BASE_CLASS)data-*/aria-*は予約キー(variant.dataKeys/state.dataKey/state.ariaKeys)のみ対象- data 値の命名は
selectorPolicy.valueNamingとvariant/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 構造を整形します。
基本的な使い方
# ファイルから読み取り、別ファイルに出力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.js の htmlFormat.classAttribute に従います(既定は class)。
className を使う場合は className を指定してください。
内部では class / className を一時的に data-spiracss-classname に変換して処理し、出力時に設定値へ戻します。
付与されるクラス
すべての子孫要素を再帰的に走査し、SpiraCSS の Block > Element 構造に整形します。
- 子要素を持つ要素 → Block プレースホルダを先頭に付与
- 子要素を持たない要素(葉ノード) → Element プレースホルダを先頭に付与
- 既に Block 名がある要素 → そのまま維持し、内部を再帰処理
- Element 名があるが子を持つ要素 → Block 形式に変換(例:
title→title-box)
※ ルート要素が Element の場合は Element → Block 変換を行わず、block-box title のように Block プレースホルダを先頭に付与します(Block/Element 以外でも同様)。
プレースホルダ名は blockCase / elementCase 設定に応じて変わります(両者は独立して設定可能):
| case | Block プレースホルダ | Element プレースホルダ |
|---|---|---|
kebab(既定) | block-box | element |
camel | blockBox | element |
pascal | BlockBox | Element |
snake | block_box | element |
注意:
- 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 | 完全対応 |
| Astro | Front-matter を自動削除 |
| EJS | <% ... %> を自動削除 |
| Nunjucks | {{ }} / {% %} / {# #} を自動削除 |
| JSX | class / className から静的クラスを抽出 |
| Vue / Svelte | v-* / :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 として扱いたいクラスは先頭に置いてください。
blockCase | Block の例 | elementCase | Element の例 |
|---|---|---|---|
kebab(既定) | hero-section, feature-card | kebab(既定) | title, body |
camel | heroSection, featureCard | camel | title, body |
pascal | HeroSection, FeatureCard | pascal | Title, Body |
snake | hero_section, feature_card | snake | title, 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.className、foo && '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"を確認してください)