モーダルは強制力の強いUIですが、スライダーを掛け合わせると「モーダルを閉じる」以外の選択肢ができ、その強制力もそこまで強くならず、逆にそのまま欲しい情報を取得できるので、ユーザーライクなUIになります。
そんなスニペットを、以下の記事を基に作っていきます。
JavaScriptのSwiperをモーダルでスライダー・カルーセル表示モーダル+スライダーのUIは比較的多く見かけるので、覚えておくと便利です。
ので、是非参考にしつつ最後までご覧いただけると嬉しいです。
目次
モーダルのプロフィールカードサンプル
早速プロフィールカードのサンプルです。
写真や、名前・紹介文などがのったプロフィールカードが4つ並んでいますが、各カードにある「詳細を見る」を選択すると、そのカードに対応したモーダルが開き、モーダルは閉じる以外に、そのままモーダルを開いた状態でスライダーで他のプロフィールの閲覧が可能になっています。
このようにモーダル+スライダーのカードです。
実装の手順と方法
実装の手順と方法は、全部で4つのSTEPになっています。順に解説していきます。
まずは、Swiperの読み込みの設定を行いましょう。
Swiperは、CDNで配信されているので以下のコードを<head>〜</head>
に記述します。
<link
rel="stylesheet"
href="https://unpkg.com/swiper@7/swiper-bundle.min.css"
/>
<script src="https://unpkg.com/swiper@7/swiper-bundle.min.js"></script>
次に、設置したい場所(ファイル)にHTMLをコピペします。
コードを表示する
<!-- コンテンツ -->
<ul class="swiperModalButton">
<li>
<!-- 中身 -->
<div class="ProfileCardContent">
<div class="ProfileCardImg"> <img src="https://dubdesign.net/wp-content/uploads/2022/03/engineer_bg.jpg" alt="背景の画像" class="ProfileCardImgBg">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/manface2.jpg" alt="顔写真" class="ProfileCardImgFace">
</div>
<div class="profileLine">
<p class="ProfileCardName">福男 太郎<span>engineer</span></p>
<ul class="plofileIcon">
<!--<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/twitter.svg" alt="Twitter"></a>
</li>-->
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/facebook.svg" alt="facebook"></a>
</li>
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/github.svg" alt="GitHub"></a>
</li>
</ul>
</div>
<p class="ProfileCardDesc">山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。山路を登りながら。</p>
<div class="ProfileCardLink"><button class="modalOpen" data-modal="1">詳細を見る</button></div>
</div>
<!-- 中身 -->
</li>
<li>
<!-- 中身 -->
<div class="ProfileCardContent">
<div class="ProfileCardImg"> <img src="https://dubdesign.net/wp-content/uploads/2022/03/designer_bg.jpg" alt="背景の画像" class="ProfileCardImgBg">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/manface.jpg" alt="顔写真" class="ProfileCardImgFace"> </div>
<div class="profileLine">
<p class="ProfileCardName">顔写真 太郎<span>Designer</span></p>
<ul class="plofileIcon">
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/twitter.svg" alt="Twitter"></a>
</li>
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/facebook.svg" alt="facebook"></a>
</li>
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/github.svg" alt="GitHub"></a>
</li>
</ul>
</div>
<p class="ProfileCardDesc">山路を登りながら、こう考えた。智に働けば角が立つ。</p>
<div class="ProfileCardLink"><button class="modalOpen" data-modal="2">詳細を見る</button></div>
</div>
<!-- 中身 -->
</li>
<li>
<!-- 中身 -->
<div class="ProfileCardContent">
<div class="ProfileCardImg"> <img src="https://dubdesign.net/wp-content/uploads/2022/03/engineer_bg.jpg" alt="背景の画像" class="ProfileCardImgBg">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/manface2.jpg" alt="顔写真" class="ProfileCardImgFace"> </div>
<div class="profileLine">
<p class="ProfileCardName">大福 剛士<span>engineer</span></p>
<ul class="plofileIcon">
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/twitter.svg" alt="Twitter"></a>
</li>
<!--<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/facebook.svg" alt="facebook"></a>
</li>-->
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/github.svg" alt="GitHub"></a>
</li>
</ul>
</div>
<p class="ProfileCardDesc">山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。山路を登りながら。</p>
<div class="ProfileCardLink"><button class="modalOpen" data-modal="3">詳細を見る</button></div>
</div>
<!-- 中身 -->
</li>
<li>
<!-- 中身 -->
<div class="ProfileCardContent">
<div class="ProfileCardImg"> <img src="https://dubdesign.net/wp-content/uploads/2022/03/designer_bg.jpg" alt="背景の画像" class="ProfileCardImgBg">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/manface.jpg" alt="顔写真" class="ProfileCardImgFace"> </div>
<div class="profileLine">
<p class="ProfileCardName">駒田 一樹<span>Designer</span></p>
<ul class="plofileIcon">
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/twitter.svg" alt="Twitter"></a>
</li>
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/facebook.svg" alt="facebook"></a>
</li>
<!--<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/github.svg" alt="GitHub"></a>
</li>-->
</ul>
</div>
<p class="ProfileCardDesc">山路を登りながら、こう考えた。智に働けば角が立つ。山路を登りながら、こう考えた。智に働けば角が立つ。山路を登りながら、こう考えた。智に働けば角が立つ。</p>
<div class="ProfileCardLink"><button class="modalOpen" data-modal="4">詳細を見る</button></div>
</div>
<!-- 中身 -->
</li>
</ul>
<!-- モーダル -->
<div class="modal" id="modal">
<div class="modal__overlay modalClose"></div>
<div class="modal__content">
<div class="modal_inner">
<div class="modal__close-btn modalClose" aria-label="閉じる"><span class="lineClose"></span></div>
<!-- スライダー -->
<div class="swiper modalInSlider">
<div class="swiper-wrapper">
<!-- 1 -->
<div class="swiper-slide modalInSlider">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/sliderinimg2.jpg" alt="顔写真">
<div class="modalInSliderText">
<p class="modalInSliderName"><span>engineer</span>福男 太郎</p>
<dl class="spec"> <dt>趣味</dt>
<dd>映画鑑賞・読書</dd> <dt>使用言語</dt>
<dd>Python・Ruby</dd>
</dl>
<p class="modalInSliderDesc">山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。山路を登りながら。</p>
</div>
</div>
<!-- 1 -->
<!-- 2 -->
<div class="swiper-slide modalInSlider">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/sliderinimg.jpg" alt="顔写真">
<div class="modalInSliderText">
<p class="modalInSliderName"><span>Designer</span>顔写真 太郎</p>
<dl class="spec">
<dt>趣味</dt>
<dd>寝ること・マラソン</dd>
<dt>使用言語</dt>
<dd>HTML・CSS・JavaScript</dd>
</dl>
<p class="modalInSliderDesc">山路を登りながら、こう考えた。智に働けば角が立つ。</p>
</div>
</div>
<!-- 2 -->
<!-- 3 -->
<div class="swiper-slide modalInSlider">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/sliderinimg2.jpg" alt="顔写真">
<div class="modalInSliderText">
<p class="modalInSliderName"><span>engineer</span>大福 剛士</p>
<dl class="spec"> <dt>趣味</dt>
<dd>映画鑑賞・読書</dd> <dt>使用言語</dt>
<dd>Python・Ruby</dd>
</dl>
<p class="modalInSliderDesc">山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。山路を登りながら。</p>
</div>
</div>
<!-- 3 -->
<!-- 4 -->
<div class="swiper-slide modalInSlider">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/sliderinimg.jpg" alt="顔写真">
<div class="modalInSliderText">
<p class="modalInSliderName"><span>Designer</span>駒田 一樹</p>
<dl class="spec">
<dt>趣味</dt>
<dd>寝ること・マラソン</dd>
<dt>使用言語</dt>
<dd>HTML・CSS・JavaScript</dd>
</dl>
<p class="modalInSliderDesc">山路を登りながら、こう考えた。智に働けば角が立つ。山路を登りながら、こう考えた。智に働けば角が立つ。山路を登りながら、こう考えた。智に働けば角が立つ。</p>
</div>
</div>
<!-- 4 -->
</div>
</div>
</div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
</div>
<!-- ここまで -->
カードを追加する場合は、コメントアウトしてある「<!-- 中身 -->
」の中と、「<!-- モーダル -->
」をそれぞれ同じ個数追加する必要があるのでご注意ください。
次に、見た目を整える為CSSをコピペします。
コードを表示する
/* ---------------------------- */
/* --- Base --- */
/* ---------------------------- */
ul.swiperModalButton {
display: flex;
flex-wrap: wrap;
gap: 25px;
justify-content: space-around;
border: none;
padding: 0;
list-style: none;
}
ul.swiperModalButton li {
width: 46%;
}
/* card */
.ProfileCardContent {
border-radius: 3px;
background: #fff;
box-shadow: 0 0 3px 0 rgb(0 0 0 / 12%), 0 0px 1px 0 rgb(0 0 0 / 12%);
transition: 0.2s ease-in-out;
display: flex;
flex-direction: column;
height: 100%;
}
.ProfileCardImg {
position: relative;
border-radius: 3px 3px 0 0;
margin-bottom: 20px;
}
img.ProfileCardImgBg {
clip-path: polygon(0 0, 100% 0%, 100% 100%, 0 72%);
border-radius: 3px 3px 0 0;
position: relative;
height: 210px;
width: 100%;
object-fit: cover;
}
img.ProfileCardImgFace {
position: absolute;
bottom: 0px;
left: 30px;
width: 100px;
height: 100px;
border-radius: 9999px;
object-fit: cover;
}
.profileLine {
display: flex;
justify-content: space-between;
padding-left: 30px;
padding-right: 30px;
align-content: flex-start;
}
p.ProfileCardName {
display: block;
letter-spacing: 0.04rem;
font-weight: 500;
font-size: 1.4rem;
margin: 0 0 15px;
width: 50%;
}
p.ProfileCardName span {
line-height: 1;
font-size: 0.8rem;
display: block;
color: #6bb6ff;
}
.profileLine ul.plofileIcon {
list-style: none;
width: 50%;
display: flex;
justify-content: right;
gap: 10px;
padding: 5px 0 0;
margin: 0;
}
.profileLine ul.plofileIcon li {
width: 30px;
}
.profileLine ul.plofileIcon li a {
display: block;
transition: 0.3s ease-in-out;
}
.profileLine ul.plofileIcon li a:hover {
transform: translateY(-2px);
opacity: 0.8;
}
.profileLine ul.plofileIcon li img {
width: 100%;
}
p.ProfileCardDesc {
padding-left: 30px;
padding-right: 30px;
margin-bottom: 15px;
flex-grow: 1;
}
.ProfileCardLink {
display: flex;
justify-content: center;
align-items: center;
padding-bottom: 25px;
}
.ProfileCardLink button {
position: relative;
display: flex;
justify-content: space-around;
align-items: center;
margin: 0 auto;
max-width: 280px;
padding: 8px 25px;
color: #aaa;
transition: 0.3s ease-in-out;
font-weight: 400;
background: #FFF;
border-radius: 2px;
overflow: hidden;
width: 100%;
border: solid 1px #ccc;
}
.ProfileCardLink button:hover {
background: #313131;
color: #FFF;
}
/* モーダル */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
padding: 30px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.3s;
pointer-events: none;
opacity: 0;
z-index: 100;
background-color: rgba(255, 255, 255, 0.9);
}
/* モーダルがactiveの時 */
.modal.is-active {
opacity: 1;
pointer-events: auto;
}
/* モーダル背景のオーバーレイ部分 */
.modal__overlay {
position: absolute;
width: 100%;
height: 100%;
cursor: pointer;
}
/* モーダルのコンテンツ */
.modal__content {
position: relative;
width: 100%;
max-width: 800px;
padding: 20px;
}
.modal_inner {
filter: drop-shadow(0px 0px 4px #ddd);
background: #FFF;
width: 90%;
margin: 0 auto;
border-radius: 2px;
padding: 35px 40px;
display: flex;
justify-content: flex-start;
align-items: center;
}
/* 閉じるボタン */
.modal__close-btn {
position: absolute;
right: 0;
top: -40px;
width: 40px;
height: 40px;
cursor: pointer;
z-index: 20;
}
.modal__close-btn:hover {
opacity: 0.8;
}
/* 閉じるボタンのX */
.lineClose {
display: inline-block;
vertical-align: middle;
color: #313131;
line-height: 1;
width: 2rem;
height: 0.1rem;
background: currentColor;
border-radius: 0.1rem;
position: relative;
transform: rotate(45deg);
}
.lineClose::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: inherit;
border-radius: inherit;
transform: rotate(90deg);
}
p.swiperText {
margin: 0;
text-align: center;
}
/* モーダルの中 */
.modalInSlider {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
}
.swiper-slide img {
width: 40%;
margin-right: 5%;
}
.modalInSliderText {
width: 55%;
}
p.modalInSliderName {
display: block;
letter-spacing: 0.04rem;
font-weight: 500;
font-size: 1.4rem;
margin: 0 0 10px;
}
p.modalInSliderName span {
line-height: 1;
font-size: 0.8rem;
display: block;
color: #6bb6ff;
}
dl.spec {
margin: 0 0 10px;
border-bottom: solid 1px #ccc;
padding-bottom: 10px;
}
dl.spec dt {
float: left;
background: #6bb6ff;
line-height: 1.8;
padding: 0 10px;
font-size: 0.9rem;
color: #FFF;
letter-spacing: 0.04rem;
margin-right: 8px;
font-weight: 600;
}
dl.spec dd {
margin: 0 0 8px;
font-size: 0.9rem;
line-height: 1.8;
color: #313131;
}
p.modalInSliderDesc {
margin: 0;
font-size: 0.95rem;
line-height: 1.6;
}
@media screen and (max-width: 767px) {
/* (ここにモバイル用スタイルを記述) */
ul.swiperModalButton li {
width: 100%;
}
.ProfileCardLink {
padding-left: 30px;
padding-right: 30px;
}
.ProfileCardLink button {
width: 100%;
max-width: none;
}
/* モーダルの中 */
.modal__content {
padding: 0;
}
.modal_inner {
padding: 0;
width: 100%;
}
.modalInSlider {
flex-direction: column;
}
.swiper-slide img {
width: 100%;
margin-bottom: 15px;
height: 200px;
object-fit: cover;
object-position: top;
}
.modalInSliderText {
width: 100%;
padding-left: 15px;
padding-right: 15px;
margin-bottom: 15px;
}
}
コード量が多いですがこれもコピペでOKです。
最後に、モーダルとスライダーで使うSwiperのオプションを使う為、以下のコードを記述します。
コードを表示する
window.addEventListener("DOMContentLoaded", () => {
// モーダルを取得
const modal = document.getElementById("modal");
// モーダルを開く
const openModalBtns = document.querySelectorAll(".modalOpen");
// モーダルを閉じる
const closeModalBtns = document.querySelectorAll(".modalClose");
// Swiperの設定
const swiper = new Swiper(".swiper", {
loop: true, //trueは必須
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
spaceBetween: 30, //任意のマージン
});
// モーダルのボタンクリック
openModalBtns.forEach((openModalBtn) => {
openModalBtn.addEventListener("click", () => {
// data-modalで設定したスライド番号を取得
const modalIndex = openModalBtn.getAttribute('data-modal');
swiper.slideTo(modalIndex);
modal.classList.add("is-active");
});
});
// モーダルの閉じるボタンクリック
closeModalBtns.forEach((closeModalBtn) => {
closeModalBtn.addEventListener("click", () => {
modal.classList.remove("is-active");
});
});
});
上記のコードを <body>〜</body>
のクロージングタグ直前にコピペすればOKです。
HTMLにカードを追加た場合でも、JavaScriptに何か追加するなどはしなくてOKです。
コピペ用コード一式
上述の手順で解説したコードと一緒ですが、コピペしやすいようこっちにもまとめておきます。
コードを表示する
<!-- コンテンツ -->
<ul class="swiperModalButton">
<li>
<!-- 中身 -->
<div class="ProfileCardContent">
<div class="ProfileCardImg"> <img src="https://dubdesign.net/wp-content/uploads/2022/03/engineer_bg.jpg" alt="背景の画像" class="ProfileCardImgBg">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/manface2.jpg" alt="顔写真" class="ProfileCardImgFace">
</div>
<div class="profileLine">
<p class="ProfileCardName">福男 太郎<span>engineer</span></p>
<ul class="plofileIcon">
<!--<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/twitter.svg" alt="Twitter"></a>
</li>-->
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/facebook.svg" alt="facebook"></a>
</li>
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/github.svg" alt="GitHub"></a>
</li>
</ul>
</div>
<p class="ProfileCardDesc">山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。山路を登りながら。</p>
<div class="ProfileCardLink"><button class="modalOpen" data-modal="1">詳細を見る</button></div>
</div>
<!-- 中身 -->
</li>
<li>
<!-- 中身 -->
<div class="ProfileCardContent">
<div class="ProfileCardImg"> <img src="https://dubdesign.net/wp-content/uploads/2022/03/designer_bg.jpg" alt="背景の画像" class="ProfileCardImgBg">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/manface.jpg" alt="顔写真" class="ProfileCardImgFace"> </div>
<div class="profileLine">
<p class="ProfileCardName">顔写真 太郎<span>Designer</span></p>
<ul class="plofileIcon">
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/twitter.svg" alt="Twitter"></a>
</li>
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/facebook.svg" alt="facebook"></a>
</li>
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/github.svg" alt="GitHub"></a>
</li>
</ul>
</div>
<p class="ProfileCardDesc">山路を登りながら、こう考えた。智に働けば角が立つ。</p>
<div class="ProfileCardLink"><button class="modalOpen" data-modal="2">詳細を見る</button></div>
</div>
<!-- 中身 -->
</li>
<li>
<!-- 中身 -->
<div class="ProfileCardContent">
<div class="ProfileCardImg"> <img src="https://dubdesign.net/wp-content/uploads/2022/03/engineer_bg.jpg" alt="背景の画像" class="ProfileCardImgBg">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/manface2.jpg" alt="顔写真" class="ProfileCardImgFace"> </div>
<div class="profileLine">
<p class="ProfileCardName">大福 剛士<span>engineer</span></p>
<ul class="plofileIcon">
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/twitter.svg" alt="Twitter"></a>
</li>
<!--<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/facebook.svg" alt="facebook"></a>
</li>-->
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/github.svg" alt="GitHub"></a>
</li>
</ul>
</div>
<p class="ProfileCardDesc">山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。山路を登りながら。</p>
<div class="ProfileCardLink"><button class="modalOpen" data-modal="3">詳細を見る</button></div>
</div>
<!-- 中身 -->
</li>
<li>
<!-- 中身 -->
<div class="ProfileCardContent">
<div class="ProfileCardImg"> <img src="https://dubdesign.net/wp-content/uploads/2022/03/designer_bg.jpg" alt="背景の画像" class="ProfileCardImgBg">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/manface.jpg" alt="顔写真" class="ProfileCardImgFace"> </div>
<div class="profileLine">
<p class="ProfileCardName">駒田 一樹<span>Designer</span></p>
<ul class="plofileIcon">
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/twitter.svg" alt="Twitter"></a>
</li>
<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/facebook.svg" alt="facebook"></a>
</li>
<!--<li>
<a href="#"><img src="https://dubdesign.net/wp-content/uploads/2022/03/github.svg" alt="GitHub"></a>
</li>-->
</ul>
</div>
<p class="ProfileCardDesc">山路を登りながら、こう考えた。智に働けば角が立つ。山路を登りながら、こう考えた。智に働けば角が立つ。山路を登りながら、こう考えた。智に働けば角が立つ。</p>
<div class="ProfileCardLink"><button class="modalOpen" data-modal="4">詳細を見る</button></div>
</div>
<!-- 中身 -->
</li>
</ul>
<!-- モーダル -->
<div class="modal" id="modal">
<div class="modal__overlay modalClose"></div>
<div class="modal__content">
<div class="modal_inner">
<div class="modal__close-btn modalClose" aria-label="閉じる"><span class="lineClose"></span></div>
<!-- スライダー -->
<div class="swiper modalInSlider">
<div class="swiper-wrapper">
<!-- 1 -->
<div class="swiper-slide modalInSlider">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/sliderinimg2.jpg" alt="顔写真">
<div class="modalInSliderText">
<p class="modalInSliderName"><span>engineer</span>福男 太郎</p>
<dl class="spec"> <dt>趣味</dt>
<dd>映画鑑賞・読書</dd> <dt>使用言語</dt>
<dd>Python・Ruby</dd>
</dl>
<p class="modalInSliderDesc">山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。山路を登りながら。</p>
</div>
</div>
<!-- 1 -->
<!-- 2 -->
<div class="swiper-slide modalInSlider">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/sliderinimg.jpg" alt="顔写真">
<div class="modalInSliderText">
<p class="modalInSliderName"><span>Designer</span>顔写真 太郎</p>
<dl class="spec">
<dt>趣味</dt>
<dd>寝ること・マラソン</dd>
<dt>使用言語</dt>
<dd>HTML・CSS・JavaScript</dd>
</dl>
<p class="modalInSliderDesc">山路を登りながら、こう考えた。智に働けば角が立つ。</p>
</div>
</div>
<!-- 2 -->
<!-- 3 -->
<div class="swiper-slide modalInSlider">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/sliderinimg2.jpg" alt="顔写真">
<div class="modalInSliderText">
<p class="modalInSliderName"><span>engineer</span>大福 剛士</p>
<dl class="spec"> <dt>趣味</dt>
<dd>映画鑑賞・読書</dd> <dt>使用言語</dt>
<dd>Python・Ruby</dd>
</dl>
<p class="modalInSliderDesc">山路を登りながら、こう考えた。智に働けば角が立つ。情に棹させば流される。山路を登りながら。</p>
</div>
</div>
<!-- 3 -->
<!-- 4 -->
<div class="swiper-slide modalInSlider">
<img src="https://dubdesign.net/wp-content/uploads/2022/03/sliderinimg.jpg" alt="顔写真">
<div class="modalInSliderText">
<p class="modalInSliderName"><span>Designer</span>駒田 一樹</p>
<dl class="spec">
<dt>趣味</dt>
<dd>寝ること・マラソン</dd>
<dt>使用言語</dt>
<dd>HTML・CSS・JavaScript</dd>
</dl>
<p class="modalInSliderDesc">山路を登りながら、こう考えた。智に働けば角が立つ。山路を登りながら、こう考えた。智に働けば角が立つ。山路を登りながら、こう考えた。智に働けば角が立つ。</p>
</div>
</div>
<!-- 4 -->
</div>
</div>
</div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
</div>
<!-- ここまで -->
/* ---------------------------- */
/* --- Base --- */
/* ---------------------------- */
ul.swiperModalButton {
display: flex;
flex-wrap: wrap;
gap: 25px;
justify-content: space-around;
border: none;
padding: 0;
list-style: none;
}
ul.swiperModalButton li {
width: 46%;
}
/* card */
.ProfileCardContent {
border-radius: 3px;
background: #fff;
box-shadow: 0 0 3px 0 rgb(0 0 0 / 12%), 0 0px 1px 0 rgb(0 0 0 / 12%);
transition: 0.2s ease-in-out;
display: flex;
flex-direction: column;
height: 100%;
}
.ProfileCardImg {
position: relative;
border-radius: 3px 3px 0 0;
margin-bottom: 20px;
}
img.ProfileCardImgBg {
clip-path: polygon(0 0, 100% 0%, 100% 100%, 0 72%);
border-radius: 3px 3px 0 0;
position: relative;
height: 210px;
width: 100%;
object-fit: cover;
}
img.ProfileCardImgFace {
position: absolute;
bottom: 0px;
left: 30px;
width: 100px;
height: 100px;
border-radius: 9999px;
object-fit: cover;
}
.profileLine {
display: flex;
justify-content: space-between;
padding-left: 30px;
padding-right: 30px;
align-content: flex-start;
}
p.ProfileCardName {
display: block;
letter-spacing: 0.04rem;
font-weight: 500;
font-size: 1.4rem;
margin: 0 0 15px;
width: 50%;
}
p.ProfileCardName span {
line-height: 1;
font-size: 0.8rem;
display: block;
color: #6bb6ff;
}
.profileLine ul.plofileIcon {
list-style: none;
width: 50%;
display: flex;
justify-content: right;
gap: 10px;
padding: 5px 0 0;
margin: 0;
}
.profileLine ul.plofileIcon li {
width: 30px;
}
.profileLine ul.plofileIcon li a {
display: block;
transition: 0.3s ease-in-out;
}
.profileLine ul.plofileIcon li a:hover {
transform: translateY(-2px);
opacity: 0.8;
}
.profileLine ul.plofileIcon li img {
width: 100%;
}
p.ProfileCardDesc {
padding-left: 30px;
padding-right: 30px;
margin-bottom: 15px;
flex-grow: 1;
}
.ProfileCardLink {
display: flex;
justify-content: center;
align-items: center;
padding-bottom: 25px;
}
.ProfileCardLink button {
position: relative;
display: flex;
justify-content: space-around;
align-items: center;
margin: 0 auto;
max-width: 280px;
padding: 8px 25px;
color: #aaa;
transition: 0.3s ease-in-out;
font-weight: 400;
background: #FFF;
border-radius: 2px;
overflow: hidden;
width: 100%;
border: solid 1px #ccc;
}
.ProfileCardLink button:hover {
background: #313131;
color: #FFF;
}
/* モーダル */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100vh;
padding: 30px;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.3s;
pointer-events: none;
opacity: 0;
z-index: 100;
background-color: rgba(255, 255, 255, 0.9);
}
/* モーダルがactiveの時 */
.modal.is-active {
opacity: 1;
pointer-events: auto;
}
/* モーダル背景のオーバーレイ部分 */
.modal__overlay {
position: absolute;
width: 100%;
height: 100%;
cursor: pointer;
}
/* モーダルのコンテンツ */
.modal__content {
position: relative;
width: 100%;
max-width: 800px;
padding: 20px;
}
.modal_inner {
filter: drop-shadow(0px 0px 4px #ddd);
background: #FFF;
width: 90%;
margin: 0 auto;
border-radius: 2px;
padding: 35px 40px;
display: flex;
justify-content: flex-start;
align-items: center;
}
/* 閉じるボタン */
.modal__close-btn {
position: absolute;
right: 0;
top: -40px;
width: 40px;
height: 40px;
cursor: pointer;
z-index: 20;
}
.modal__close-btn:hover {
opacity: 0.8;
}
/* 閉じるボタンのX */
.lineClose {
display: inline-block;
vertical-align: middle;
color: #313131;
line-height: 1;
width: 2rem;
height: 0.1rem;
background: currentColor;
border-radius: 0.1rem;
position: relative;
transform: rotate(45deg);
}
.lineClose::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: inherit;
border-radius: inherit;
transform: rotate(90deg);
}
p.swiperText {
margin: 0;
text-align: center;
}
/* モーダルの中 */
.modalInSlider {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
align-items: center;
}
.swiper-slide img {
width: 40%;
margin-right: 5%;
}
.modalInSliderText {
width: 55%;
}
p.modalInSliderName {
display: block;
letter-spacing: 0.04rem;
font-weight: 500;
font-size: 1.4rem;
margin: 0 0 10px;
}
p.modalInSliderName span {
line-height: 1;
font-size: 0.8rem;
display: block;
color: #6bb6ff;
}
dl.spec {
margin: 0 0 10px;
border-bottom: solid 1px #ccc;
padding-bottom: 10px;
}
dl.spec dt {
float: left;
background: #6bb6ff;
line-height: 1.8;
padding: 0 10px;
font-size: 0.9rem;
color: #FFF;
letter-spacing: 0.04rem;
margin-right: 8px;
font-weight: 600;
}
dl.spec dd {
margin: 0 0 8px;
font-size: 0.9rem;
line-height: 1.8;
color: #313131;
}
p.modalInSliderDesc {
margin: 0;
font-size: 0.95rem;
line-height: 1.6;
}
@media screen and (max-width: 767px) {
/* (ここにモバイル用スタイルを記述) */
ul.swiperModalButton li {
width: 100%;
}
.ProfileCardLink {
padding-left: 30px;
padding-right: 30px;
}
.ProfileCardLink button {
width: 100%;
max-width: none;
}
/* モーダルの中 */
.modal__content {
padding: 0;
}
.modal_inner {
padding: 0;
width: 100%;
}
.modalInSlider {
flex-direction: column;
}
.swiper-slide img {
width: 100%;
margin-bottom: 15px;
height: 200px;
object-fit: cover;
object-position: top;
}
.modalInSliderText {
width: 100%;
padding-left: 15px;
padding-right: 15px;
margin-bottom: 15px;
}
}
window.addEventListener("DOMContentLoaded", () => {
// モーダルを取得
const modal = document.getElementById("modal");
// モーダルを開く
const openModalBtns = document.querySelectorAll(".modalOpen");
// モーダルを閉じる
const closeModalBtns = document.querySelectorAll(".modalClose");
// Swiperの設定
const swiper = new Swiper(".swiper", {
loop: true, //trueは必須
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
spaceBetween: 30, //任意のマージン
});
// モーダルのボタンクリック
openModalBtns.forEach((openModalBtn) => {
openModalBtn.addEventListener("click", () => {
// data-modalで設定したスライド番号を取得
const modalIndex = openModalBtn.getAttribute('data-modal');
swiper.slideTo(modalIndex);
modal.classList.add("is-active");
});
});
// モーダルの閉じるボタンクリック
closeModalBtns.forEach((closeModalBtn) => {
closeModalBtn.addEventListener("click", () => {
modal.classList.remove("is-active");
});
});
});
ざっくりとしたコードの解説
ざっくりながら、このスニペットの各コードについて解説します。カスタマイズする時などにご覧ください。
CSS:clip-pathで画像のくり抜き
画像のくり抜きは、clip-path
のプロパティを使っていろんな形を作ることができます。
この記事のスニペットでは、clip-path: polygon(0 0, 100% 0%, 100% 100%, 0 72%);
と記述して、四角い写真の形を変形させています。
このプロパティを使えば、六角形や矢印などいろんな図形でマスクを作ることができるので、詳しくは以下の記事にあるのでチェックしてみてください。
CSSで画像にくり抜きのマスクをかける便利なスニペット3選CSS:flex-growで子要素の高さを揃えて定位置に配置
カードの下部にある「詳細を見るボタン」は、flex-grow:1;
を使い、横並びのカードの高さが違っていても全て最下部の定位置に配置しています。
実際に「flex-grow」を使わない場合・使う場合をそれぞれ見てみましょう。
まずは、flex-grow を使わない場合のレイアウトです。
上記のように横並びでカードを配置した場合、その子要素の位置や高さが揃わなくなってしまいます。
次に「flex-grow」を使った場合です。
flex-grow
を使うには、まず親要素に height:100%;
の記述をする必要があります。この記事のスニペットでは、「ProfileCardContent」のclass名がついている div
タグへ以下のように記述します。
.ProfileCardContent {
height: 100%;
}
次に、カード内で高さが揃わない要素。ここでは、カード内で「山路を登りながら、こう考えた。〜〜」の文章が入る「ProfileCardDesc」のclass名がついている p タグに以下を記述すればOKで、高さが揃うようになります。
p.ProfileCardDesc {
flex-grow: 1;
}
これだけで高さが揃うので、Flexboxを使う場合はぜひ覚えておきましょう。
さいごに
いかがでしたでしょうか?
個人で運営しているブログの場合、このようなプロフィールカードを一覧で並べることはないと思いますが、サイトで複数人の紹介をしたい場合にはぜひこの記事を参考にしてみてください。