01Lock-on hero
Four corner brackets close in around the headline card like a camera locking focus, while a scanline sweeps top to bottom behind it.
Best forSaaS landing first screen, product launch cover
브래킷 락온스캔라인 스윕모노스페이스 리드아웃

<main class="stage">
<section class="hero is-demo">
<span class="hero__corner hero__corner--tl" aria-hidden="true"></span>
<span class="hero__corner hero__corner--tr" aria-hidden="true"></span>
<span class="hero__corner hero__corner--bl" aria-hidden="true"></span>
<span class="hero__corner hero__corner--br" aria-hidden="true"></span>
<div class="hero__scan" aria-hidden="true"></div>
<p class="hero__eyebrow"><span class="hero__dot" aria-hidden="true"></span>SENTRY / ONLINE</p>
<h1 class="hero__title">Never miss a moment —<br>a dashboard that watches</h1>
<p class="hero__desc">It detects and logs in real time.</p>
<button class="hero__cta" type="button">Start for free</button>
<p class="hero__readout">SIG <span class="hero__cursor" aria-hidden="true"></span></p>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop; // 브래킷 락온 + 스캔 한 주기
$easing: $ease-spring; // 브래킷이 조여드는 곡선
$color: $stage-yellow; // 호박색 터미널 강조색
@include stage($stage-ink);
.hero {
position: relative;
box-sizing: border-box;
width: clamp(240px, 82vw, 380px);
padding: $sp-5 $sp-4;
border-radius: $r-sm;
background: $subject-ink;
box-shadow: 0 0 0 1px rgba($color, .4), 0 0 24px rgba($color, .16);
overflow: hidden;
// 320px 폰 체크: eyebrow·2줄 제목·설명·CTA·터미널 readout 5단이 129·130 형제보다 한 단 더 많아
// 200px 높이를 넘는다 (131-01 실측: scrollHeight 238px vs innerHeight 200px). 단마다 여백을
// 한 단계씩 좁혀 맞춘다.
}
.hero__scan {
position: absolute;
inset: 0;
pointer-events: none;
background:
repeating-linear-gradient(to bottom, rgba($color, 0) 0, rgba($color, 0) 5px, rgba($color, .1) 6px, rgba($color, 0) 7px),
linear-gradient(to bottom, rgba($color, 0) 0%, rgba($color, .32) 48%, rgba($color, 0) 100%);
background-size: 100% 100%, 100% 240%;
background-position: 0 0, 0 -140%;
animation: scanSweep $duration linear infinite;
}
@keyframes scanSweep {
0% { background-position: 0 0, 0 -140%; }
100% { background-position: 0 0, 0 140%; }
}
.hero__corner {
position: absolute;
width: 18px;
height: 18px;
pointer-events: none;
border-color: $color;
border-style: solid;
border-width: 0;
animation: cornerPulse $duration $easing infinite;
}
.hero__corner--tl { top: 0; left: 0; border-top-width: 2px; border-left-width: 2px; transform-origin: top left; } // 규칙8: 괄호 꼭짓점 모서리 고정
.hero__corner--tr { top: 0; right: 0; border-top-width: 2px; border-right-width: 2px; transform-origin: top right; animation-delay: -.15s; } // 규칙8: 괄호 꼭짓점 모서리 고정
.hero__corner--bl { bottom: 0; left: 0; border-bottom-width: 2px; border-left-width: 2px; transform-origin: bottom left; animation-delay: -.3s; } // 규칙8: 괄호 꼭짓점 모서리 고정
.hero__corner--br { bottom: 0; right: 0; border-bottom-width: 2px; border-right-width: 2px; transform-origin: bottom right; animation-delay: -.45s; } // 규칙8: 괄호 꼭짓점 모서리 고정
@keyframes cornerPulse {
0%, 100% { transform: scale(.55); opacity: .35; }
50% { transform: scale(1); opacity: 1; }
}
.hero__eyebrow {
position: relative;
margin: 0 0 $sp-2;
display: flex;
align-items: center;
gap: $sp-2;
color: $color;
font-family: "Courier New", monospace;
font-weight: 700;
font-size: clamp(10px, 2.4vw, 11px);
line-height: 1;
letter-spacing: .12em;
text-transform: uppercase;
}
.hero__dot {
width: 6px;
height: 6px;
flex: none;
border-radius: 50%;
background: $color;
animation: blink $dur-loop steps(1) infinite;
}
@keyframes blink {
0%, 49% { opacity: 1; }
50%, 100% { opacity: .2; }
}
.hero__title {
position: relative;
margin: 0 0 $sp-1;
color: $subject-cream;
font-weight: 700;
font-size: clamp(18px, 5vw, 25px);
line-height: 1.15;
font-family: inherit;
}
.hero__desc {
position: relative;
margin: 0 0 $sp-3;
color: rgba($subject-cream, .85); // 스캔라인이 지나는 밝은 프레임(#544623)에서 .6 은 4.29:1 — .85 로 6.77:1
font-weight: 500;
font-size: clamp(12px, 2.8vw, 14px);
line-height: 1.4;
font-family: inherit;
}
.hero__cta {
position: relative;
border: 1px solid $color;
border-radius: $r-control;
background: rgba($color, .12);
color: $color;
padding: $sp-1 $sp-5;
font-weight: 700;
font-size: clamp(12px, 2.8vw, 13px);
line-height: 1;
font-family: inherit;
letter-spacing: .02em;
}
.hero__readout {
position: relative;
margin: $sp-2 0 0;
color: rgba($color, .7);
font-family: "Courier New", monospace;
font-weight: 400;
font-size: clamp(9px, 2.2vw, 10px);
line-height: 1;
letter-spacing: .04em;
}
.hero__cursor {
display: inline-block;
width: 6px;
height: 10px;
background: $color;
vertical-align: -1px;
animation: blink $dur-loop steps(1) infinite;
}
@media (prefers-reduced-motion: reduce) {
.hero__scan, .hero__corner, .hero__dot, .hero__cursor {
animation: none !important;
}
.hero__corner { transform: scale(1); opacity: 1; }
.hero__dot, .hero__cursor { opacity: 1; }
}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);
}
.hero {
position: relative;
box-sizing: border-box;
width: clamp(240px, 82vw, 380px);
padding: 20px 16px;
border-radius: 8px;
background: #17141a;
box-shadow: 0 0 0 1px rgba(255, 210, 63, 0.4), 0 0 24px rgba(255, 210, 63, 0.16);
overflow: hidden;
}
.hero__scan {
position: absolute;
inset: 0;
pointer-events: none;
background: repeating-linear-gradient(to bottom, rgba(255, 210, 63, 0) 0, rgba(255, 210, 63, 0) 5px, rgba(255, 210, 63, 0.1) 6px, rgba(255, 210, 63, 0) 7px), linear-gradient(to bottom, rgba(255, 210, 63, 0) 0%, rgba(255, 210, 63, 0.32) 48%, rgba(255, 210, 63, 0) 100%);
background-size: 100% 100%, 100% 240%;
background-position: 0 0, 0 -140%;
animation: scanSweep 2s linear infinite;
}
@keyframes scanSweep {
0% {
background-position: 0 0, 0 -140%;
}
100% {
background-position: 0 0, 0 140%;
}
}
.hero__corner {
position: absolute;
width: 18px;
height: 18px;
pointer-events: none;
border-color: #ffd23f;
border-style: solid;
border-width: 0;
animation: cornerPulse 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.hero__corner--tl {
top: 0;
left: 0;
border-top-width: 2px;
border-left-width: 2px;
transform-origin: top left;
}
.hero__corner--tr {
top: 0;
right: 0;
border-top-width: 2px;
border-right-width: 2px;
transform-origin: top right;
animation-delay: -0.15s;
}
.hero__corner--bl {
bottom: 0;
left: 0;
border-bottom-width: 2px;
border-left-width: 2px;
transform-origin: bottom left;
animation-delay: -0.3s;
}
.hero__corner--br {
bottom: 0;
right: 0;
border-bottom-width: 2px;
border-right-width: 2px;
transform-origin: bottom right;
animation-delay: -0.45s;
}
@keyframes cornerPulse {
0%, 100% {
transform: scale(0.55);
opacity: 0.35;
}
50% {
transform: scale(1);
opacity: 1;
}
}
.hero__eyebrow {
position: relative;
margin: 0 0 8px;
display: flex;
align-items: center;
gap: 8px;
color: #ffd23f;
font-family: "Courier New", monospace;
font-weight: 700;
font-size: clamp(10px, 2.4vw, 11px);
line-height: 1;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.hero__dot {
width: 6px;
height: 6px;
flex: none;
border-radius: 50%;
background: #ffd23f;
animation: blink 2s steps(1) infinite;
}
@keyframes blink {
0%, 49% {
opacity: 1;
}
50%, 100% {
opacity: 0.2;
}
}
.hero__title {
position: relative;
margin: 0 0 4px;
color: #fff7e6;
font-weight: 700;
font-size: clamp(18px, 5vw, 25px);
line-height: 1.15;
font-family: inherit;
}
.hero__desc {
position: relative;
margin: 0 0 12px;
color: rgba(255, 247, 230, 0.85);
font-weight: 500;
font-size: clamp(12px, 2.8vw, 14px);
line-height: 1.4;
font-family: inherit;
}
.hero__cta {
position: relative;
border: 1px solid #ffd23f;
border-radius: 12px;
background: rgba(255, 210, 63, 0.12);
color: #ffd23f;
padding: 4px 20px;
font-weight: 700;
font-size: clamp(12px, 2.8vw, 13px);
line-height: 1;
font-family: inherit;
letter-spacing: 0.02em;
}
.hero__readout {
position: relative;
margin: 8px 0 0;
color: rgba(255, 210, 63, 0.7);
font-family: "Courier New", monospace;
font-weight: 400;
font-size: clamp(9px, 2.2vw, 10px);
line-height: 1;
letter-spacing: 0.04em;
}
.hero__cursor {
display: inline-block;
width: 6px;
height: 10px;
background: #ffd23f;
vertical-align: -1px;
animation: blink 2s steps(1) infinite;
}
@media (prefers-reduced-motion: reduce) {
.hero__scan, .hero__corner, .hero__dot, .hero__cursor {
animation: none !important;
}
.hero__corner {
transform: scale(1);
opacity: 1;
}
.hero__dot, .hero__cursor {
opacity: 1;
}
}import type { CSSProperties } from "react";
import styles from "./HeroHud.module.scss";
export type HeroHudProps = {
brand?: string;
title?: string;
desc?: string;
cta?: string;
readout?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function HeroHud({
brand = "SENTRY / ONLINE",
title = "놓치는 순간 없이 지켜보는 대시보드",
desc = "실시간으로 감지하고 기록합니다",
cta = "무료로 시작",
readout = "SIG",
duration = 2000,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#ffd23f",
}: HeroHudProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.hero} style={vars}>
<span className={`${styles.corner} ${styles.cornerTl}`} aria-hidden="true" />
<span className={`${styles.corner} ${styles.cornerTr}`} aria-hidden="true" />
<span className={`${styles.corner} ${styles.cornerBl}`} aria-hidden="true" />
<span className={`${styles.corner} ${styles.cornerBr}`} aria-hidden="true" />
<div className={styles.scan} aria-hidden="true" />
<p className={styles.eyebrow}>
<span className={styles.dot} aria-hidden="true" />
{brand}
</p>
<h1 className={styles.title}>{title}</h1>
<p className={styles.desc}>{desc}</p>
<button type="button" className={styles.cta}>{cta}</button>
<p className={styles.readoutLine}>
{readout} <span className={styles.cursor} aria-hidden="true" />
</p>
</section>
);
}01 Lock-on hero — Measured area 48.094% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · HUD Interface Design: 9 UIs That Watch You Back
The post this demo lives in
HUD Interface Design: 9 UIs That Watch You BackHUD interface design borrows the targeting reticles and radar overlays from sci-fi movies and games, laying corner brack See all demos in this post


