FAQ 디자인 적용

This commit is contained in:
현성필
2025-12-04 15:30:21 +09:00
parent 4ad34c9dfc
commit dacae095c5
2 changed files with 115 additions and 171 deletions
@@ -1,158 +1,106 @@
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Accordion Component - FAQ and Collapsible Sections // Accordion Component - FAQ and Collapsible Sections
// Figma Design: node 984-2146
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// FAQ Accordion // FAQ Accordion - Figma Design
.faq-accordion { .faq-accordion {
background: $white; background: #F6F9FB;
border-radius: $border-radius-lg; border-radius: $border-radius-lg;
box-shadow: $shadow-sm;
overflow: hidden; overflow: hidden;
margin-bottom: $spacing-2xl;
.faq-item { .faq-item {
border-bottom: 1px solid $border-gray; border-bottom: 1px solid #DADADA;
transition: $transition-base; transition: $transition-base;
&:last-child { &:last-child {
border-bottom: none; border-bottom: none;
} }
// Active state - when accordion is open
&.active { &.active {
background: rgba($primary-blue, 0.02);
.faq-question { .faq-question {
color: $primary-blue;
.faq-icon { .faq-icon {
transform: rotate(180deg); transform: rotate(180deg);
color: $primary-blue;
} }
} }
.faq-answer {
display: block;
animation: slideDown 0.3s ease;
}
} }
} }
// Question row - Figma: 18px bold, #212529
.faq-question { .faq-question {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: $spacing-lg $spacing-xl; padding: $spacing-lg 20px;
font-size: $font-size-base; font-size: 18px;
font-weight: $font-weight-medium; font-weight: $font-weight-bold;
color: $text-dark; color: #212529;
cursor: pointer; cursor: pointer;
transition: $transition-base; transition: $transition-base;
position: relative; gap: 95px;
gap: $spacing-md; min-height: 54px;
@media (max-width: $breakpoint-sm) { @media (max-width: $breakpoint-md) {
padding: $spacing-md $spacing-lg; padding: $spacing-md $spacing-lg;
font-size: $font-size-sm; font-size: 16px;
} gap: $spacing-lg;
&::before {
content: 'Q';
position: absolute;
left: $spacing-xl;
top: 50%;
transform: translateY(-50%);
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: $light-bg;
color: $primary-blue;
border-radius: $border-radius-circle;
font-size: $font-size-sm;
font-weight: $font-weight-bold;
transition: $transition-base;
@media (max-width: $breakpoint-sm) {
width: 28px;
height: 28px;
font-size: $font-size-xs;
}
} }
&:hover { &:hover {
background: rgba($primary-blue, 0.03); background: rgba($primary-blue, 0.02);
color: $primary-blue;
} }
.faq-question-text { .faq-question-text {
flex: 1; flex: 1;
padding-left: 48px;
line-height: $line-height-normal; line-height: $line-height-normal;
@media (max-width: $breakpoint-sm) {
padding-left: 40px;
}
} }
// Chevron icon
.faq-icon { .faq-icon {
flex-shrink: 0; flex-shrink: 0;
font-size: 20px; display: flex;
color: $text-gray; align-items: center;
transition: $transition-base; justify-content: center;
width: 22px;
height: 22px;
color: #212529;
transition: transform 0.3s ease;
@media (max-width: $breakpoint-sm) { svg {
font-size: 18px; width: 14px;
height: 8px;
} }
} }
} }
// Answer section - white background
.faq-answer { .faq-answer {
display: none; display: none;
padding: 0 $spacing-xl $spacing-lg; background: #F6F9FB;
margin-left: 48px; padding: $spacing-lg 20px $spacing-xl;
margin-right: $spacing-xl; border-top: none;
position: relative;
@media (max-width: $breakpoint-sm) { @media (max-width: $breakpoint-md) {
padding: 0 $spacing-lg $spacing-md; padding: $spacing-md $spacing-lg $spacing-lg;
margin-left: 40px;
margin-right: $spacing-lg;
}
&::before {
content: 'A';
position: absolute;
left: -48px;
top: 8px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: $primary-blue;
color: $white;
border-radius: $border-radius-circle;
font-size: $font-size-sm;
font-weight: $font-weight-bold;
@media (max-width: $breakpoint-sm) {
left: -40px;
width: 28px;
height: 28px;
font-size: $font-size-xs;
}
}
&.show {
display: block;
animation: slideDown 0.3s ease;
} }
.faq-answer-content { .faq-answer-content {
padding: $spacing-md 0; font-size: 18px;
font-size: $font-size-sm; font-weight: $font-weight-regular;
color: $text-gray; color: #515151;
background-color: #ffffff;
padding: $spacing-lg;
line-height: $line-height-loose; line-height: $line-height-loose;
@media (max-width: $breakpoint-sm) { @media (max-width: $breakpoint-md) {
font-size: $font-size-xs; font-size: 14px;
padding: $spacing-sm 0;
} }
// Content formatting // Content formatting
@@ -209,7 +157,7 @@
} }
} }
// FAQ Page specific styles // FAQ Page specific styles (legacy - for backward compatibility)
.faq-page { .faq-page {
min-height: calc(100vh - 140px); min-height: calc(100vh - 140px);
background: $gray-bg; background: $gray-bg;
@@ -1,90 +1,90 @@
<!doctype html> <!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_base_layout}"> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/kjbank_title_layout}">
<body> <body>
<section layout:fragment="title">
<div class="page-title-banner">
<img th:src="@{/img/img_title_bg.png}" class="title-image" alt="타이틀 배경">
<h1>FAQ</h1>
</div>
</section>
<th:block layout:fragment="contentFragment"> <th:block layout:fragment="contentFragment">
<!-- FAQ Page --> <section class="user-management-container">
<div class="faq-page">
<div class="faq-container">
<!-- Page Header --> <!-- Search and Filter Controls -->
<div class="faq-header"> <form name="faqForm" th:action="@{/faq_list}" method="post" th:object="${search}" onsubmit="return false;">
<h1 class="page-title">FAQ</h1> <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<p class="page-description">자주 묻는 질문과 답변을 확인하세요.</p> <input type="hidden" name="page" value="1">
</div>
<!-- Search and Filter Controls --> <div class="table-controls">
<form name="articleForm" th:action="@{/faq_list}" method="post" th:object="${search}" onSubmit="fn_search_article(); return false;"> <h2 class="total-count">
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"> <strong th:text="${page.totalElements}">0</strong>
<input name="page" type="hidden" value=""> </h2>
<div class="table-controls"> <div class="search-field">
<div class="total-count"> <input type="text"
<strong th:text="${page.totalElements}">0</strong> th:field="*{searchWrd}"
</div> placeholder="질문 또는 답변 내용으로 검색"
onkeypress="if(event.keyCode === 13) { fn_search_faq(); return false; }">
<div class="search-box"> <button type="button" class="search-field-btn" onclick="fn_search_faq()">
<input type="text" <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
th:field="*{searchWrd}" <path d="M17.5 17.5L13.875 13.875M15.8333 9.16667C15.8333 12.8486 12.8486 15.8333 9.16667 15.8333C5.48477 15.8333 2.5 12.8486 2.5 9.16667C2.5 5.48477 5.48477 2.5 9.16667 2.5C12.8486 2.5 15.8333 5.48477 15.8333 9.16667Z" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
placeholder="질문 또는 답변 내용으로 검색"> </svg>
<button type="button" class="btn btn-primary" onclick="fn_search_article()"> <span class="blind">검색</span>
<i class="fas fa-search"></i> </button>
<span>검색</span>
</button>
</div>
</div> </div>
</form> </div>
</form>
<!-- FAQ Accordion --> <!-- FAQ Accordion -->
<div class="faq-accordion" th:if="${!#lists.isEmpty(page.content)}"> <div class="faq-accordion" th:if="${!#lists.isEmpty(page.content)}">
<div class="faq-item" th:each="faq : ${page.content}"> <div class="faq-item" th:each="faq : ${page.content}">
<div class="faq-question"> <div class="faq-question">
<div class="faq-question-text" th:text="${faq.faqQuestion}"> <span class="faq-question-text" th:text="${faq.faqQuestion}">질문 내용이 여기에 표시됩니다.</span>
질문 내용이 여기에 표시됩니다. <span class="faq-icon">
</div> <svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
<i class="faq-icon fas fa-chevron-down"></i> <path d="M1 1L7 7L13 1" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</div> </svg>
<div class="faq-answer"> </span>
<div class="faq-answer-content" th:utext="${faq.faqAnswer}"> </div>
답변 내용이 여기에 표시됩니다. <div class="faq-answer">
</div> <div class="faq-answer-content" th:utext="${faq.faqAnswer}">
답변 내용이 여기에 표시됩니다.
</div> </div>
</div> </div>
</div> </div>
<!-- Empty State -->
<div class="table-empty" th:if="${#lists.isEmpty(page.content)}">
<div class="empty-icon">
<img th:src="@{/img/img_nodata.png}" alt="데이터 없음">
</div>
<p class="empty-text">조회된 검색 결과가 없습니다.</p>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div>
</div> </div>
</div>
<!-- Empty State -->
<div class="user-empty-state" th:if="${#lists.isEmpty(page.content)}">
<div class="empty-icon">
<img th:src="@{/img/img_nodata.png}" alt="데이터 없음">
</div>
<h3>조회된 FAQ가 없습니다</h3>
<p>검색 조건을 변경하여 다시 시도해주세요.</p>
</div>
<!-- Pagination -->
<div class="pagination" th:replace="~{fragment/pagination :: pagination(jsFunction='fn_select_page')}">
</div>
</section>
</th:block> </th:block>
</body> </body>
<th:block layout:fragment="contentScript"> <th:block layout:fragment="contentScript">
<script> <script>
function fn_select_page(pageNo) { function fn_select_page(pageNo) {
document.articleForm.page.value = pageNo; document.faqForm.page.value = pageNo;
document.articleForm.action = '[[@{/faq_list}]]'; document.faqForm.submit();
document.articleForm.submit();
} }
function fn_search_article() { function fn_search_faq() {
document.articleForm.page.value = 1; document.faqForm.page.value = 1;
document.articleForm.submit(); document.faqForm.submit();
} }
function decodeHTMLEntities(text) { function decodeHTMLEntities(text) {
@@ -105,23 +105,19 @@
e.preventDefault(); e.preventDefault();
const faqItem = this.closest('.faq-item'); const faqItem = this.closest('.faq-item');
const faqAnswer = faqItem.querySelector('.faq-answer');
const isActive = faqItem.classList.contains('active'); const isActive = faqItem.classList.contains('active');
// Close all other items (optional - for single open behavior)
// document.querySelectorAll('.faq-item.active').forEach(function(item) {
// item.classList.remove('active');
// });
// Toggle active state // Toggle active state
faqItem.classList.toggle('active'); faqItem.classList.toggle('active');
// Toggle answer visibility with animation
if (isActive) {
faqAnswer.classList.remove('show');
} else {
faqAnswer.classList.add('show');
}
}); });
}); });
}); });
</script> </script>
</th:block> </th:block>
</html> </html>