07Event FAQ
질문 세 개 중 하나만 열려 있고, 클릭한 질문의 답변 칸이 grid-template-rows 로 펼쳐지며 화살표가 회전합니다. 자동 재생 중엔 세 질문이 순서대로 스스로 열립니다.
Best for
grid-template-rowsaria-expandedclick

<main class="stage">
<section class="fq is-demo">
<div class="fq__item is-open" data-i="0">
<button class="fq__q" type="button" aria-expanded="true">
<span>Can I get a refund?</span>
<span class="fq__chevron" aria-hidden="true"></span>
</button>
<div class="fq__a-wrap"><p class="fq__a">Full refund up to 7 days before; 10% fee after.</p></div>
</div>
<div class="fq__item" data-i="1">
<button class="fq__q" type="button" aria-expanded="false">
<span>Can I join online?</span>
<span class="fq__chevron" aria-hidden="true"></span>
</button>
<div class="fq__a-wrap"><p class="fq__a">In-person only. Key sessions go up on video later.</p></div>
</div>
<div class="fq__item" data-i="2">
<button class="fq__q" type="button" aria-expanded="false">
<span>Is parking available?</span>
<span class="fq__chevron" aria-hidden="true"></span>
</button>
<div class="fq__a-wrap"><p class="fq__a">Free parking for 2h, then ₩2,000/hr after.</p></div>
</div>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-base; // 답변 칸이 펼쳐지는 시간
$easing: $ease-spring;
$color: $subject-ink; // 화살표·강조 색
@include stage($stage-orange);
.fq { width: min(400px, 94vw); display: flex; flex-direction: column; gap: $sp-2; }
.fq__item { background: $subject-cream; border-radius: $r-card; overflow: hidden; }
.fq__q {
width: 100%;
display: flex; justify-content: space-between; align-items: center; gap: $sp-2;
padding: $sp-3 $sp-4;
background: none; border: 0; cursor: pointer; text-align: left;
font-weight: 600; font-size: clamp(11px, 3.2vw, 12px); line-height: 1.3; font-family: inherit;
color: $subject-ink;
-webkit-tap-highlight-color: transparent;
}
.fq__chevron {
flex: 0 0 auto;
width: 7px; height: 7px;
border-right: 2px solid rgba(23, 20, 26, .5);
border-bottom: 2px solid rgba(23, 20, 26, .5);
transform: rotate(-45deg);
transition: transform $duration $easing;
}
.fq__item.is-open .fq__chevron { transform: rotate(45deg); }
.fq__a-wrap {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows $duration $easing;
}
.fq__item.is-open .fq__a-wrap { grid-template-rows: 1fr; }
.fq__a {
min-height: 0;
overflow: hidden;
margin: 0;
padding: 0 $sp-4 $sp-3;
font-weight: 500; font-size: 11px; line-height: 1.5; font-family: inherit;
color: rgba(23, 20, 26, .7);
}
// ── 데모 루프: 클릭 전까지 세 질문이 순서대로 스스로 열린다 ──────────────
.fq.is-demo .fq__item:nth-child(1) .fq__a-wrap { animation: fq-open-0 $dur-loop $easing infinite; }
.fq.is-demo .fq__item:nth-child(1) .fq__chevron { animation: fq-chev-0 $dur-loop $easing infinite; }
.fq.is-demo .fq__item:nth-child(2) .fq__a-wrap { animation: fq-open-1 $dur-loop $easing infinite; }
.fq.is-demo .fq__item:nth-child(2) .fq__chevron { animation: fq-chev-1 $dur-loop $easing infinite; }
.fq.is-demo .fq__item:nth-child(3) .fq__a-wrap { animation: fq-open-2 $dur-loop $easing infinite; }
.fq.is-demo .fq__item:nth-child(3) .fq__chevron { animation: fq-chev-2 $dur-loop $easing infinite; }
@keyframes fq-open-0 { 0%, 15% { grid-template-rows: 1fr; } 30%, 100% { grid-template-rows: 0fr; } }
@keyframes fq-open-1 { 0%, 30% { grid-template-rows: 0fr; } 45%, 63% { grid-template-rows: 1fr; } 78%, 100% { grid-template-rows: 0fr; } }
@keyframes fq-open-2 { 0%, 63% { grid-template-rows: 0fr; } 78%, 96% { grid-template-rows: 1fr; } 100% { grid-template-rows: 0fr; } }
@keyframes fq-chev-0 { 0%, 15% { transform: rotate(45deg); } 30%, 100% { transform: rotate(-45deg); } }
@keyframes fq-chev-1 { 0%, 30% { transform: rotate(-45deg); } 45%, 63% { transform: rotate(45deg); } 78%, 100% { transform: rotate(-45deg); } }
@keyframes fq-chev-2 { 0%, 63% { transform: rotate(-45deg); } 78%, 96% { transform: rotate(45deg); } 100% { transform: rotate(-45deg); } }
// ── 반응형: 세로가 짧은 화면에선 여백을 줄인다(BD9) ──────────────
@media (max-width: 340px), (max-height: 220px) {
.fq { gap: $sp-1; }
.fq__q { padding: $sp-2 $sp-3; font-size: 11px; }
.fq__a { padding: 0 $sp-3 $sp-2; font-size: 10px; }
}
// ── 접근성 ──────────────────────────────────────────
@media (prefers-reduced-motion: reduce) {
.fq__chevron, .fq__a-wrap { transition: none; }
.fq.is-demo .fq__item .fq__a-wrap,
.fq.is-demo .fq__item .fq__chevron { animation: none !important; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ff4d1f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.fq {
width: min(400px, 94vw);
display: flex;
flex-direction: column;
gap: 8px;
}
.fq__item {
background: #fff7e6;
border-radius: 16px;
overflow: hidden;
}
.fq__q {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: none;
border: 0;
cursor: pointer;
text-align: left;
font-weight: 600;
font-size: clamp(11px, 3.2vw, 12px);
line-height: 1.3;
font-family: inherit;
color: #17141a;
-webkit-tap-highlight-color: transparent;
}
.fq__chevron {
flex: 0 0 auto;
width: 7px;
height: 7px;
border-right: 2px solid rgba(23, 20, 26, 0.5);
border-bottom: 2px solid rgba(23, 20, 26, 0.5);
transform: rotate(-45deg);
transition: transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.fq__item.is-open .fq__chevron {
transform: rotate(45deg);
}
.fq__a-wrap {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.fq__item.is-open .fq__a-wrap {
grid-template-rows: 1fr;
}
.fq__a {
min-height: 0;
overflow: hidden;
margin: 0;
padding: 0 16px 12px;
font-weight: 500;
font-size: 11px;
line-height: 1.5;
font-family: inherit;
color: rgba(23, 20, 26, 0.7);
}
.fq.is-demo .fq__item:nth-child(1) .fq__a-wrap {
animation: fq-open-0 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.fq.is-demo .fq__item:nth-child(1) .fq__chevron {
animation: fq-chev-0 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.fq.is-demo .fq__item:nth-child(2) .fq__a-wrap {
animation: fq-open-1 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.fq.is-demo .fq__item:nth-child(2) .fq__chevron {
animation: fq-chev-1 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.fq.is-demo .fq__item:nth-child(3) .fq__a-wrap {
animation: fq-open-2 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.fq.is-demo .fq__item:nth-child(3) .fq__chevron {
animation: fq-chev-2 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
@keyframes fq-open-0 {
0%, 15% {
grid-template-rows: 1fr;
}
30%, 100% {
grid-template-rows: 0fr;
}
}
@keyframes fq-open-1 {
0%, 30% {
grid-template-rows: 0fr;
}
45%, 63% {
grid-template-rows: 1fr;
}
78%, 100% {
grid-template-rows: 0fr;
}
}
@keyframes fq-open-2 {
0%, 63% {
grid-template-rows: 0fr;
}
78%, 96% {
grid-template-rows: 1fr;
}
100% {
grid-template-rows: 0fr;
}
}
@keyframes fq-chev-0 {
0%, 15% {
transform: rotate(45deg);
}
30%, 100% {
transform: rotate(-45deg);
}
}
@keyframes fq-chev-1 {
0%, 30% {
transform: rotate(-45deg);
}
45%, 63% {
transform: rotate(45deg);
}
78%, 100% {
transform: rotate(-45deg);
}
}
@keyframes fq-chev-2 {
0%, 63% {
transform: rotate(-45deg);
}
78%, 96% {
transform: rotate(45deg);
}
100% {
transform: rotate(-45deg);
}
}
@media (max-width: 340px), (max-height: 220px) {
.fq {
gap: 4px;
}
.fq__q {
padding: 8px 12px;
font-size: 11px;
}
.fq__a {
padding: 0 12px 8px;
font-size: 10px;
}
}
@media (prefers-reduced-motion: reduce) {
.fq__chevron, .fq__a-wrap {
transition: none;
}
.fq.is-demo .fq__item .fq__a-wrap,
.fq.is-demo .fq__item .fq__chevron {
animation: none !important;
}
}// 실제 동작: 질문을 클릭하면 그 답변만 실제로 펼쳐지고 나머지는 닫힌다(단일 오픈).
var root = document.querySelector('.fq');
var items = root.querySelectorAll('.fq__item');
items.forEach(function (item) {
item.querySelector('.fq__q').addEventListener('click', function () {
root.classList.remove('is-demo');
var open = item.classList.contains('is-open');
items.forEach(function (o) {
o.classList.remove('is-open');
o.querySelector('.fq__q').setAttribute('aria-expanded', 'false');
});
if (!open) {
item.classList.add('is-open');
item.querySelector('.fq__q').setAttribute('aria-expanded', 'true');
}
});
});import { useState } from "react";
import type { CSSProperties } from "react";
import styles from "./EventFaq.module.scss";
const ITEMS = [
{ q: "티켓은 환불되나요?", a: "행사 7일 전까지 전액 환불, 이후엔 수수료 10%가 붙습니다." },
{ q: "온라인으로도 참가할 수 있나요?", a: "현장 참가만 가능합니다. 주요 세션은 이후 영상으로 공개됩니다." },
{ q: "주차는 지원되나요?", a: "건물 지하 주차장 2시간 무료, 이후 시간당 2천 원입니다." },
];
export type EventFaqProps = { duration?: number; easing?: string; color?: string };
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function EventFaq({ duration = 300, easing = "cubic-bezier(.2,.8,.2,1)", color = "#17141a" }: EventFaqProps) {
const [open, setOpen] = useState(0);
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<div className={styles.fq} style={vars}>
{ITEMS.map((item, i) => (
<div key={item.q} className={i === open ? `${styles.item} ${styles.isOpen}` : styles.item}>
<button
type="button"
className={styles.q}
aria-expanded={i === open}
onClick={() => setOpen((o) => (o === i ? -1 : i))}
>
<span>{item.q}</span>
<span className={styles.chevron} aria-hidden="true" />
</button>
<div className={styles.aWrap}>
<p className={styles.a}>{item.a}</p>
</div>
</div>
))}
</div>
);
}07 Event FAQ — Measured area 28.795% · moving frames 10/23Original by GODRICH · SCSS and React ship in the zip · 9 Event Landing Page Templates — Copy-Paste
The post this demo lives in
9 Event Landing Page Templates — Copy-PasteEvent landing page templates are ready-to-paste hero, schedule, speaker, and pricing sections for conference and meetup See all demos in this post


