Motion
Reveal
A headline line rising out of its own baseline.
betav0.1.0 Chromium 103 ui-scale safe
Install
Copies the file into
components/ds/, with its registry dependencies and npm packages. See Installation for the one-time setup.bunx shadcn@3 add @fastrp/revealExamples
Default
Şehir uyanıyor.Sen hazır mısın?
Şehir uyanıyor.
Sen hazır mısın?
API
<Reveal />A line that rises out of its own baseline — the headline entrance. The clip is the wrapper's own box, so the travel is a percentage of the line and needs no pixel length. Under reduced motion it fades in where it stands. The wrapper is what is watched, not the line: the line starts fully clipped by the wrapper's `overflow: hidden`, and an IntersectionObserver counts a clipped element as never visible.
| Prop | Type | Default | Notes |
|---|---|---|---|
| as | "div" | "span" | div | The element the line is set in. |
| className | string | — | |
| delay | number | 0 | Seconds. |
Dependencies
npm
- @fastrp/design-tokens
- motion@^12.40.0
Registry
- @fastrp/scroll-root
- @fastrp/utils
Source
components/ds/reveal.tsxShowHide
'use client';
import { DURATION_S, EASE } from '@fastrp/design-tokens/motion';
import { motion, useInView, useReducedMotion } from 'motion/react';
import type * as React from 'react';
import { useRef } from 'react';
import { useScrollRoot } from '@/components/ds/scroll-root';
import { cn } from '@/lib/utils';
// @fast-ds [email protected]
export type RevealProps = {
children: React.ReactNode;
/** Seconds. */
delay?: number;
className?: string;
/** The element the line is set in. */
as?: 'div' | 'span';
};
/**
* A line that rises out of its own baseline — the headline entrance. The clip is the wrapper's
* own box, so the travel is a percentage of the line and needs no pixel length. Under reduced
* motion it fades in where it stands.
*
* The wrapper is what is watched, not the line: the line starts fully clipped by the wrapper's
* `overflow: hidden`, and an IntersectionObserver counts a clipped element as never visible.
*/
function Reveal({ children, delay = 0, className, as = 'div' }: RevealProps) {
const reduceMotion = useReducedMotion();
const root = useScrollRoot();
const ref = useRef<HTMLElement>(null);
const inView = useInView(ref, { once: true, root });
const Wrapper = as as 'div';
const Inner = as === 'span' ? motion.span : motion.div;
return (
<Wrapper
ref={ref as React.RefObject<HTMLDivElement>}
data-slot="reveal"
className={cn('block overflow-hidden', className)}
>
<Inner
className="block"
initial={{ y: reduceMotion ? '0%' : '105%', opacity: reduceMotion ? 0 : 1 }}
animate={inView ? { y: '0%', opacity: 1 } : undefined}
transition={{ duration: DURATION_S.slower, delay, ease: EASE }}
>
{children}
</Inner>
</Wrapper>
);
}
export { Reveal };