05Viền nhấn mạnh mục đang mở
Chỉ mục đang mở có viền trái và nền được nhấn mạnh để nổi bật.
Dùng choDanh sách cài đặt, đánh dấu section đang chọn
3px배경 포함색만

<main class="stage">
<section class="accordion is-demo-border">
<button class="accordion__header" type="button" aria-expanded="false">
<span class="accordion__title">Ưu đãi theo hạng thành viên</span>
<span class="accordion__chevron" aria-hidden="true"></span>
</button>
<div class="accordion__panel is-demo">
<p class="accordion__body">Tỷ lệ tích điểm khác nhau theo từng hạng.</p>
</div>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-base;
$easing: $ease-spring;
$color: $subject-blue;
@include stage($stage-ink);
.accordion {
width: clamp(220px, 78vw, 340px);
background: $subject-cream;
border-radius: $r-card;
border-left: 3px solid transparent;
box-shadow: $shadow-raised;
overflow: hidden;
font-family: inherit;
}
.accordion__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
width: 100%;
border: 0;
background: none;
padding: clamp(12px, 3vw, 18px) clamp(14px, 4vw, 20px);
cursor: pointer;
color: $subject-ink;
font-weight: 600;
font-size: clamp(14px, 3.6vw, 17px);
line-height: 1.3;
font-family: inherit;
text-align: left;
}
.accordion__chevron {
flex: none; width: 9px; height: 9px;
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
transform: rotate(45deg);
}
.accordion__body {
margin: 0;
padding: 0 clamp(14px, 4vw, 20px) clamp(12px, 3vw, 18px);
color: $subject-ink;
opacity: .86;
font-weight: 400;
font-size: clamp(12px, 3.2vw, 14.5px);
line-height: 1.5;
font-family: inherit;
}
.accordion__panel {
height: 0;
overflow: hidden;
interpolate-size: allow-keywords;
}
// 실사용: 클릭하면 열림 상태(.is-open)가 붙고 목표 높이까지 전환된다
.accordion.is-open .accordion__panel {
height: calc-size(auto, size);
transition: height $duration $easing;
}
@supports not (height: calc-size(auto, size)) {
.accordion.is-open .accordion__panel { height: auto; }
}
// 데모: 2초 주기로 스스로 열고 닫힌다. WAAPI 시계로 재생 위치를 잡으려면
// transition 이 아니라 keyframes 여야 한다 (blog-demo 측정 규칙)
.accordion__panel.is-demo {
animation: accordion-loop $dur-loop $easing infinite;
}
@keyframes accordion-loop {
0%, 8% { height: 0; }
42%, 58% { height: calc-size(auto, size); }
92%, 100% { height: 0; }
}
@supports not (height: calc-size(auto, size)) {
@keyframes accordion-loop {
0%, 8% { height: 0; }
42%, 58% { height: 220px; }
92%, 100% { height: 0; }
}
}
// 열려 있는 동안만 왼쪽 테두리와 배경이 강조된다 (패널과 같은 리듬)
.accordion.is-demo-border {
animation: border-loop $dur-loop $easing infinite;
}
@keyframes border-loop {
0%, 8% { border-left-color: transparent; background: $subject-cream; }
42%, 58% { border-left-color: $color; background: $subject-cream; }
92%, 100% { border-left-color: transparent; background: $subject-cream; }
}
@media (prefers-reduced-motion: reduce) {
.accordion__panel,
.accordion__panel.is-demo,
.accordion__chevron,
.accordion__body,
.accordion.is-open .accordion__panel {
animation: none !important;
transition: none !important;
}
.accordion__panel.is-demo { height: auto; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.accordion {
width: clamp(220px, 78vw, 340px);
background: #fff7e6;
border-radius: 16px;
border-left: 3px solid transparent;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
overflow: hidden;
font-family: inherit;
}
.accordion__header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
width: 100%;
border: 0;
background: none;
padding: clamp(12px, 3vw, 18px) clamp(14px, 4vw, 20px);
cursor: pointer;
color: #17141a;
font-weight: 600;
font-size: clamp(14px, 3.6vw, 17px);
line-height: 1.3;
font-family: inherit;
text-align: left;
}
.accordion__chevron {
flex: none;
width: 9px;
height: 9px;
border-right: 2px solid currentColor;
border-bottom: 2px solid currentColor;
transform: rotate(45deg);
}
.accordion__body {
margin: 0;
padding: 0 clamp(14px, 4vw, 20px) clamp(12px, 3vw, 18px);
color: #17141a;
opacity: 0.86;
font-weight: 400;
font-size: clamp(12px, 3.2vw, 14.5px);
line-height: 1.5;
font-family: inherit;
}
.accordion__panel {
height: 0;
overflow: hidden;
interpolate-size: allow-keywords;
}
.accordion.is-open .accordion__panel {
height: calc-size(auto, size);
transition: height 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
@supports not (height: calc-size(auto, size)) {
.accordion.is-open .accordion__panel {
height: auto;
}
}
.accordion__panel.is-demo {
animation: accordion-loop 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
@keyframes accordion-loop {
0%, 8% {
height: 0;
}
42%, 58% {
height: calc-size(auto, size);
}
92%, 100% {
height: 0;
}
}
@supports not (height: calc-size(auto, size)) {
@keyframes accordion-loop {
0%, 8% {
height: 0;
}
42%, 58% {
height: 220px;
}
92%, 100% {
height: 0;
}
}
}
.accordion.is-demo-border {
animation: border-loop 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
@keyframes border-loop {
0%, 8% {
border-left-color: transparent;
background: #fff7e6;
}
42%, 58% {
border-left-color: #2f6df6;
background: #fff7e6;
}
92%, 100% {
border-left-color: transparent;
background: #fff7e6;
}
}
@media (prefers-reduced-motion: reduce) {
.accordion__panel,
.accordion__panel.is-demo,
.accordion__chevron,
.accordion__body,
.accordion.is-open .accordion__panel {
animation: none !important;
transition: none !important;
}
.accordion__panel.is-demo {
height: auto;
}
}(function () {
var acc = document.querySelector('.accordion');
var header = acc.querySelector('.accordion__header');
var panel = acc.querySelector('.accordion__panel');
header.addEventListener('click', function () {
panel.classList.remove('is-demo');
acc.classList.remove('is-demo-border');
var open = acc.classList.toggle('is-open');
header.setAttribute('aria-expanded', String(open));
});
})();import { useState } from "react";
import type { CSSProperties, ReactNode } from "react";
import styles from "./AccordionBorderHighlight.module.scss";
export type AccordionBorderHighlightProps = {
/** 헤더에 보이는 제목 */
header?: ReactNode;
/** 열렸을 때 보이는 본문 */
children?: ReactNode;
/** height:auto 전환 시간 (기본 300ms) */
duration?: number;
/** CSS easing */
easing?: string;
/** 강조 색 */
color?: string;
defaultOpen?: boolean;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function AccordionBorderHighlight({
header = "Membership tier perks",
children = "Points rates vary by tier.",
duration = 300,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#2f6df6",
defaultOpen = false,
}: AccordionBorderHighlightProps) {
const [open, setOpen] = useState(defaultOpen);
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.accordion} style={vars}>
<button
type="button"
className={styles.header}
aria-expanded={open}
onClick={() => setOpen((v) => !v)}
>
<span>{header}</span>
<span className={`${styles.chevron} ${open ? styles.chevronOpen : ""}`} aria-hidden="true" />
</button>
<div className={`${styles.panel} ${open ? styles.panelOpen : ""}`}>
<p className={styles.body}>{children}</p>
</div>
</section>
);
}05 Viền nhấn mạnh mục đang mở — Diện tích đo được 12.473% · khung hình chuyển động 17/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 hiệu ứng css accordion animation không JS
Bài viết chứa demo này
9 hiệu ứng css accordion animation không JSCss accordion animation là hiệu ứng chuyển chiều cao nội dung từ 0 sang auto, dựng bằng interpolate-size và calc-size() Xem tất cả demo trong bài này


