CSSの scroll-snap を用いた、モダンで滑らかなカルーセル型スライダーです。レスポンシブに対応し、ナビゲーションドットとボタンで直感的な操作が可能です。
当サービスをご利用いただいた皆様の体験談をご紹介します。
<div class="testimonial-section">
<div class="testimonial-header">
<h2>お客様の声</h2>
<p>当サービスをご利用いただいた皆様の体験談をご紹介します。</p>
</div>
<div class="slider-wrapper">
<button class="slider-btn prev" aria-label="前のレビューへ">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M15 18l-6-6 6-6"/></svg>
</button>
<div class="testimonial-slider" id="testimonial-slider">
{/* Testimonial 1 */}
<div class="testimonial-card">
<div class="quote-icon">"</div>
<p class="review">「このプロダクトのおかげで、私たちのワークフローは完全に生まれ変わりました。直感的なインターフェースと素晴らしいサポートチームのおかげで、日々成長するビジネスに最適です。」</p>
<div class="author-info">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Sarah" alt="Sarah Jenkins" class="avatar" />
<div>
<h4 class="author-name">佐藤 彩</h4>
<span class="author-role">プロダクトマネージャー</span>
</div>
</div>
</div>
{/* Testimonial 2 */}
<div class="testimonial-card">
<div class="quote-icon">"</div>
<p class="review">「多くの類似ツールを試してきましたが、これこそが際立っています。デザインの細部へのこだわりとスムーズな操作性により、毎日の作業が楽しくなりました。」</p>
<div class="author-info">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Michael" alt="Michael Chen" class="avatar" />
<div>
<h4 class="author-name">田中 健一</h4>
<span class="author-role">UX デザイナー</span>
</div>
</div>
</div>
{/* Testimonial 3 */}
<div class="testimonial-card">
<div class="quote-icon">"</div>
<p class="review">「既存システムとのシームレスな連携により、数週間分の開発期間を短縮できました。非常に堅牢で、私たちの規模拡大にも難なく対応してくれます。」</p>
<div class="author-info">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=Elena" alt="Elena Rodriguez" class="avatar" />
<div>
<h4 class="author-name">鈴木 エレナ</h4>
<span class="author-role">CTO</span>
</div>
</div>
</div>
{/* Testimonial 4 */}
<div class="testimonial-card">
<div class="quote-icon">"</div>
<p class="review">「カスタマーサービスが並外れています。質問があるときはいつでも、役に立つ解決策を即座に返信してくれます。私たちの成功の真のパートナーです。」</p>
<div class="author-info">
<img src="https://api.dicebear.com/7.x/avataaars/svg?seed=David" alt="David Miller" class="avatar" />
<div>
<h4 class="author-name">山田 大輔</h4>
<span class="author-role">オペレーションディレクター</span>
</div>
</div>
</div>
</div>
<button class="slider-btn next" aria-label="次のレビューへ">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18l6-6-6-6"/></svg>
</button>
</div>
<div class="slider-dots" id="slider-dots"></div>
</div>
.testimonial-section {
max-width: 1000px;
margin: 0 auto;
padding: 4rem 2rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
color: #1a1a2e;
}
.testimonial-header {
text-align: center;
margin-bottom: 3rem;
}
.testimonial-header h2 {
font-size: 2.5rem;
font-weight: 800;
margin-bottom: 0.5rem;
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.testimonial-header p {
color: #64748b;
font-size: 1.1rem;
}
.slider-wrapper {
position: relative;
display: flex;
align-items: center;
gap: 1rem;
}
.testimonial-slider {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
gap: 2rem;
padding: 1rem 0;
/* スクロールバーを非表示 */
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE/Edge */
}
.testimonial-slider::-webkit-scrollbar {
display: none; /* Chrome/Safari */
}
.testimonial-card {
flex: 0 0 100%;
scroll-snap-align: center;
background: #ffffff;
border-radius: 24px;
padding: 2.5rem;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.08);
border: 1px solid rgba(255, 255, 255, 0.8);
display: flex;
flex-direction: column;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
@media (min-width: 768px) {
.testimonial-card {
flex: 0 0 calc(50% - 1rem); /* 2カラム表示 */
}
}
.testimonial-card:hover {
transform: translateY(-5px);
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.12);
}
.quote-icon {
font-size: 4rem;
line-height: 1;
color: #e2e8f0;
margin-bottom: -1rem;
font-family: serif;
}
.review {
font-size: 1.125rem;
line-height: 1.7;
color: #334155;
flex-grow: 1;
margin-bottom: 2rem;
font-style: italic;
}
.author-info {
display: flex;
align-items: center;
gap: 1rem;
border-top: 1px solid #f1f5f9;
padding-top: 1.5rem;
}
.avatar {
width: 56px;
height: 56px;
border-radius: 50%;
object-fit: cover;
border: 2px solid #e2e8f0;
}
.author-name {
margin: 0 0 0.25rem 0;
font-weight: 700;
font-size: 1.1rem;
}
.author-role {
color: #64748b;
font-size: 0.9rem;
}
.slider-btn {
background: #ffffff;
border: none;
border-radius: 50%;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
color: #4facfe;
transition: all 0.2s ease;
flex-shrink: 0;
z-index: 10;
}
.slider-btn:hover {
background: #4facfe;
color: #ffffff;
transform: scale(1.05);
}
.slider-btn:active {
transform: scale(0.95);
}
.slider-dots {
display: flex;
justify-content: center;
gap: 0.75rem;
margin-top: 2rem;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #cbd5e1;
border: none;
cursor: pointer;
padding: 0;
transition: all 0.3s ease;
}
.dot.active {
background: #4facfe;
width: 24px;
border-radius: 12px;
}
/* ダークモード対応 */
@media (prefers-color-scheme: dark) {
.testimonial-section {
color: #f8fafc;
}
.testimonial-header p {
color: #94a3b8;
}
.testimonial-card {
background: #1e293b;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
border-color: rgba(255, 255, 255, 0.05);
}
.quote-icon {
color: #334155;
}
.review {
color: #cbd5e1;
}
.author-info {
border-top-color: #334155;
}
.slider-btn {
background: #1e293b;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.slider-btn:hover {
background: #4facfe;
}
.dot {
background: #475569;
}
}
document.addEventListener('DOMContentLoaded', () => {
const slider = document.getElementById('testimonial-slider');
const dotsContainer = document.getElementById('slider-dots');
const prevBtn = document.querySelector('.slider-btn.prev');
const nextBtn = document.querySelector('.slider-btn.next');
if (!slider) return;
const cards = slider.querySelectorAll('.testimonial-card');
const cardCount = cards.length;
// 画面幅に応じて表示されているカード数を取得
const getVisibleCards = () => window.innerWidth >= 768 ? 2 : 1;
// ドットの生成と更新
const updateDots = () => {
dotsContainer.innerHTML = '';
const visibleCards = getVisibleCards();
const dotCount = Math.max(1, cardCount - visibleCards + 1);
for (let i = 0; i < dotCount; i++) {
const dot = document.createElement('button');
dot.classList.add('dot');
if (i === 0) dot.classList.add('active');
dot.setAttribute('aria-label', `スライド ${i + 1} へ`);
dot.addEventListener('click', () => scrollToSlide(i));
dotsContainer.appendChild(dot);
}
};
updateDots();
// リサイズ時に再計算
window.addEventListener('resize', () => {
updateDots();
updateActiveDot();
});
// 指定のスライドへスクロール
const scrollToSlide = (index) => {
const cardWidth = cards[0].offsetWidth;
const gap = parseFloat(window.getComputedStyle(slider).gap) || 0;
slider.scrollTo({
left: index * (cardWidth + gap),
behavior: 'smooth'
});
};
// スクロール位置に応じてアクティブなドットを更新
const updateActiveDot = () => {
const dots = dotsContainer.querySelectorAll('.dot');
if (!dots.length) return;
const cardWidth = cards[0].offsetWidth;
const gap = parseFloat(window.getComputedStyle(slider).gap) || 0;
const scrollPosition = slider.scrollLeft;
// 現在のインデックスを計算
const activeIndex = Math.round(scrollPosition / (cardWidth + gap));
dots.forEach((dot, index) => {
dot.classList.toggle('active', index === activeIndex);
});
};
// スクロールイベントに連動
slider.addEventListener('scroll', updateActiveDot);
// ボタンによるスクロール操作
prevBtn.addEventListener('click', () => {
const cardWidth = cards[0].offsetWidth;
const gap = parseFloat(window.getComputedStyle(slider).gap) || 0;
slider.scrollBy({ left: -(cardWidth + gap), behavior: 'smooth' });
});
nextBtn.addEventListener('click', () => {
const cardWidth = cards[0].offsetWidth;
const gap = parseFloat(window.getComputedStyle(slider).gap) || 0;
slider.scrollBy({ left: (cardWidth + gap), behavior: 'smooth' });
});
});