



WithCodeMedia-1-pc
WithCodeMedia-2-pc
WithCodeMedia-3-pc
WithCodeMedia-4-pc




WithCodeMedia-1-sp
WithCodeMedia-2-sp
WithCodeMedia-3-sp
WithCodeMedia-4-sp









生徒HTMLのタグの使い方が正しいかどうかを毎回レビューで確認しているんですが、見落としも多くて大変です……自動化できませんか?



Markuplintがその解決策じゃ!HTMLリンターとして、タグの正しい使い方・ARIA属性の適切な設定・アウトライン構造など自動でチェックしてくれるんじゃ。VSCode拡張機能を入れればリアルタイムでエラーを表示してくれるし、pre-commitと組み合わせれば不正なHTMLのコミット自体を防げる。React/Vue/Astro/Svelteにも対応しているんじゃ!
HTMLの品質をレビューに依存していると、レビュワーのスキルによってチェック精度にばらつきが出ます。MarkuplintはHTML専用のリンターで、タグの正しい使い方・ARIAアクセシビリティ・見出しのアウトライン・必須属性など、自動でチェックしてくれます。
本記事では、Markuplintの概要・インストール・全ルール解説・エラーレベルの設定・ignoreコメント・React/Vue/Astro/Svelte対応・VSCode連携・pre-commit+lint-staged連携・GitHub Actionsでの自動化・カスタムルール・monorepo設定・既存PJへの段階的導入戦略・つまずきやすいポイントを完全解説します。
Markuplintは、HTMLの構造・セマンティクス・アクセシビリティを自動検証するHTML専用リンターです。JavaScriptを対象とするESLintとは役割が異なり、両者を組み合わせて使うのが理想的です。
>【Markuplintが自動チェックする内容】
✅ HTMLタグの正しい使い方(構造チェック)
→ <p>の中に<div>は入れられない(インライン要素の親にブロック要素)
→ <li>は<ul>/<ol>の直下にのみ置ける
→ <table>の構造チェック(<thead>/<tbody>/<tr>/<td>の正しい配置)
→ 空要素(<img>/<input>)の誤った閉じタグ
✅ アクセシビリティ(ARIA属性)
→ <img>のalt属性が設定されているか
→ aria-label/aria-labelledbyの正しい使い方
→ <button>にアクセシブルな名前があるか
→ インタラクティブ要素の適切なrole
✅ 見出しのアウトライン(heading-levels)
→ h1 → h2 → h3 の正しい順序で使われているか
→ h1 を飛ばして h3 を使っていないか
→ ページにh1が1つだけ存在するか
✅ 必須属性チェック(required-attr)
→ <img>にwidth・heightがあるか(Core Web Vitals対策)
→ <a>にhrefがあるか
→ <input>にid属性があるか
✅ その他のチェック
→ attr-duplication: 重複属性チェック
→ no-boolean-attr-value: boolean属性の値の不要な記述
→ case-sensitive-attr-name: 属性名の大文字小文字チェック
→ character-reference: 文字参照の正しい使い方
【ESLintとの違い】
ESLint: JavaScript/TypeScriptの構文・スタイルをチェック
Markuplint: HTMLの構造・セマンティクス・アクセシビリティに特化
→ 両方を組み合わせて使うのが理想的># Markuplint本体のインストール
npm install --save-dev markuplint
# 対話式初期設定コマンド(推奨:自動で必要なパッケージを選択・インストール)
npx markuplint --init
# → フレームワークの選択(React/Vue/Svelte/Astro等)
# → 設定ファイルの形式(JSON/YAML/JavaScript)
# → 使用するプリセットルール
# → 必要なパッケージを自動インストール># React / Next.js (JSX/TSX) 対応
npm install --save-dev markuplint @markuplint/jsx-parser @markuplint/react-spec
# Vue.js (.vue) 対応
npm install --save-dev markuplint @markuplint/vue-parser @markuplint/vue-spec
# Astro (.astro) 対応
npm install --save-dev markuplint @markuplint/astro-parser
# Svelte (.svelte) 対応
npm install --save-dev markuplint @markuplint/svelte-parser
# Pug (.pug) 対応
npm install --save-dev markuplint @markuplint/pug-parser
# WordPress / EJS (.php) 対応
# HTMLパーサーがデフォルトで対応(追加不要)>{
"$schema": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/markuplint/schemas/markuplintrc.json",
// プリセットを継承(推奨プリセット)
"extends": ["markuplint:recommended"],
"rules": {
// ===== 構造チェック =====
// aria-*属性の正しい使い方を検証
"invalid-attr": true,
// 要素のコンテンツモデルに違反する子要素を検出
// 例: <p>の中に<div>が含まれる
"required-attr": true,
// 同じ属性名の重複を検出
"attr-duplication": true,
// boolean属性に不要な値が記述されている
// ×: <input disabled="disabled"> → ✅: <input disabled>
"no-boolean-attr-value": true,
// ===== アクセシビリティチェック =====
// ARIAロールの不正な使い方を検出
"wai-aria": true,
// label と input の関連付けを検証
"label-has-control": true,
// ランドマークのラベル付けを検証
"landmark-roles": true,
// 見出しレベルのスキップを検出(h1 → h3 のジャンプ等)
"heading-levels": {
"severity": "error",
"options": {
"minLevel": 1,
"allowMultipleH1": false
}
},
// ===== パフォーマンス・品質チェック =====
// <img>のwidth/height属性(Core Web Vitals対策)
"required-attr": {
"options": {
"attrs": {
"img": ["src", "alt", "width", "height"]
}
}
},
// 空の要素を検出(<p></p> など)
"no-empty-palpable-content": true,
// 大文字小文字の一貫性
"case-sensitive-attr-name": true,
// ===== スタイル設定 =====
// ファイルのエンコーディング宣言
"charset": true
}
}>{
"$schema": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/markuplint/schemas/markuplintrc.json",
// React推奨プリセットを使用
"extends": ["markuplint:recommended-react"],
// ファイル種別とパーサーのマッピング
"parser": {
"\\.jsx$": "@markuplint/jsx-parser",
"\\.tsx$": "@markuplint/jsx-parser"
},
// ファイル種別とスペックのマッピング
"specs": {
"\\.jsx$": "@markuplint/react-spec",
"\\.tsx$": "@markuplint/react-spec"
},
"rules": {
"invalid-attr": {
"options": {
// カスタム属性プレフィックスの無視
"ignoreAttrNamePrefix": [
"css", // CSS Modules: css={{ ... }}
"data-", // HTML data属性
"aria-", // ARIAはスペックで別途管理
"x-" // フレームワーク固有属性
]
}
},
"required-attr": true,
"heading-levels": true,
"wai-aria": true,
// JSXでは className を使うため class のチェックを調整
"no-use-event-handler-attr": false
},
// 特定ファイル・ディレクトリを除外
"excludeFiles": [
"node_modules/**",
".next/**",
"out/**",
"coverage/**"
]
}Markuplintのルールは大きく「構造系」「アクセシビリティ系」「コンテンツ品質系」の3カテゴリに分類できます。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【構造系ルール】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■ invalid-attr
HTML仕様に存在しない属性・値の型違反を検出
例: <div hoge="test"> → 'hoge'は無効な属性
■ attr-duplication
同じ属性を重複して記述した場合に検出
例: <div class="foo" class="bar"> → 重複エラー
■ no-boolean-attr-value
boolean属性に不要な値が記述されている場合に検出
×: <input disabled="disabled"> → ✅: <input disabled>
■ case-sensitive-attr-name
属性名の大文字混在を検出
例: <div Class="foo"> → 'class'が正しい
■ character-reference
文字参照のエラー(& の代わりに & を直接使用など)
■ no-refer-to-non-existent-id
aria-labelledbyやfor属性で参照するIDが存在しない場合に検出
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【アクセシビリティ系ルール】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■ wai-aria
ARIAロール・ステート・プロパティの正しい使い方を検証
例: <div role="button"> ← role=buttonには focusable が必要
■ heading-levels
見出しレベルのスキップを検出(h1→h3のジャンプ等)
例: h1の後にh3が来るとエラー
■ label-has-control
<label>要素がフォームコントロールと正しく関連付けられているか検証
例: for属性が存在するinputのidと一致しているか
■ landmark-roles
ランドマーク要素(nav、main、header等)の適切な使用をチェック
例: <nav> が複数ある場合はaria-labelで区別が必要
■ no-abstract-roles
抽象的なARIAロール(composite, landmark等)の使用を禁止
例: <div role="landmark"> → 抽象ロールは直接使用不可
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
【コンテンツ品質系】
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
■ required-attr
要素に必須とされる属性が欠落している場合に検出
例: <img>にalt・width・heightがない
■ no-empty-palpable-content
知覚可能なコンテンツがない要素を検出
例: <p></p>、<li></li>
■ deprecated-element
廃止された要素の使用を検出
例: <center>、<font>、<marquee>
■ no-use-event-handler-attr
onclickなどのイベントハンドラ属性の直接記述を禁止
例: <button onclick="fn()"> → JSで addEventListener を使うMarkuplintではルールごとにエラーレベルを設定できます。既存プロジェクトへの導入時はまずwarningにして、徐々にerrorに昇格させる戦略が効果的です。
| 設定値 | 動作 | 推奨シーン |
|---|---|---|
true / "error" | エラーとして検出・CIをブロック | 必ず守るべきルール |
"warning" | 警告として表示・CIはブロックしない | 導入初期・徐々に移行するルール |
false | ルール無効 | プロジェクトに合わないルール |
>{
"rules": {
// エラー: CIをブロックする重大なルール
"heading-levels": {
"severity": "error"
},
// 警告: 表示はするが通過させる(導入初期)
"wai-aria": {
"severity": "warning"
},
// 無効: このプロジェクトでは使わないルール
"no-use-event-handler-attr": false
}
}サードパーティコンポーネントや避けられない構造では、ignoreコメントで特定行・特定ルールを無効化できます。
<!-- markuplint-disable heading-levels -->
<h3>サードパーティウィジェット内の見出し</h3>
<!-- markuplint-enable heading-levels -->
<!-- 1行だけ無効にする場合 -->
<!-- markuplint-disable-next-line wai-aria -->
<div role="tooltip" aria-hidden="true">...</div>
<!-- 全ルールを無効にする(非推奨・最小範囲で使う) -->
<!-- markuplint-disable -->
...
<!-- markuplint-enable -->>【VSCode Markuplint 拡張機能のインストール】
1. VSCodeを開く
2. 拡張機能(Ctrl+Shift+X)で「Markuplint」を検索
3. 公式の「Markuplint」をインストール
→ 発行者: markuplint
【動作確認】
- .markuplintrc が存在するプロジェクトで HTML/JSX/Vue 等を開く
- エラー箇所が赤波線で表示され、Problems パネルにエラー内容が出る
- ホバーすると該当ルールの説明と修正ヒントが表示される
【settings.json 推奨設定】
{
"editor.formatOnSave": true,
"": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"markuplint.enable": true,
"markuplint.debug.enable": false
}コミット時にMarkuplintを自動実行することで、不正なHTMLがリポジトリに入り込むことを防げます。
># husky と lint-staged のインストール
npm install --save-dev husky lint-staged
# huskyの初期化
npx husky init
# .husky/pre-commit に以下を記述
npx lint-staged// package.json に lint-staged の設定を追加
{
"lint-staged": {
"*.{html,jsx,tsx,vue,astro,svelte}": [
"markuplint --fix",
"prettier --write"
]
}
}PRのHTML変更に対してMarkuplintを自動実行し、エラーがあればマージをブロックできます。
# .github/workflows/markuplint.yml
name: Markuplint
on:
pull_request:
paths:
- '**/*.html'
- '**/*.jsx'
- '**/*.tsx'
- '**/*.vue'
- '**/*.astro'
- '**/*.svelte'
jobs:
markuplint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run Markuplint
run: npx markuplint "src/**/*.{html,jsx,tsx,vue,astro,svelte}"# Monorepoのディレクトリ構成例
root/
├── .markuplintrc.json ← 共通設定(全アプリ共通ルール)
├── apps/
│ ├── web/ ← Next.js (TSX)
│ │ └── .markuplintrc.json ← アプリ固有の設定
│ └── blog/ ← Astro
│ └── .markuplintrc.json ← アプリ固有の設定
├── packages/
│ └── ui/ ← Reactコンポーネントライブラリ
│ └── .markuplintrc.json// apps/web/.markuplintrc.json(ルートを継承して上書き)
{
"extends": ["../../.markuplintrc.json", "markuplint:recommended-react"],
"parser": {
"\\.tsx$": "@markuplint/jsx-parser"
},
"specs": {
"\\.tsx$": "@markuplint/react-spec"
}
}プロジェクト固有のHTML規約を強制したい場合、カスタムルールを作成できます。
># カスタムルール例2: 特定のクラス名規約を強制する(BEM命名規則チェック)
// markuplint-rules/bem-class-naming.js
import { createRule } from '@markuplint/ml-core'
// BEM命名規則: block__element--modifier
const bemPattern = /^[a-z][a-z0-9]*(__[a-z][a-z0-9]*)?(--[a-z][a-z0-9]*)?$/
export default createRule({
name: 'bem-class-naming',
defaultSeverity: 'warning',
verify({ attr, report }) {
if (attr.name !== 'class') return
const classes = attr.value.split(' ')
for (const cls of classes) {
if (cls && !bemPattern.test(cls)) {
report({
message: `クラス名 "${cls}" はBEM命名規則に違反しています`,
line: attr.line,
col: attr.col,
})
}
}
},
})// .markuplintrc.json でカスタムルールを読み込む
{
"extends": ["markuplint:recommended"],
"customRules": {
"bem-class-naming": "./markuplint-rules/bem-class-naming.js"
},
"rules": {
"bem-class-naming": true
}
}大規模な既存プロジェクトへの導入は、一度に全エラーを修正しようとすると破綻します。以下の4ステップで段階的に導入することを推奨します。
falseまたはwarningにしてMarkuplintをインストールし、CIを通す。heading-levels・required-attrなど影響の大きいルールから順にerrorにする。overridesを使い、既存ファイルはwarning・新規ファイルはerrorという設定にする。overrides対象外にしてerrorルールを適用する。// 段階的導入のための.markuplintrc.json例
{
"extends": ["markuplint:recommended"],
"rules": {
// まずすべてwarningに
"heading-levels": "warning",
"wai-aria": "warning",
"required-attr": "warning"
},
"overrides": {
// 新規ファイル(src/new/配下)はerrorに
"src/new/**/*.{html,tsx,vue}": {
"rules": {
"heading-levels": "error",
"wai-aria": "error",
"required-attr": "error"
}
}
}
}>【よくある問題と解決策】
■ 問題1: アイコンボタンのアクセシブルな名前エラー
<button type="button">
<svg viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12"/></svg>
</button>
→ エラー: 'button' element has no accessible name
✅ 修正方法1: aria-label を追加
<button type="button" aria-label="閉じる">
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
<path d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
✅ 修正方法2: visually-hidden テキストを追加
<button type="button">
<span class="sr-only">閉じる</span>
<svg viewBox="0 0 24 24" aria-hidden="true">...</svg>
</button>
■ 問題2: Reactコンポーネントのprops属性エラー
<MyComponent customProp="value" />
→ エラー: 'customProp' is not a valid attribute
✅ 解決: ignoreAttrNamePrefix または ignoreAttrName で除外
"invalid-attr": {
"options": {
"ignoreAttrNamePrefix": ["custom"]
}
}
■ 問題3: 動的なclass名でBEM違反エラー
<div className={`block ${isActive ? 'block--active' : ''}`}>
→ 動的値はMarkuplintが解析できない
✅ 解決: markuplint-disable-next-line で該当行を除外A. VSCode拡張機能でリアルタイムチェック、pre-commitフックでコミット前チェック、GitHub ActionsでPRマージ前チェックの3段階が理想です。重要度の高い順にGitHub Actions(必須)→ pre-commit(推奨)→ VSCode(任意)の順で導入してください。
A. 段階的な導入が推奨です。最初はルールを一旦すべて false にするか警告レベルにして Markuplint が動く状態にしてから、段階的にルールを有効にしていく方法が実用的です。特に物量が多い場合はoverridesを使って新規ファイルのみ厳格化し、既存ファイルは後から修正していく方法が現実的です。
A. @markuplint/ml-coreのcreateRule関数を使います。上述のカスタムルールセクションの実装例を参考にしてください。
A. Next.js App RouterのサーバーコンポーネントはTypeScript/JSXで記述されるため、通常の@markuplint/jsx-parserで解析可能です。ただし注意点が3つあります。(1)asyncなサーバーコンポーネントの関数宣言はMarkuplintが解析エラーになる可能性があるため、overridesでapp/**/*.tsxの一部ルールを調整する必要があります。(2)'use client'ディレクティブはJSXのコメントではないため解析上問題ありません。(3)Metadata APIで生成されるHTMLタグ(title、meta等)はコンポーネントのJSXに含まれないため、Markuplintのチェック対象外になります。
A. いくつかの方法で強制できます。(1)pre-commitフック(husky + lint-staged): ローカルでコミット時に自動チェック。ただし git commit --no-verify で回避できます。(2)GitHub Actions: PRのCIでMarkuplintを実行し、エラーがあればマージをブロック(Branchプロテクションルールで「Required status checks」に設定)。これは回避不可能で最も確実な方法です。(3)CODEOWNERS + Required Reviews: HTMLファイルの変更に特定レビュワーのapprovalを必須にする。段階的に導入し、最終的にはGitHub ActionsでのCIチェックを必須化するのがベストプラクティスです。
MarkuplintはHTMLの品質をチームで均一に保つための必須ツールです。まずVSCode拡張機能をインストールして、自分のコードがどんなエラーを持っているか確認してみましょう。その後、段階的にpre-commit連携とGitHub Actionsに導入することで、不正なHTMLがリポジトリに入り込むことを根本から防ぐ品質管理の仕組みが完成します。


副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?
未経験でも心配することはありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!
公式サイト より
今すぐ
無料カウンセリング
を予約!