01Hero phòng báo chí
Ba vòng tròn mờ phía sau khung cảnh liên tục lơ lửng trong khi wordmark placeholder, headline, subline và hai CTA nằm gọn trong một màn hình; nút 'Đăng ký nhận tin' bên phải đổi aria-pressed thật sau từng lần bấm.
Dùng choSection trên cùng của press room và trang media, hero cho hub thông cáo báo chí
transformopacityaria-pressed

<main class="stage">
<section class="ph">
<span class="ph__orb ph__orb--1" aria-hidden="true"></span>
<span class="ph__orb ph__orb--2" aria-hidden="true"></span>
<span class="ph__orb ph__orb--3" aria-hidden="true"></span>
<span class="ph__mark" aria-hidden="true"></span>
<p class="ph__eyebrow">PRESS ROOM</p>
<h1 class="ph__title">Dành cho báo chí & đối tác<br>Tin mới nhất</h1>
<p class="ph__sub">Thông cáo báo chí, media kit và liên hệ đều gộp tại một chỗ.</p>
<div class="ph__row">
<a class="ph__cta" href="#">Xem thông cáo</a>
<button class="ph__sub-btn" type="button" aria-pressed="false">
<span class="ph__sub-ic" aria-hidden="true"></span>
Đăng ký nhận tin
</button>
</div>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-quick; // 구독 버튼 전환
$easing: $ease-pop;
$color: $subject-blue; // CTA·아이콘 강조색
@include stage($stage-ink);
.ph {
position: relative;
box-sizing: border-box; // width 에 패딩을 포함 — 아니면 320px 에서 패딩만큼 넘친다(실측 2026-09-10)
width: min(400px, 92vw);
overflow: hidden;
border-radius: $r-card;
padding: $sp-3 $sp-5 $sp-2;
text-align: center;
isolation: isolate;
}
// ── 배경 원 세 개: 계속 떠다니는 장식(호버 아님, 무조건 재생) ──────────────
.ph__orb {
position: absolute;
border-radius: 50%;
filter: blur(18px);
opacity: .55;
z-index: -1;
}
.ph__orb--1 { width: 170px; height: 170px; left: -45px; top: -35px; background: radial-gradient(circle, $subject-blue, transparent 62%); opacity: 1; filter: blur(3px); animation: ph-float-a 2s ease-in-out infinite; }
.ph__orb--2 { width: 130px; height: 130px; right: -25px; top: 6px; background: radial-gradient(circle, $subject-cream, transparent 62%); opacity: .7; filter: blur(3px); animation: ph-float-b 2s ease-in-out infinite; }
.ph__orb--3 { width: 150px; height: 150px; left: 28%; bottom: -70px; background: radial-gradient(circle, $subject-blue, transparent 62%); opacity: .95; filter: blur(3px); animation: ph-float-a 2s ease-in-out infinite reverse; }
@keyframes ph-float-a {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate($sp-10, -$sp-8) scale(1.5); }
}
@keyframes ph-float-b {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(-$sp-8, $sp-8) scale(.6); }
}
// ── 플레이스홀더 워드마크: 실제 로고가 아니라 도형 하나 ──────────────
.ph__mark {
display: block;
width: $sp-5; height: $sp-5;
margin: 0 auto $sp-1;
background: $color;
border-radius: $r-xs;
transform: rotate(45deg);
}
.ph__eyebrow {
margin: 0 0 $sp-1;
font-weight: 700; font-size: 10px; line-height: 1; font-family: inherit;
letter-spacing: .14em;
color: rgba(255, 247, 230, .6);
}
.ph__title {
margin: 0 0 $sp-1;
font-weight: 800; font-size: clamp(16px, 4.6vw, 19px); line-height: 1.14; font-family: inherit;
color: $subject-cream;
}
.ph__sub {
margin: 0 0 $sp-2;
font-weight: 500; font-size: clamp(10px, 2.8vw, 11px); line-height: 1.4; font-family: inherit;
color: rgba(255, 247, 230, .68);
}
.ph__row { display: flex; justify-content: center; align-items: center; gap: $sp-2; flex-wrap: wrap; }
.ph__cta {
padding: $sp-2 $sp-5;
border-radius: $r-control;
background: $subject-blue-deep;
color: $subject-cream;
font-weight: 700; font-size: 12px; line-height: 1.4; font-family: inherit;
text-decoration: none;
}
.ph__sub-btn {
display: inline-flex; align-items: center; gap: $sp-1;
padding: $sp-2 $sp-3;
border-radius: $r-control;
border: 1px solid rgba(255, 247, 230, .3);
background: transparent;
color: rgba(255, 247, 230, .82);
font-weight: 600; font-size: 12px; line-height: 1.4; font-family: inherit;
cursor: pointer;
transition: background $duration $easing, border-color $duration $easing, transform $duration $easing;
}
.ph__sub-ic {
width: $sp-3; height: $sp-3;
border-radius: 0;
background: currentColor;
clip-path: polygon(50% 0%, 63% 38%, 100% 38%, 70% 60%, 82% 100%, 50% 76%, 18% 100%, 30% 60%, 0% 38%, 37% 38%);
opacity: .5;
transition: opacity $duration $easing, transform $duration $easing;
}
.ph__sub-btn[aria-pressed="true"] {
background: rgba(255, 247, 230, .12);
border-color: $subject-cream;
}
.ph__sub-btn[aria-pressed="true"] .ph__sub-ic {
opacity: 1;
color: $color;
transform: scale(1.15);
}
// ── 반응형: 320×200 처럼 세로가 짧은 화면에선 장식을 줄인다(BD9) ──────────────
@media (max-width: 340px), (max-height: 220px) {
.ph { padding: $sp-2 $sp-4; }
.ph__mark { display: none; }
.ph__eyebrow { display: none; }
.ph__title { font-size: 14px; line-height: 1.15; margin-bottom: $sp-1; }
.ph__sub { display: none; }
.ph__row { gap: $sp-1; }
.ph__cta, .ph__sub-btn { padding: 4px $sp-3; font-size: 10px; }
}
// ── 접근성 ──────────────────────────────────────────
@media (prefers-reduced-motion: reduce) {
.ph__orb { animation: none !important; }
.ph__sub-btn, .ph__sub-ic { transition: none; }
}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);
}
.ph {
position: relative;
box-sizing: border-box;
width: min(400px, 92vw);
overflow: hidden;
border-radius: 16px;
padding: 12px 20px 8px;
text-align: center;
isolation: isolate;
}
.ph__orb {
position: absolute;
border-radius: 50%;
filter: blur(18px);
opacity: 0.55;
z-index: -1;
}
.ph__orb--1 {
width: 170px;
height: 170px;
left: -45px;
top: -35px;
background: radial-gradient(circle, #2f6df6, transparent 62%);
opacity: 1;
filter: blur(3px);
animation: ph-float-a 2s ease-in-out infinite;
}
.ph__orb--2 {
width: 130px;
height: 130px;
right: -25px;
top: 6px;
background: radial-gradient(circle, #fff7e6, transparent 62%);
opacity: 0.7;
filter: blur(3px);
animation: ph-float-b 2s ease-in-out infinite;
}
.ph__orb--3 {
width: 150px;
height: 150px;
left: 28%;
bottom: -70px;
background: radial-gradient(circle, #2f6df6, transparent 62%);
opacity: 0.95;
filter: blur(3px);
animation: ph-float-a 2s ease-in-out infinite reverse;
}
@keyframes ph-float-a {
0%, 100% {
transform: translate(0, 0) scale(1);
}
50% {
transform: translate(40px, -32px) scale(1.5);
}
}
@keyframes ph-float-b {
0%, 100% {
transform: translate(0, 0) scale(1);
}
50% {
transform: translate(-32px, 32px) scale(0.6);
}
}
.ph__mark {
display: block;
width: 20px;
height: 20px;
margin: 0 auto 4px;
background: #2f6df6;
border-radius: 4px;
transform: rotate(45deg);
}
.ph__eyebrow {
margin: 0 0 4px;
font-weight: 700;
font-size: 10px;
line-height: 1;
font-family: inherit;
letter-spacing: 0.14em;
color: rgba(255, 247, 230, 0.6);
}
.ph__title {
margin: 0 0 4px;
font-weight: 800;
font-size: clamp(16px, 4.6vw, 19px);
line-height: 1.14;
font-family: inherit;
color: #fff7e6;
}
.ph__sub {
margin: 0 0 8px;
font-weight: 500;
font-size: clamp(10px, 2.8vw, 11px);
line-height: 1.4;
font-family: inherit;
color: rgba(255, 247, 230, 0.68);
}
.ph__row {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.ph__cta {
padding: 8px 20px;
border-radius: 12px;
background: #0a4fe8;
color: #fff7e6;
font-weight: 700;
font-size: 12px;
line-height: 1.4;
font-family: inherit;
text-decoration: none;
}
.ph__sub-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 8px 12px;
border-radius: 12px;
border: 1px solid rgba(255, 247, 230, 0.3);
background: transparent;
color: rgba(255, 247, 230, 0.82);
font-weight: 600;
font-size: 12px;
line-height: 1.4;
font-family: inherit;
cursor: pointer;
transition: background 220ms cubic-bezier(0.2, 1.4, 0.4, 1), border-color 220ms cubic-bezier(0.2, 1.4, 0.4, 1), transform 220ms cubic-bezier(0.2, 1.4, 0.4, 1);
}
.ph__sub-ic {
width: 12px;
height: 12px;
border-radius: 0;
background: currentColor;
clip-path: polygon(50% 0%, 63% 38%, 100% 38%, 70% 60%, 82% 100%, 50% 76%, 18% 100%, 30% 60%, 0% 38%, 37% 38%);
opacity: 0.5;
transition: opacity 220ms cubic-bezier(0.2, 1.4, 0.4, 1), transform 220ms cubic-bezier(0.2, 1.4, 0.4, 1);
}
.ph__sub-btn[aria-pressed=true] {
background: rgba(255, 247, 230, 0.12);
border-color: #fff7e6;
}
.ph__sub-btn[aria-pressed=true] .ph__sub-ic {
opacity: 1;
color: #2f6df6;
transform: scale(1.15);
}
@media (max-width: 340px), (max-height: 220px) {
.ph {
padding: 8px 16px;
}
.ph__mark {
display: none;
}
.ph__eyebrow {
display: none;
}
.ph__title {
font-size: 14px;
line-height: 1.15;
margin-bottom: 4px;
}
.ph__sub {
display: none;
}
.ph__row {
gap: 4px;
}
.ph__cta, .ph__sub-btn {
padding: 4px 12px;
font-size: 10px;
}
}
@media (prefers-reduced-motion: reduce) {
.ph__orb {
animation: none !important;
}
.ph__sub-btn, .ph__sub-ic {
transition: none;
}
}// 실제 동작: 클릭마다 Đăng ký 상태가 실제로 바뀐다(아이콘 채움 + aria-pressed).
var btn = document.querySelector('.ph__sub-btn');
btn.addEventListener('click', function () {
var on = btn.getAttribute('aria-pressed') === 'true';
btn.setAttribute('aria-pressed', String(!on));
});import { useState } from "react";
import type { CSSProperties } from "react";
import styles from "./PressHero.module.scss";
export type PressHeroProps = {
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function PressHero({ duration = 220, easing = "cubic-bezier(.2,1.4,.4,1)", color = "#2f6df6" }: PressHeroProps) {
const [subscribed, setSubscribed] = useState(false);
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.ph} style={vars}>
<span className={`${styles.orb} ${styles.orb1}`} aria-hidden="true" />
<span className={`${styles.orb} ${styles.orb2}`} aria-hidden="true" />
<span className={`${styles.orb} ${styles.orb3}`} aria-hidden="true" />
<span className={styles.mark} aria-hidden="true" />
<p className={styles.eyebrow}>PRESS ROOM</p>
<h1 className={styles.title}>언론과 파트너를 위한<br />최신 소식</h1>
<p className={styles.sub}>보도자료·미디어 키트·연락처를 한 곳에서 확인하세요.</p>
<div className={styles.row}>
<a className={styles.cta} href="#">보도자료 보기</a>
<button
type="button"
className={styles.subBtn}
aria-pressed={subscribed}
onClick={() => setSubscribed((v) => !v)}
>
<span className={styles.subIc} aria-hidden="true" />
보도자료 구독
</button>
</div>
</section>
);
}01 Hero phòng báo chí — Diện tích đo được 30.411% · khung hình chuyển động 18/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 mẫu press page design — dán vào landing
Bài viết chứa demo này
9 mẫu press page design — dán vào landingPress page design là bộ code gom sẵn từng khối của trang báo chí: press release, media kit, liên hệ người phụ trách. Từ Xem tất cả demo trong bài này


