Waves <mv-waves>
하나의 캔버스에 그리는 물결 선입니다. 원근으로 본 등고선의 바다가 흐릿한 수평선에서 밀려오며 가까워질수록 부풀어 오르거나, 가는 실로 이루어진 몇 개의 꼬인 리본이 됩니다.
작동 방식
하나의 캔버스에 그리는 물결 선입니다. 원근으로 본 등고선의 바다가 흐릿한 수평선에서 밀려오며 가까워질수록 부풀어 오르거나, 가는 실로 이루어진 몇 개의 꼬인 리본이 됩니다. interactive를 켜면 포인터 아래에서 선이 부드럽게 솟아오르고, 포인터가 움직이거나 누르는 곳에 물결이 퍼집니다. 색상은 CSS 변수에서 가져와 테마를 따르며, 루프는 화면 밖에서 쉬고, 동작 줄이기 설정에서는 정지 프레임을 보여 줍니다.
| 카테고리 | 배경 |
|---|---|
| 유형 | Web Component (<mv-waves>) |
| 상태 | 안정 |
| Keywords | safe-rewrite, waves, lines, ribbons, noise, perspective, ripple, interactive, hero, canvas |
When to use
- A SaaS or product hero needs a calm sea of moving lines behind the headline, in light or dark themes
- Visitors should feel the page respond: the lines swell under the pointer and ripple where it moves or taps
- Flowing lines or twisting ribbons are wanted from one 2D canvas, without a WebGL context
Avoid when
- A still wave pattern is enough and nothing should move → use Backdrop instead
설치
node scripts/add.mjs waves --out ./src/marvelousMarvelous UI MCP 서버를 사용하는 AI 에이전트: install_components({ slugs: ["waves"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
복사되는 파일(의존성 포함): tokens/tokens.css, core/base.css, core/canvas.js, core/dom.js, core/element.js, core/i18n.js, core/motion.js, core/pointer.js, components/waves/waves.js, components/waves/waves.css.
사용법
기본 마크업입니다. 여기서 시작해 속성, data-*, CSS 변수로 커스터마이즈하세요:
<div style="display:flex;flex-wrap:wrap;min-height:320px">
<section style="flex:1.6 1 22rem;position:relative;display:grid;place-items:center;text-align:center;overflow:hidden;padding:2rem 1.5rem">
<mv-waves data-fill interactive></mv-waves>
<div style="position:relative">
<p style="margin:0;font:700 clamp(1.6rem,4vw,2.6rem)/1.1 var(--mv-font-sans);letter-spacing:-.03em">Ride the next wave</p>
<p style="margin:.6rem auto 1.2rem;max-width:30rem;color:var(--mv-fg-muted)">Lines roll in from the horizon. Move your cursor to send ripples across them.</p>
<a class="mv-button" href="#waves">Get started</a>
</div>
</section>
<section style="flex:1 1 14rem;position:relative;display:grid;place-items:center;text-align:center;overflow:hidden;border-left:1px solid var(--mv-border);min-height:220px;padding:1.5rem">
<mv-waves data-fill variant="ribbons" interactive></mv-waves>
<div style="position:relative">
<strong style="display:block;font-size:1.1rem">Ribbons</strong>
<span style="font-size:.85rem;color:var(--mv-fg-muted)">variant="ribbons"</span>
</div>
</section>
</div>API
Attributes
| Name | 유형 | Default | Description |
|---|---|---|---|
variant | lines | ribbons | lines | A sea of lines in perspective, or twisting ribbons made of strands. |
count | number | 44 (lines) / 3 (ribbons) | Number of lines on the sea (2 to 80) or of ribbons (1 to 8). |
colors | list of CSS colors | --mv-waves-1, -2, -3 | Comma-separated palette (tokens and var() accepted, follows the theme). Lines run through it from left to right, ribbon strands from one edge to the other. |
amplitude | number | 1 | Swell height (0 flattens the lines). |
speed | number | 1 | Speed multiplier for the flow and the ripples. |
interactive | boolean | The lines rise in a soft swell under the pointer, moving it sends ripples and a press sends a stronger one (listens on the parent with data-fill). | |
radius | number | 220 | Pointer reach in px: how far a ripple travels before it dies out, and the size of the swell. |
data-fill | boolean | Fills the positioned parent (absolute, inset 0) and lets the pointer through. |
Content structure
| Name | Description |
|---|---|
children | Without data-fill, content placed inside the element sits above the waves. |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-waves-1 / -2 / -3 | accent hue −40° / accent / +95° | Default palette, darker in light theme and lighter in dark theme. |
--mv-waves-weight | 1 | Line width multiplier. |
--mv-waves-haze | 1 | Strength of the glow along the horizon (lines variant, 0 removes it). |
--mv-waves-mask | none | Mask applied to the canvas, for example a fade on the sides. |
Accessibility
Decorative: the canvas is aria-hidden and never takes pointer events, and content placed inside stays readable by assistive tech. With data-fill the whole element ignores the pointer; ripples come from pointer events on the parent and never block clicks. The loop stops off screen and in hidden tabs. Reduced motion (OS or [data-motion="reduce"]): one still frame, no swell and no ripples. Forced colors and print hide the canvas. Keep text readable over the lines: the far lines near the horizon are the faintest, so a centered headline sits well there.