메인페이지 스타일 1차 적용

This commit is contained in:
현성필
2025-11-15 13:59:30 +09:00
parent 8b84033d63
commit aae7a5ac89
12 changed files with 966 additions and 551 deletions
@@ -5,41 +5,96 @@
<body>
<th:block layout:fragment="contentFragment">
<section class="hero-section">
<div class="hero-patterns">
<div class="pattern-circle pattern-1"></div>
<div class="pattern-circle pattern-2"></div>
<div class="pattern-dots"></div>
</div>
<div class="container">
<div class="hero-content">
<div class="hero-badge">
<span class="badge-icon">🚀</span>
<span>Wa bank 생태계에 오신 것을 환영합니다!</span>
<!-- Hero Carousel Section -->
<section class="hero-carousel-section">
<!-- Carousel Container -->
<div class="hero-carousel-container">
<div class="hero-carousel-track">
<!-- Slide 1 -->
<div class="hero-slide active" data-slide="0">
<div class="hero-slide-content">
<div class="hero-text-content">
<div class="hero-text">
<p class="hero-subtitle">세상의 모든 서비스</p>
<p class="hero-title">광주은행 API가 함께 합니다.</p>
</div>
<a th:href="@{/signup}" class="btn-hero-signup">회원가입하기</a>
</div>
<div class="hero-image-content">
<img th:src="@{/img/hero_image_1.png}" alt="광주은행 API 서비스" class="hero-image">
</div>
</div>
</div>
<h1 class="main-headline">
<span class="headline-wa">Wa한</span> 금융 혁신,<br>
여러분의 아이디어로 시작됩니다
</h1>
<h2 class="sub-headline">
광주은행 오픈 API로 새로운 금융 서비스를 만들어보세요.<br>
안전하고 빠르게, 그리고 쉽게 연동할 수 있습니다.
</h2>
<div class="hero-buttons">
<a href="/apis" class="btn btn-primary">
<i class="fas fa-code"></i> API 둘러보기
</a>
<a href="#get-started" class="btn btn-secondary">
<i class="fas fa-play"></i> 5분 만에 시작하기
</a>
<!-- Slide 2 -->
<div class="hero-slide" data-slide="1">
<div class="hero-slide-content">
<div class="hero-text-content">
<div class="hero-text">
<p class="hero-subtitle">세상의 모든 서비스</p>
<p class="hero-title">광주은행 API가 함께 합니다.</p>
</div>
<a th:href="@{/signup}" class="btn-hero-signup">회원가입하기</a>
</div>
<div class="hero-image-content">
<img th:src="@{/img/hero_image_2.png}" alt="광주은행 API 서비스" class="hero-image">
</div>
</div>
</div>
<!-- Slide 3 -->
<div class="hero-slide" data-slide="2">
<div class="hero-slide-content">
<div class="hero-text-content">
<div class="hero-text">
<p class="hero-subtitle">세상의 모든 서비스</p>
<p class="hero-title">광주은행 API가 함께 합니다.</p>
</div>
<a th:href="@{/signup}" class="btn-hero-signup">회원가입하기</a>
</div>
<div class="hero-image-content">
<img th:src="@{/img/hero_image_3.png}" alt="광주은행 API 서비스" class="hero-image">
</div>
</div>
</div>
</div>
<!-- Navigation Buttons -->
<button class="hero-nav-btn hero-nav-prev" id="heroPrevBtn" aria-label="이전 슬라이드">
<svg width="18" height="32" viewBox="0 0 18 32" fill="none" xmlns="http://www.w3.org/2000/svg" style="transform: scaleX(-1);">
<path d="M2.17021 31.1127L-0.000440909 28.942L13.3853 15.5564L-0.000438653 2.17065L2.17021 4.76837e-07L17.7266 15.5564L2.17021 31.1127Z" fill="#0049B4"/>
</svg>
</button>
<button class="hero-nav-btn hero-nav-next" id="heroNextBtn" aria-label="다음 슬라이드">
<svg width="18" height="32" viewBox="0 0 18 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.17021 31.1127L-0.000440909 28.942L13.3853 15.5564L-0.000438653 2.17065L2.17021 4.76837e-07L17.7266 15.5564L2.17021 31.1127Z" fill="#0049B4"/>
</svg>
</button>
<!-- Carousel Indicators -->
<div class="hero-carousel-indicators">
<button class="hero-indicator active" data-slide-to="0" aria-label="슬라이드 1"></button>
<button class="hero-indicator" data-slide-to="1" aria-label="슬라이드 2"></button>
<button class="hero-indicator" data-slide-to="2" aria-label="슬라이드 3"></button>
<!-- Auto Play Toggle Button -->
<button class="hero-autoplay-toggle" id="heroAutoplayToggle" aria-label="자동 재생 일시정지">
<!-- Pause Icon (default) -->
<svg class="pause-icon active" width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 14V0H12V14H8ZM0 14V0H4V14H0Z" fill="#1D1B20"/>
</svg>
<!-- Play Icon -->
<svg class="play-icon" width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0V14L12 7L0 0Z" fill="#0049B4"/>
</svg>
</button>
</div>
</div>
</section>
<!-- API 검색 섹션 -->
<section class="api-search-section">
<div class="curved-background"></div>
<div class="search-content-wrapper">
<div class="search-character">
<img th:src="@{/img/img_search_character.png}" alt="검색">
@@ -75,7 +130,7 @@
</div>
</section>
<!-- 추천 API 섹션 - Figma Design -->
<!-- 추천 API 섹션 -->
<section class="api-showcase">
<div class="showcase-wrapper">
<div class="section-header">
@@ -137,7 +192,7 @@
</div>
</section>
<!-- 정보 섹션 - Figma Design -->
<!-- 정보 섹션 -->
<section class="info-section">
<div class="info-background"></div>
<!-- <div class="info-point-image"></div>-->
@@ -170,7 +225,7 @@
</div>
</section>
<!-- 지원 센터 섹션 - Figma Design -->
<!-- 지원 센터 섹션 -->
<section class="support-center">
<div class="support-background-pattern"></div>
<div class="container">
@@ -215,7 +270,7 @@
</div>
</section>
<!-- API 활용 현황 섹션 - Figma Design -->
<!-- API 활용 현황 섹션 -->
<section class="api-stats-section">
<div class="stats-background"></div>
<div class="container">
@@ -281,7 +336,7 @@
</div>
</section>
<!-- 지금 바로 회원가입 CTA 섹션 - Figma Design -->
<!-- 지금 바로 회원가입 CTA 섹션 -->
<section class="signup-cta-section">
<div class="cta-background"></div>
<div class="container">
@@ -296,8 +351,208 @@
</body>
<th:block layout:fragment="contentScript">
<script>
// 숫자 카운트업 애니메이션
document.addEventListener('DOMContentLoaded', function() {
// ========================================
// Hero Carousel
// ========================================
const heroCarousel = {
currentSlide: 0,
totalSlides: 3,
autoPlayInterval: null,
autoPlayDelay: 5000,
isPlaying: true,
init: function() {
this.slides = document.querySelectorAll('.hero-slide');
this.indicators = document.querySelectorAll('.hero-indicator');
this.prevBtn = document.getElementById('heroPrevBtn');
this.nextBtn = document.getElementById('heroNextBtn');
this.autoplayToggle = document.getElementById('heroAutoplayToggle');
this.pauseIcon = this.autoplayToggle?.querySelector('.pause-icon');
this.playIcon = this.autoplayToggle?.querySelector('.play-icon');
if (!this.slides.length) return;
this.bindEvents();
this.startAutoPlay();
},
bindEvents: function() {
// Previous button
if (this.prevBtn) {
this.prevBtn.addEventListener('click', () => {
this.goToSlide(this.currentSlide - 1);
this.resetAutoPlay();
});
}
// Next button
if (this.nextBtn) {
this.nextBtn.addEventListener('click', () => {
this.goToSlide(this.currentSlide + 1);
this.resetAutoPlay();
});
}
// Indicators
this.indicators.forEach((indicator, index) => {
indicator.addEventListener('click', () => {
this.goToSlide(index);
this.resetAutoPlay();
});
});
// Auto play toggle button
if (this.autoplayToggle) {
this.autoplayToggle.addEventListener('click', () => {
this.toggleAutoPlay();
});
}
// Pause on hover (only if playing)
const container = document.querySelector('.hero-carousel-container');
if (container) {
container.addEventListener('mouseenter', () => {
if (this.isPlaying) {
this.pauseAutoPlay();
}
});
container.addEventListener('mouseleave', () => {
if (this.isPlaying) {
this.startAutoPlay();
}
});
}
// Touch events for mobile
let touchStartX = 0;
let touchEndX = 0;
if (container) {
container.addEventListener('touchstart', (e) => {
touchStartX = e.changedTouches[0].screenX;
});
container.addEventListener('touchend', (e) => {
touchEndX = e.changedTouches[0].screenX;
this.handleSwipe(touchStartX, touchEndX);
});
}
},
goToSlide: function(index) {
// Wrap around
if (index < 0) {
index = this.totalSlides - 1;
} else if (index >= this.totalSlides) {
index = 0;
}
// Update slides
this.slides.forEach((slide, i) => {
slide.classList.toggle('active', i === index);
});
// Update indicators
this.indicators.forEach((indicator, i) => {
indicator.classList.toggle('active', i === index);
});
this.currentSlide = index;
},
handleSwipe: function(startX, endX) {
const minSwipeDistance = 50;
const distance = startX - endX;
if (Math.abs(distance) > minSwipeDistance) {
if (distance > 0) {
// Swipe left - next slide
this.goToSlide(this.currentSlide + 1);
} else {
// Swipe right - previous slide
this.goToSlide(this.currentSlide - 1);
}
this.resetAutoPlay();
}
},
startAutoPlay: function() {
this.pauseAutoPlay();
this.autoPlayInterval = setInterval(() => {
this.goToSlide(this.currentSlide + 1);
}, this.autoPlayDelay);
},
pauseAutoPlay: function() {
if (this.autoPlayInterval) {
clearInterval(this.autoPlayInterval);
this.autoPlayInterval = null;
}
},
resetAutoPlay: function() {
if (this.isPlaying) {
this.pauseAutoPlay();
this.startAutoPlay();
}
},
toggleAutoPlay: function() {
this.isPlaying = !this.isPlaying;
if (this.isPlaying) {
// Start auto play
this.startAutoPlay();
// Update icons
if (this.pauseIcon) this.pauseIcon.classList.add('active');
if (this.playIcon) this.playIcon.classList.remove('active');
// Update aria-label
if (this.autoplayToggle) {
this.autoplayToggle.setAttribute('aria-label', '자동 재생 일시정지');
}
} else {
// Stop auto play
this.pauseAutoPlay();
// Update icons
if (this.pauseIcon) this.pauseIcon.classList.remove('active');
if (this.playIcon) this.playIcon.classList.add('active');
// Update aria-label
if (this.autoplayToggle) {
this.autoplayToggle.setAttribute('aria-label', '자동 재생 시작');
}
}
}
};
// Initialize hero carousel
heroCarousel.init();
// ========================================
// API 검색 기능
// ========================================
const searchInput = document.getElementById('apiSearchInput');
const searchForm = document.querySelector('.search-form');
const hashtags = document.querySelectorAll('.hashtag-link');
// 해시태그 클릭 이벤트
hashtags.forEach(tag => {
tag.addEventListener('click', function(e) {
e.preventDefault();
const searchTerm = this.getAttribute('data-search');
if (searchInput) {
searchInput.value = searchTerm;
}
// 폼 제출
if (searchForm) {
searchForm.submit();
}
});
});
// ========================================
// 숫자 카운트업 애니메이션
// ========================================
const observerOptions = {
threshold: 0.5,
rootMargin: '0px 0px -100px 0px'
@@ -338,24 +593,6 @@
if (statsSection) {
observer.observe(statsSection);
}
// API 검색 기능
const searchInput = document.getElementById('apiSearchInput');
const searchForm = document.querySelector('.search-form');
const hashtags = document.querySelectorAll('.hashtag');
// 해시태그 클릭 이벤트
hashtags.forEach(tag => {
tag.addEventListener('click', function(e) {
e.preventDefault();
const searchTerm = this.getAttribute('data-search');
searchInput.value = searchTerm;
// 폼 제출
if (searchForm) {
searchForm.submit();
}
});
});
});
</script>
</th:block>