フォームのCSS

フォームは特にブラウザごとの表示の差異が大きいので、普段あまりCSSを使わない人向けにベース用のCSSを書いてみました。

[フォームのJavaScript](https://qiita.com/takeshisakuma/items/f1ae123457bc99f23f7d)、[フォームのHTML](https://qiita.com/takeshisakuma/items/8198e78e28d346350868)も併せてご参照ください。

## CSS適用前後の比較
### before
CSS適用前画像
![nocss.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/c80e5c17-beb7-5bc3-b7ab-91eff9909e23.png)

### after
CSS適用後画像(HTMLもデザイン変更に必要な部分は一部変更しています)
![after.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/451263/d3e6ce52-18e7-1a57-265d-71d45d25a6d2.png)



## 共通リセット

```:css
input,
button,
textarea,
select {
  /* デフォルトスタイルをリセット */
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;

  /* font-familyを継承しないので、継承させる */
  font-family: inherit;

  /* iOSの角丸をリセット */
  border-radius: 0;

  /* box-size */
  box-sizing: border-box;

  /* 文字の大きさ iOSでズームさせないために16px以上を指定 */
  font-size: 16px;

  /* 文字色を親から継承 */
  color: inherit;
}

label {
  /* iOSでのlabelとinput,select,textareaの関連付け */
  cursor: pointer;
}

/* スピンボタン非表示 chrome,safari */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button,
input[type="month"]::-webkit-outer-spin-button,
input[type="month"]::-webkit-inner-spin-button,
input[type="datetime-local"]::-webkit-outer-spin-button,
input[type="datetime-local"]::-webkit-inner-spin-button,
input[type="week"]::-webkit-outer-spin-button,
input[type="week"]::-webkit-inner-spin-button,
input[type="time"]::-webkit-outer-spin-button,
input[type="time"]::-webkit-inner-spin-button,
input[type="date"]::-webkit-outer-spin-button,
input[type="date"]::-webkit-inner-spin-button {
  /*-webkit-appearance: none;
  margin: 0;*/
  display: none;
}

/* スピンボタン非表示(firefox) */
input[type="number"],
input[type="month"],
input[type="datetime-local"],
input[type="week"],
input[type="time"],
input[type="date"] {
  -moz-appearance: textfield;
}

/* クリアボタン非表示 */
input[type="date"]::-webkit-clear-button,
input[type="month"]::-webkit-clear-button,
input[type="datetime-local"]::-webkit-clear-button,
input[type="time"]::-webkit-clear-button,
input[type="week"]::-webkit-clear-button {
  -webkit-appearance: none;
}
``` 



## input

### input共通CSS

```:css
/* input */
input {
  /* 背景色(任意の色を指定) */
  background-color: #eee;

  /* inputの枠線を消す */
  border: 1px solid transparent;
  transition: border 0.2s ease-out;

  /* 文字色を親から継承 */
  color: inherit;

  /* 任意の高さ */
  height: 46px;

  /*inputのフォーカス時の枠線を消す*/
  outline: 0;
}

/* inputにフォーカスが当たっている時 */
input:focus {
  border-bottom: 1px solid#e74c3c;
}
```



### input(日時系以外)

```:html
<div>
  <label for="form__input--text">テキスト(type="text")</label>
  <input type="text" name="text" id="form__input--text">
</div>
 
<div>
  <label for="form__input--email">メールアドレス(type="email")</label>
  <input type="email" name="email" id="form__input--email">
</div>
 
<div>
  <label for="form__input--password">パスワード(type="password")</label>
  <input type="password" name="password" id="form__input--password">
</div>
 
<div>
  <label for="form__input--url">URL(type="url")</label>
  <input type="url" name="url" id="form__input--url">
</div>
 
<div>
  <label for="form__input--tel">電話番号(type="telephone")</label>
  <input type="tel" name="tel" id="form__input--tel">
</div>
 
<div>
  <label for="form__input--search">検索ワード(type="search")</label>
  <input type="search" name="search" id="form__input--search">
</div>
 
<div>
  <label for="form__input--number">数(type="number")</label>
  <input type="number" name="number" id="form__input--number">
</div>
```

```:css
/* サーチキャンセルボタンのデザイン変更 */
input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  height: 16px;
  width: 16px;
  background-image: url(cancel.svg);
  background-repeat: no-repeat;
  background-size: cover;

  /* サーチキャンセルボタンを非表示にする場合 */
  /*-webkit-appearance: none;*/
}

input[type="search"]::-webkit-search-decoration {
  display: none;
}

input[type="search"]:focus {
  outline-offset: -2px;
}

/* パスワードの表示/非表示の切り替えスイッチ表示(IE、Edge)の非表示 */
input[type=password]::-ms-reveal{
   display:none;
 }
```



### input(日時系)

```:html
<div>
  <label for="form__input--date">日付(type="date")</label>
  <input type="date" name="date" id="form__input--date">
</div>

<div>
  <label for="form__input--datetime-local">日時(type="datetime-local")</label>
  <input type="datetime-local" name="datetime-local" id="form__input--datetime-local">
</div>

<div>
  <label for="form__input--month">月(type="month")</label>
  <input type="month" name="month" id="form__input--month">
</div>

<div>
  <label for="form__input--week">週(type="week")</label>
  <input type="week" name="week" id="form__input--week">
</div>
 
<div>
  <label for="form__input--time">時(type="time")</label>
  <input type="time" name="time" id="form__input--time">
</div>
```

```:css
/* カレンダー表示ボタン アイコン変更 */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator,
input[type="week"]::-webkit-calendar-picker-indicator {
  color: transparent;
  cursor: pointer;
  background: #eee url(calendar.svg) no-repeat center right 0px/16px 16px;
  background-size: 16px;
}
```


### range

```:html
<div>
  <label for="form__input--range">範囲(type="range")</label>
  <input type="range" name="range" id="form__input--range">
</div>
```

```:css
input[type=range] {
  -webkit-appearance: none;
  /* height: 46px; */
}

/* ツールチップ 非表示(ms) */
input[type=range]::-ms-tooltip {
  display: none;
}

/* ツマミ(webkit) */
input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 0 4px 0 #333;
  cursor: pointer;
  height: 20px;
  width: 20px;
  margin-top: -10px;
  /* iOS safari */
  border: none;
}

/* ツマミ(mozilla) */
input[type=range]::-moz-range-thumb {
  background-color: #fff;
  border: none;
  border-radius: 50%;
  box-shadow: 0 0 4px 0 #333;
  cursor: pointer;
  height: 20px;
  width: 20px;
}

/* ツマミ(ms) */
input[type=range]::-ms-thumb {
  background-color: #fff;
  border: none;
  border-radius: 50%;
  box-shadow: 0 0 4px 0 #333;
  cursor: pointer;
  height: 20px;
  width: 20px;
  margin: 0 1px;
}

/* ツマミ(ms) chomeツマミ調整の影響を消す */
@supports (-ms-ime-align: auto) {
  input[type=range]::-webkit-slider-thumb {
    margin-top: 0px !important;
  }

}

/* 溝(mozilla) */
input[type=range]::-moz-range-track {
  height: 0;
  border: 1px solid #333;
}

/* 溝(WebKit) */
input[type=range]::-webkit-slider-runnable-track {
  width: 100%;
  height: 0;
  cursor: pointer;
  background: #cccccc;
  border: 1px solid #333;
  margin-top: 0px;
}

/* 溝(ms) */
input[type=range]::-ms-track {
  width: 100%;
  height: 12px;
  cursor: pointer;
  background: transparent;
  border-color: transparent;
  border-width: 16px 0;
  color: transparent;
}

/* 溝の位置調整(ie) */
@media all and (-ms-high-contrast: none) {
  input[type=range]::-ms-track {
    margin-top: -5px !important;
  }
}

/* 溝の色(ms)（つまみより左側） */
input[type=range]::-ms-fill-lower {
  background: #cccccc;
  border: 0.2px solid #010101;
  border-radius: 2.6px;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
}

/* 溝の色(ms)（つまみより右側） */
input[type=range]::-ms-fill-upper {
  background: #cccccc;
  border: 0.2px solid #010101;
  border-radius: 2.6px;
  box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
}
 
/* 溝の色(ms)（つまみより左側）フォーカス時 */
input[type=range]:focus::-ms-fill-lower {
  background: #999999;
}

/* 溝の色(ms)（つまみより右側）フォーカス時 */
input[type=range]:focus::-ms-fill-upper {
  background: #999999;
}
``` 


### color

```:html
<div>
  <label for="form__input--color">色(type="color")</label>
  <input type="color" name="color" id="form__input--color">
</div>
```

```:css
/* なし(共通リセットのみ) */
```

### file

```:html
<div>
  <p>ファイル(type="file")</p>

  <input type="file" name="file" id="form__input--file">
  <label for="form__input--file">ファイルを選択</label>
</div>
```

```:css
input[type="file"] {
  display: initial;
  position: absolute;
  opacity: 0;
}

input[type="file"]+label {
  background: #2980b9;
  border-bottom: 4px solid #434343;
  border-radius: 4px;
  color: #FFF;
  display: table;
  font-weight: bold;
  line-height: 1;
  margin: 10px auto 0;
  padding: 20px 40px;
  transition: background-color 0.2s ease-out, transform 0.2s ease-out, border-bottom 0.2s ease-out;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

input[type="file"]:hover+label,
input[type="file"]:focus+label{
  background: #3498db;
}

input[type="file"]:active+label {
  background: #34495e;
  border-bottom: 4px solid transparent;
  transform: translateY(4px);
}
```

### radio

```:html
<fieldset>
  <legend>性別(type="radio")</legend>

  <div class="radiobuttonMember">
    <input type="radio" name="sex" value="male" id="form__input--radio1">
    <label for="form__input--radio1">
      <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64">
        <path d="M21.184 54.944c-9.963-.554-17.871-8.793-17.871-18.88 0-10.442 8.479-18.909 18.938-18.909s18.938 8.466 18.938 18.909-8.479 18.91-18.938 18.91" />
      </svg>男
    </label>
  </div>
       
  <div class="radiobuttonMember">
    <input type="radio" name="sex" value="female" id="form__input--radio2">
    <label for="form__input--radio2">
    <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64">
      <path d="M21.184 54.944c-9.963-.554-17.871-8.793-17.871-18.88 0-10.442 8.479-18.909 18.938-18.909s18.938 8.466 18.938 18.909-8.479 18.91-18.938 18.91" />
     </svg>女</label>
  </div>
</fieldset>
```

```:css
.radiobuttonMember {
  margin-top: 10px;
}

/* デフォルトのinputを隠す */
input[type="radio"] {
  display: initial;
  position: relative;
  opacity: 0;
}

/* ラベルの前の四角形を作る */
input[type="radio"]+label::before {
  content: "";
  position: absolute;
  border: 2px solid #343434;
  width: 16px;
  height: 16px;
  margin-top: 4px;
}

/* ラベルの前の四角形のフォーカス時 */
input[type="radio"]:focus+label::before {
  border: 2px solid #e74c3c;
}

/* SVGの位置と大きさの指定 */
input[type="radio"]+label>svg {
  width: 20px;
  height: 20px;
  position: relative;
  margin-right: 5px;
  left: 3px;
  top: 3px;
}
 
/* チェックマークの設定(未チェック時)  */
input[type="radio"]+label>svg>path {
  fill: none;
  stroke: #27ae60;
  stroke-linejoin: round;
  stroke-linecap: round;
  stroke-dasharray: 130;
  stroke-dashoffset: 100;
  stroke-opacity: 0;
  stroke-width: 8px;
  transition: stroke-dashoffset 0.2s ease-out;
}
 
/* チェックマークの設定(チェック時) */
input[type="radio"]:checked+label>svg>path {
  stroke-dashoffset: 0;
  stroke-opacity: 1;
}

/* ラベルの位置調整 */
input[type="radio"]+label {
  left: -30px;
  position: relative;
}

```

### checkbox

```:html
<fieldset>
  <legend>趣味(type="checkbox")</legend>

  <div class="checkboxMember">
    <input type="checkbox" name="hobby" value="listenmusic" id="form__input--checkbox1">
    <label for="form__input--checkbox1">
      <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64">
        <path d="M18 4l-6 42s66-68 74-40" />
      </svg>音楽鑑賞
    </label>
  </div>

  <div class="checkboxMember">
    <input type="checkbox" name="hobby" value="readbook" id="form__input--checkbox2">
    <label for="form__input--checkbox2">
      <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64">
        <path d="M18 4l-6 42s66-68 74-40" />
      </svg>読書
    </label>
  </div>

  <div class="checkboxMember">
    <input type="checkbox" name="hobby" value="watchmovie" id="form__input--checkbox3">
    <label for="form__input--checkbox3">
      <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64">
        <path d="M18 4l-6 42s66-68 74-40" />
      </svg>映画鑑賞
    </label>
  </div>

  <div class="checkboxMember">
    <input type="checkbox" name="hobby" value="playgame" id="form__input--checkbox4">
    <label for="form__input--checkbox4">
      <svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 64 64">
        <path d="M18 4l-6 42s66-68 74-40" />
      </svg>ゲーム
    </label>
  </div>

</fieldset>
```


```:css
.checkboxMember {
  margin-top: 10px;
}

/* デフォルトのinputを隠す */
input[type="checkbox"] {
  display: initial;
  position: relative;
  opacity: 0;
}

/* ラベルの前の四角形を作る */
input[type="checkbox"]+label::before {
  content: "";
  position: absolute;
  border: 2px solid #343434;
  width: 16px;
  height: 16px;
  margin-top: 4px;
}

/* ラベルの前の四角形のフォーカス時 */
input[type="checkbox"]:focus+label::before {
  border: 2px solid #e74c3c;
}

/* SVGの位置と大きさの指定 */
input[type="checkbox"]+label>svg {
  width: 20px;
  height: 20px;
  position: relative;
  margin-right: 5px;
  left: 3px;
  top: 2px;
}

/* チェックマークの設定(未チェック時)  */
input[type="checkbox"]+label>svg>path {
  fill: none;
  stroke: #e74c3c;
  stroke-linejoin: round;
  stroke-linecap: round;
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  stroke-opacity: 0;
  stroke-width: 8px;
  transition: stroke-dashoffset 0.1s ease-out;
}

/* チェックマークの設定(チェック時) */
input[type="checkbox"]:checked+label>svg>path {
  stroke-dashoffset: 0;
  stroke-opacity: 1;
}

/* ラベルの位置調整 */
input[type="checkbox"]+label {
  left: -30px;
  position: relative;
}


```


## selectbox

```:html
<div>
  <label for="form__select--blood">血液型(select)</label>
  <select name="blood" id="form__select--blood">
    <option value="A">A型</option>
    <option value="B">B型</option>
    <option value="O">O型</option>
    <option value="AB">AB型</option>
  </select>
</div>
```

```:css
select {
  /* 右端の▼を消す */
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  border: none;

  /* 代わりに任意の画像を指定 */
  background: #eee url(triangle_down.svg) no-repeat center right 10px/16px 16px;
  background-size: 10px;
}

 
/* 右端の▼を消す(IE) */
select::-ms-expand {
  display: none;
}

select:focus {
  background: #eee url(triangle_up.svg) no-repeat center right 10px/16px 16px;
  background-size: 10px;
  border-bottom: 1px solid#e74c3c;
  outline: none;
}
```

## textarea

```:html
<div>
  <label for="form__textarea">コメント(textarea)</label>
  <textarea name="comments" rows="4" cols="40" id="form__textarea"></textarea>
</div>
```

```:css
textarea {
  padding: 10px;
  border-radius: 0;
  resize: none;
  border: 1px solid transparent;
  transition: border 0.2s ease-out;
  outline: none;
  background-color: #eee;
}

textarea::-webkit-scrollbar {
  width: 10px;
}

textarea::-webkit-scrollbar-track {
  background-color: #eee;
}

textarea::-webkit-scrollbar-thumb {
  background-color: #ccc;
}

textarea:focus {
  border-bottom: 1px solid #e74c3c;
  box-shadow: none;
  outline: none;
}
``` 


## ボタン
フォームでは```input[type="submit"]```や```input[type="reset"]```が用意されていますが、特に理由がなければ```button[type="submit"]```を使用したほうが良いです。

- 送信ボタン```input[type="submit"]```、リセットボタン```input[type="reset"]```はCSSが効きにくい
- リセットボタンはユーザビリティ的に配置自体しない方が良いので```button[type="reset"]```も使わない

```:html
<div>
  <p>送信ボタン(button)</p>
  <button type="submit" value="send">送信</button>
</div>
```

```:css
button {
  background: #16a085;
  border: none;
  border-bottom: 4px solid #434343;
  border-radius: 4px;
  color: #FFF;
  display: table;
  font-weight: bold;
  line-height: 1;
  margin: 10px auto 0;
  outline: none;
  padding: 20px 40px;
  transition: background-color 0.2s ease-out, transform 0.2s ease-out, border-bottom 0.2s ease-out;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
 
button:hover,
button:focus {
  background: #1abc9c;
}

button:active {
  background: #006266;
  border-bottom: 4px solid transparent;
  transform: translateY(4px);
}
```

## 状態別
### プレースホルダの文字色

```:html
<div>
  <label for="form__input--text">テキスト(type="text")</label>
  <input type="text" name="text" id="form__input--text" placeholder="プレースホルダ">
</div>

<div>
  <label>コメント</label>
  <textarea name="comments" rows="2" cols="40" placeholder="プレースホルダ"></textarea>
</div>
```

```:css
/* placeholderの文字色指定 */
input::placeholder,
textarea::placeholder {
  color: #7f8c8d;
}

/* IE (疑似クラスで指定) */
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
  color: #7f8c8d;

}

/* Edge (疑似要素で指定)*/
input::-ms-input-placeholder,
textarea::-ms-input-placeholder {
  color: #7f8c8d;
}
```
 
### オートコンプリート有効時
browser-syncを使用しているときは無効なので気づきにくいですが、chromeはオートコンプリート使用時に独自のスタイルが効いてしまうので、リセットします。

```:css
/* auto complete(chrome) */
input:-webkit-autofill {
  /* 背景色(background-colorではなくbox-shadow) */
  -webkit-box-shadow: 0 0 0 1000px #eee inset;

  /* 文字色(colorではなく-webkit-text-fill-color) */
  -webkit-text-fill-color: #333 !important;
}
```

### 入力値の検証
inputタグのtype属性で指定されている形式に合致しているか、していないかを判定できます。

```:css
input:invalid{
  /* 入力値がOKのときのスタイルを記述 */
}
 
input:valid {
  /* 入力値がNGのときのスタイルを記述 */
}
```

### 値の範囲の検証


|疑似クラス|状態|
|---|---|
|in-range|inputタグのminやmax属性で指定されている数値の範囲に合致している|
|out-of-range|inputタグのminやmax属性で指定されている数値の範囲に合致していない|


```:html
<input type="number" min="1" max="20">
```

```:css
input:in-range {
  /* 範囲内のときのスタイルを記述 */
}

input:out-of-range {
  /* 範囲外のときのスタイルを記述 */
}
```

### その他の擬似クラス

|疑似クラス|状態|
|---|---|
|required|requiredが指定されている(入力必須)|
|optional|入力必須でない入力要素|
|disabled|disabledが指定されている(変更できない)|
|read-only|readonlyが指定されている(変更できない)|
|empty|値が空|
|placeholder-shown|スレースホルダが表示されている|
|focus|フォーカスが当たっている|

table:disabledとreadonlyの違い

||disabled|readonly|
|---|---|---|
|値の編集|できない|できない|
|form送信時|値を送信しない|値を送信する|
|値の選択やコピー|選択不可|選択可|



## フローティングラベル
最近流行りのフローティングラベルもCSSで対応できます。

```:html
<div>
 <label class="floatinglabel">
   <input type="text" placeholder="&nbsp;">
   <span class="labeltext">名前</span>
 </label>
</div>
```

```:css
.floatinglabel {
  margin-top: 40px;
  position: relative;
}

.floatinglabel input:not(:placeholder-shown) + span {
  transform: translateY(-38px) translateX(-16px);
}

.labeltext {
  position: absolute;
  top: 0px;
  left: 16px;
  font-size: 16px;
  color: #7f8c8d;
  transform-origin: 0 0;
  transition: all 0.2s ease-out;
}

.floatinglabel input:focus+span {
  color: #333;
  transform: translateY(-38px) translateX(-16px)
}
```

## リポジトリ

https://github.com/takeshisakuma/css_for_form