HTML

The min and max attributes declare the smallest and largest values an input may accept—letting the browser automatically block anything outside that range.

<input min max>

そもそも何を解決するのか

input 要素の minmax 属性を指定すると、数値型や日付型の入力欄で入力できる最小値と最大値を指定することができます。

自由入力フィールドは便利ですが、想定より小さすぎる数や未来すぎる日付が送られてくるとビジネスロジックが壊れます。

minmax 属性は「この範囲内しか受け付けません」とブラウザーに宣言することで、

という三つの効果を同時に達成します。

属性

min
最小値を指定する。
max
最大値を指定する。

基本の書き方と動作の流れ

HTML

<label>
	年齢:
	<input type="number" name="age" min="0" max="120" step="1" required>
</label>
  1. 値が入力される
  2. パース (解析) – type に応じて数値や日付へ変換
  3. 制約チェック – 変換後の値が minmax の範囲かを検証
  4. エラーなら :invalid 疑似クラスを付与、フォーム送信をブロック

min / max は “表示用のヒント” ではなく Constraint Validation API が参照する 仕様レベルの制約 です。

対応している type

直感的用途 type 設定例 min / max の値の書式例
整数・小数 number / range 0 , 10.5
絶対時間 date / datetime-local 2025-01-01 , 2025-01-01T09:00
年月 month 2025-07
week 2025-W27
時刻 time 09:00 , 23:59

ブラウザー UI とバリデーション挙動の差異

Chrome / Edge
範囲外入力時に赤枠+ツールチップ
Firefox
ツールチップ文言が独自(日本語ロケール可)
Safari (iOS)
モバイルキーボードの 増減ボタン が min / max に合わせて停止
古い IE
type="number" 未実装。polyfill か JavaScript でフォールバック必須

step と組み合わせるときの注意点

stepmin と “割り切れない” 場合、ブラウザーは「最も近い有効値」を提案 しつつ、依然として制約エラー (stepMismatch) を返します。

HTML

<!-- 例: 0.3 刻みだが min は 0.2 -->
<input type="number" min="0.2" max="1" step="0.3">
<!-- 0.2, 0.5, 0.8 が OK/0.3 は NG -->

アクセシビリティ: 画面読み上げとの統合

JavaScript での制御例


0〜120 の整数で入力してください

HTML

<label>
	年齢:
	<input
		type="number"
		id="ageInput"
		name="age"
		min="0"
		max="120"
		step="1"
		required
	>
</label>
<button id="showAgeBtn" type="button">表示</button><br>
<!-- バリデーション範囲のヒント -->
<small id="ageHint">0〜120 の整数で入力してください</small>
<!-- 結果やエラーをここに出力 -->
<p id="message" aria-live="polite"> </p>

<script>
	<!-- JavaScript code here -->
</script>

JavaScript

const ageInput = document.getElementById('ageInput');
const message = document.getElementById('message');
const showAgeBtn = document.getElementById('showAgeBtn');

showAgeBtn.addEventListener('click', () => {
	if (ageInput.validity.valid) {
		message.textContent = `入力された年齢: ${ageInput.value}`;
	} else {
		message.textContent = '年齢は 0 から 120 の範囲で入力してください。';
	}
});
ageInput.addEventListener('input', () => {
	if (ageInput.validity.rangeUnderflow) {
		message.textContent = '年齢は 0 以上でなければなりません。';
	} else if (ageInput.validity.rangeOverflow) {
		message.textContent = '年齢は 120 以下でなければなりません。';
	} else if (ageInput.validity.stepMismatch) {
		message.textContent = `年齢は ${ageInput.step} 刻みで入力してください。`;
	} else {
		message.textContent = '';
	}
});

setCustomValidity() を使ったサンプル


0〜120 の整数で入力してください

HTML

<label>
	年齢:
	<input
		type="number"
		id="ageInputCv"
		name="age"
		min="0"
		max="120"
		step="1"
		required
		aria-describedby="ageHintCv"
	>
</label>
<button id="showAgeBtnCv" type="button">表示</button><br>
<!-- バリデーション範囲のヒント -->
<small id="ageHintCv">0〜120 の整数で入力してください</small>
<!-- 結果やエラーをここに出力 -->
<p id="messageCv" aria-live="polite"></p>

<script>
	<!-- JavaScript code here -->
</script>

JavaScript

(function () {
	const ageInput  = document.getElementById('ageInputCv');
	const showBtn   = document.getElementById('showAgeBtnCv');
	const messageEl = document.getElementById('messageCv');

	/** カスタムバリデーションの付与・解除 */
	function applyCustomValidity() {
		if (!ageInput.value) {
			ageInput.setCustomValidity('年齢を入力してください');
		} else if (ageInput.validity.rangeUnderflow) {
			ageInput.setCustomValidity('年齢は 0 歳以上で入力してください');
		} else if (ageInput.validity.rangeOverflow) {
			ageInput.setCustomValidity('年齢は 120 歳以下で入力してください');
		} else if (ageInput.validity.stepMismatch) {
			ageInput.setCustomValidity('整数で入力してください');
		} else {
			ageInput.setCustomValidity(''); // エラー解除
		}
	}

	/** インプット時に即時検証 */
	ageInput.addEventListener('input', () => {
		applyCustomValidity();
		messageEl.textContent = ageInput.validationMessage; // 空文字なら正常
	});

	/** ボタン押下時の処理 */
	showBtn.addEventListener('click', () => {
		applyCustomValidity();          // 念のため再検証
		if (ageInput.checkValidity()) { // すべて OK
			messageEl.textContent = `入力された年齢: ${ageInput.value}`;
		} else {
			// ブラウザー標準のエラー吹き出しも表示
			ageInput.reportValidity();
			// messageEl には既にエラー文が入っている
		}
	});
})();
仕組みのポイント
setCustomValidity('…')
空文字ならエラー解除、文字列を渡すとその場で無効 (:invalid) に。
validationMessage
setCustomValidity() で設定した文言がそのまま取得できるので、同じテキストを <p> にも反映。
reportValidity()
ボタン押下時に呼ぶことで、ブラウザー標準の吹き出しも並行表示。
アクセシビリティ
aria-describedby で範囲ヒントを事前提供、エラーは aria-live に即時反映。

サーバー側バリデーションの必然

HTML の制約は「親切な顧客に対するサービス」であり、悪意あるリクエスト は cURL や改竄 JS で容易に突破できます。

したがって、サーバー側でも同じ制約を再実装し、受信した値が min / max の範囲内であることを確認する必要があります。

このように、クライアント側の制約はユーザー体験を向上させるためのものであり、セキュリティやデータ整合性のためにはサーバー側でも同様のチェックが不可欠です。

PHP

$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, [
	'options' => ['min_range' => 0, 'max_range' => 120]
]);
if ($age === false) {
	http_response_code(400);
	exit('Invalid age');
}

実践シナリオ

年齢入力フォーム
min="0" max="120"
チケット価格
type="number" step="0.01" min="0" max="99999.99"
予約日付
type="date" min="<?= date('Y-m-d') ?>" max="2026-12-31"
作業開始時刻
type="time" min="09:00" max="18:00"
動画再生速度スライダー
type="range" min="0.25" max="2" step="0.05"

よくある落とし穴

ベストプラクティスまとめ

UI・UX
境界値をラベルやプレースホルダーにも明示
アクセシビリティ
エラー時は aria-live で動的メッセージを読ませる
セキュリティ
サーバー側で同一制約を再検証
保守性
境界値は 定数化 し、クライアントと API で一元管理
互換性
非対応ブラウザー検出時は Polyfill か pattern 属性で代替検証

よく使われる 15 のユースケース

数字・日付・時刻など “連続値” を扱う入力欄では、境界値を宣言するだけで UX と品質が大きく向上 します。以下は実務で頻出するシーンをタイプ別にまとめたものです。

数値 (type="number" / type="range")

年齢登録フォーム
min="0"max="120" で非現実的な値をブロック。
チケット枚数・商品の購入数量
min="1"max="10" など在庫上限に合わせて制限。
EC サイトの価格フィルター
上限付きのスライダー (range) で min="0"max="50000"
パスワード長さのカスタム設定
管理画面で min="6"max="128" を指定し、不適切な長さを防止。
動画再生速度や音量のスライダー
min="0.25"max="2"step="0.05" で倍速範囲をコントロール。

日付 (type="date" / type="datetime-local")

誕生日入力
min="1900-01-01"max="<?= date('Y-m-d') ?>" で未来日を排除。
ホテルや航空券の予約受付期間
min="<?= date('Y-m-d') ?>"max="2026-12-31" — 予約開始日から 1 年半先まで許可。
期間限定キャンペーン応募フォーム
min="2025-08-01"max="2025-08-31" の 1 か月間に限定。
勤怠システムの勤務開始日時
min="2025-04-01T00:00" で年度開始以降のみ許可。

時刻 (type="time")

営業時間内の問い合わせ予約
min="09:00"max="18:00" で深夜帯を自動却下。
レストランのディナー席予約
min="17:30"max="22:00"、さらに step="900"(15 分刻み)で UX 向上。

月・週 (type="month" / type="week")

家計簿の入力期間
type="month"min="2020-01"max="<?= date('Y-m') ?>"
スクラムのスプリント週選択
type="week" で四半期内 (min="2025-W27"max="2025-W39") に限定。

システム設定・管理系

ファイルアップロードの同時実行数
管理 UI で min="1"max="8" — サーバー負荷をコントロール。
メール送信リトライ回数
min="0"max="5" を設定し、過度なリトライを防止。

まとめ

minmax は 入力制御・データ品質・アクセシビリティを同時に底上げできる “宣言的ガードレール” です。ユーザー体験を損なわず、サーバー側のバリデーションと二重化することで、安全かつ保守性の高いフォームを実現できます。

ありきたりではない「min / max」の使い倒しアイデア集

単に「範囲を決める」だけではもったいない。

先進的な UI/UX やドメインロジックと組み合わせることで、minmax は “リアクティブ・パラメータ” へと進化します。

動的境界値 — 他フィールドや外部データをトリガーに即時更新

開始日・終了日ペア
終了日の min を「開始日 + 1 日」に JavaScript で随時書き換え。
change イベントで end.min = dayjs(start.value).add(1, 'day').format('YYYY‑MM‑DD')
株価・暗号資産のリアルタイム取引
WebSocket で受け取った板情報に応じて、価格入力欄の min / max を毎秒更新。
送信前に input.reportValidity() を呼び、自動的に最新の “許容スプレッド” を強制。

ユーザーごとにパーソナライズされた閾値

年齢制限付きコンテンツ
サーバーから返すユーザープロファイルに応じて min="18" もしくは min="21" に切り替え。
クレジット残高に連動する購入数量
API から取得した remainingCredits をそのまま max にバインド。
フロント側で値が変わるたびに max を書き換えることで、二重送信を防止。

非線形スケールをラップする“ゲート”としての利用

音量やゲインを対数スケールで制御
実際に送る値は Math.pow(10, dB/20) だが、ユーザー入力は dB。
min="-60" max="0" にしておき、変換は input ハンドラで行う。
ゲームの難易度スライダー
UI は 0〜100 だが、内部ロジックは指数関数で敵 HP を計算。
min / max が UI の手掛かり、実際のゲームバランスは JS 側で調整。

アクセシビリティ & ガイダンス向上のための“適応的バリデーション”

エラー時に境界を“寄せる”セルフヒーリングフォーム
入力が min 未満なら min をその値に一旦合わせて setCustomValidity('') → ユーザーが再入力しやすい余白を作る。
送信前にサーバー閾値へ戻すことでセキュリティも両立。
aria-livemin/max の協調
min/max 変更時に「入力可能範囲が変わりました」とライブリージョンへ通知し、視覚障害者にも即時共有。

スキーマ駆動開発との連携

GraphQL / JSON Schema → HTML 生成
バックエンドの型定義 (minLength, maximum) をビルド時に静的 HTML へ埋め込み。
“サーバーと同一ソース” なのでバリデーションの ドリフトゼロ。
フォームビルダー SaaS でのメタ・テンプレート
設計画面で管理者が数値を入力すると、そのまま渡された値が公開フォームの min/max へ反映される。

UI コンポーネントの状態同期

CSS の attr() + カスタムプロパティと連動
スライダーのトラック色を --range-max 等で線形グラデーション。
input[min] 変更時に style.setProperty('--range-max', input.max) でダイナミックに再描画。
複数スライダーの“相互拘束”
2 軸フィルター (例: 価格・期間) で、一方の変更に合わせてもう一方の min/max が縮まる。
交差しそうになったら JS でスワップし、ユーザーは常に「左 ≤ 右」を保てる。

IoT・リアルタイム制御ダッシュボード

温室制御アプリ
センサーが壊れたときに飛び値を送らない保険として min="-10" max="50" を設定。
設定温度を変えるごとに MQTT 経由で許容範囲を再配布し、UI と実機が同期。
ドローン操縦アプリ
バッテリー残量や飛行高度の安全域を min/max に反映。
フロント側で geofenceExceeded が発生すると即座に max を下げ、操縦不能域を防ぐ。

セキュリティ・レートリミットのフロント提示

パスワード再試行インターバル
連続失敗回数に応じて待機秒数入力欄の min を指数的に増加させ、人間は編集不可だがロック解除 API が共通パラメータで使う。
2FA トークン残存時間調整
管理 UI で max="300" → 運用変更時にロールアウト用スクリプトが自動で 180 秒 へ書換え。

アニメーションやゲームエンジン的応用

シークバーを“キーフレームスキップ”用に変形
min/max を切り替えて「0〜1.0」のフェーズ位置と「1〜24」の FPS 設定でモードを多重化。
Web MIDI コントローラー
物理ツマミの範囲(0–127)を input[type="range"] にマッピングし、min/max でリング LED と同期。

実験的 API・仕様とのハイブリッド

autocomplete="off" + min/max で PWA オフラインキャッシュ指標
ネットワーク無効時でもキャッシュ済み min/max が残るので、オフラインフォームでも制約が生きる。
Declarative Shadow DOM 内での min/max
<template shadowrootmode="open"> に書いた入力欄でも、ホスト要素が外部から動的に属性を更新可能。
マイクロフロントエンドで “独立デプロイ & 共通制約” を実現。

まとめ

minmax は「入力範囲の固定値」というイメージを超え、リアルタイムで変わるビジネスルールや安全制約を宣言的に押し出す “双方向インターフェース” に進化させることができます。

  1. 動的・個別パーソナライズで UX 最適化
  2. 非線形マッピングや複数フィールド拘束で複雑ロジックを単純化
  3. IoT・セキュリティ領域で「ハードガード」の前段階として活用

JavaScript で値を変えるだけでブラウザー標準のバリデーション機構が再利用できるため、実装コスト < 効果 の非常に高い “モダンなフォーム戦略” と言えます。