ボタン 初級

アウトライン→塗り

枠線だけのボタンが、ホバーで下から色がせり上がり塗りつぶされ、文字色も同時に白へ反転します。控えめな副次アクションのボタンに向きます。

#css#button#hover

ライブデモ

コード

HTML
<!-- アウトライン→塗りボタン: hoverで下から塗りがせり上がり文字色が反転する -->
<div class="stage">
  <button class="btn-outline-fill" type="button" aria-label="資料をダウンロード">
    <span class="fill" aria-hidden="true"></span>
    <span class="label label--base">資料をダウンロード</span>
    <span class="label label--fill" aria-hidden="true">資料をダウンロード</span>
  </button>
  <p class="hint">ホバーで下から塗りつぶされ、文字色も白へ反転します</p>
</div>
CSS
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  overflow: hidden;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: linear-gradient(160deg, #f6faf8 0%, #eaf5f0 55%, #dff0e8 100%);
}

.stage {
  display: grid;
  place-items: center;
  gap: 22px;
}

:root {
  --accent: #0f9d76;
}

/* ボタン本体: 枠線のみ。fillレイヤーとlabel--fillを内側に重ねる */
.btn-outline-fill {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: inline-grid;
  place-items: center;
  background: #fff;
  border: 2px solid var(--accent);
  border-radius: 14px;
  padding: 19px 54px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--accent);
  cursor: pointer;
  transition: transform .2s ease, box-shadow .3s ease;
  box-shadow: 0 6px 16px rgba(15, 157, 118, .12);
  /* 待機時のアイドル: 枠の外側にごく淡いリングが呼吸する */
  animation: outline-breathe 3.2s ease-in-out infinite;
}

@keyframes outline-breathe {
  0%, 100% { box-shadow: 0 6px 16px rgba(15, 157, 118, .12), 0 0 0 0 rgba(15, 157, 118, 0); }
  50%      { box-shadow: 0 6px 16px rgba(15, 157, 118, .12), 0 0 0 7px rgba(15, 157, 118, .1); }
}

/* 塗りレイヤー: 下から高さで伸びる(scaleでなくheightで、上端をイージングで見せる) */
.fill {
  position: absolute;
  inset: auto 0 0 0;
  height: 0%;
  background: var(--accent);
  z-index: 0;
  transition: height .5s cubic-bezier(.65, 0, .35, 1);
}

.label {
  position: relative;
  z-index: 1;
  grid-area: 1 / 1;
  white-space: nowrap;
}

.label--base { color: var(--accent); }

/* 反転後の白文字: clip-pathで塗りの高さと同じ速度で下から現れる */
.label--fill {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
  clip-path: inset(100% 0 0 0);
  transition: clip-path .5s cubic-bezier(.65, 0, .35, 1);
}

.btn-outline-fill:hover,
.btn-outline-fill:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 12px 26px rgba(15, 157, 118, .28);
  animation-play-state: paused;
}

.btn-outline-fill:hover .fill,
.btn-outline-fill:focus-visible .fill {
  height: 100%;
}

.btn-outline-fill:hover .label--fill,
.btn-outline-fill:focus-visible .label--fill {
  clip-path: inset(0 0 0 0);
}

.btn-outline-fill:active {
  transform: translateY(0) scale(.98);
}

.btn-outline-fill:focus-visible {
  outline: 3px solid rgba(15, 157, 118, .45);
  outline-offset: 4px;
}

.hint {
  margin: 0;
  font-size: 13px;
  color: #52685f;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .btn-outline-fill { animation: none; }
  .fill,
  .label--fill { transition-duration: .01ms; }
}
JavaScript
/* アウトライン→塗りボタン: 演出はCSSのみで完結(height + clip-pathの同期アニメーション)。JS不要。 */

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「アウトライン→塗り」の効果を追加してください。

# 追加してほしい効果
アウトライン→塗り(ボタン)
枠線だけのボタンが、ホバーで下から色がせり上がり塗りつぶされ、文字色も同時に白へ反転します。控えめな副次アクションのボタンに向きます。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- アウトライン→塗りボタン: hoverで下から塗りがせり上がり文字色が反転する -->
<div class="stage">
  <button class="btn-outline-fill" type="button" aria-label="資料をダウンロード">
    <span class="fill" aria-hidden="true"></span>
    <span class="label label--base">資料をダウンロード</span>
    <span class="label label--fill" aria-hidden="true">資料をダウンロード</span>
  </button>
  <p class="hint">ホバーで下から塗りつぶされ、文字色も白へ反転します</p>
</div>

【CSS】
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  overflow: hidden;
  font-family: "Segoe UI", system-ui, sans-serif;
  background: linear-gradient(160deg, #f6faf8 0%, #eaf5f0 55%, #dff0e8 100%);
}

.stage {
  display: grid;
  place-items: center;
  gap: 22px;
}

:root {
  --accent: #0f9d76;
}

/* ボタン本体: 枠線のみ。fillレイヤーとlabel--fillを内側に重ねる */
.btn-outline-fill {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: inline-grid;
  place-items: center;
  background: #fff;
  border: 2px solid var(--accent);
  border-radius: 14px;
  padding: 19px 54px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--accent);
  cursor: pointer;
  transition: transform .2s ease, box-shadow .3s ease;
  box-shadow: 0 6px 16px rgba(15, 157, 118, .12);
  /* 待機時のアイドル: 枠の外側にごく淡いリングが呼吸する */
  animation: outline-breathe 3.2s ease-in-out infinite;
}

@keyframes outline-breathe {
  0%, 100% { box-shadow: 0 6px 16px rgba(15, 157, 118, .12), 0 0 0 0 rgba(15, 157, 118, 0); }
  50%      { box-shadow: 0 6px 16px rgba(15, 157, 118, .12), 0 0 0 7px rgba(15, 157, 118, .1); }
}

/* 塗りレイヤー: 下から高さで伸びる(scaleでなくheightで、上端をイージングで見せる) */
.fill {
  position: absolute;
  inset: auto 0 0 0;
  height: 0%;
  background: var(--accent);
  z-index: 0;
  transition: height .5s cubic-bezier(.65, 0, .35, 1);
}

.label {
  position: relative;
  z-index: 1;
  grid-area: 1 / 1;
  white-space: nowrap;
}

.label--base { color: var(--accent); }

/* 反転後の白文字: clip-pathで塗りの高さと同じ速度で下から現れる */
.label--fill {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: #fff;
  clip-path: inset(100% 0 0 0);
  transition: clip-path .5s cubic-bezier(.65, 0, .35, 1);
}

.btn-outline-fill:hover,
.btn-outline-fill:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 12px 26px rgba(15, 157, 118, .28);
  animation-play-state: paused;
}

.btn-outline-fill:hover .fill,
.btn-outline-fill:focus-visible .fill {
  height: 100%;
}

.btn-outline-fill:hover .label--fill,
.btn-outline-fill:focus-visible .label--fill {
  clip-path: inset(0 0 0 0);
}

.btn-outline-fill:active {
  transform: translateY(0) scale(.98);
}

.btn-outline-fill:focus-visible {
  outline: 3px solid rgba(15, 157, 118, .45);
  outline-offset: 4px;
}

.hint {
  margin: 0;
  font-size: 13px;
  color: #52685f;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .btn-outline-fill { animation: none; }
  .fill,
  .label--fill { transition-duration: .01ms; }
}

【JavaScript】
/* アウトライン→塗りボタン: 演出はCSSのみで完結(height + clip-pathの同期アニメーション)。JS不要。 */

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。