Skip to content

Type Foundry Hero

Typography hero section with no images: a giant specimen word whose letters swell in weight under the pointer.

  • Heroes
  • Type & publishing
  • React only · 0 deps
  • 1 file
Full screen

Browser support · All evergreen browsers. Weight interpolates continuously on variable system fonts (SF Pro, Segoe UI Variable) and steps on static ones.

Install. One command, or copy the file.

$npx shadcn@latest add https://freedesignmd.com/r/hero-type-only.json
  • Byte-identical to the preview
  • No Tailwind or config needed
  • SSR-safe · keyboard accessible
  • Respects reduced motion & transparency
components/heroes/hero-type-only.tsx
1/**
2 * HeroTypeOnly: a purely typographic hero for a type foundry release. No
3 * images. A giant specimen word whose letters swell in weight under the
4 * pointer (font-weight interpolation, continuous on variable system fonts
5 * such as SF Pro, stepped on static families), a weight ruler that tracks the
6 * pointer, a live axis readout and a specimen grid of family metadata set on
7 * hairline rules. Strictly black and white.
8 *
9 * Props: word (specimen word), release, family, meta (label/value pairs),
10 * sample (pangram line), price, nav, onTrial.
11 * CSS vars: --fd-tyo-ink, --fd-tyo-paper, --fd-tyo-size.
12 *
13 * Browser support: all evergreen browsers. Pointer Events (mouse, pen and
14 * touch drag across the word). When idle the weight sweeps slowly by itself.
15 * Motion: one rAF loop eases the pointer with a spring (k 380, c 30), paused
16 * off-screen and in hidden tabs. prefers-reduced-motion shows a static
17 * 100 to 900 weight ramp across the word and no idle sweep.
18 * Accessibility: the word is a real heading with an aria-label; per-letter
19 * spans are aria-hidden. The weight ruler is decorative; the readout is text.
20 */
21import { useEffect, useRef, useState } from "react";
22import type { ReactNode } from "react";
23
24export interface HeroTypeOnlyMeta {
25 label: string;
26 value: ReactNode;
27}
28
29export interface HeroTypeOnlyProps {
30 word?: string;
31 release?: string;
32 family?: string;
33 meta?: HeroTypeOnlyMeta[];
34 sample?: string;
35 price?: string;
36 nav?: { label: string; href: string }[];
37 trialHref?: string;
38 buyHref?: string;
39}
40
41const DEFAULT_META: HeroTypeOnlyMeta[] = [
42 { label: "Family", value: "Brume Grotesk" },
43 { label: "Styles", value: "18, nine weights with italics" },
44 { label: "Axes", value: "wght 100 to 900, opsz 8 to 144" },
45 { label: "Glyphs", value: "1,214 per style" },
46 { label: "Scripts", value: "Latin, Cyrillic, Greek, 219 languages" },
47 { label: "Drawn by", value: "Ines Vogt, Basel, 2023 to 2026" },
48];
49
50const DEFAULT_NAV = [
51 { label: "Typefaces", href: "#typefaces" },
52 { label: "Trials", href: "#trials" },
53 { label: "Licensing", href: "#licensing" },
54 { label: "Journal", href: "#journal" },
55];
56
57const W_MIN = 100;
58const W_MAX = 900;
59
60export function HeroTypeOnly({
61 word = "Brume",
62 release = "Release No. 14",
63 family = "Brume Grotesk",
64 meta = DEFAULT_META,
65 sample = "Fog lifts off the Rhine at 07:40; the 8 tram to Kleinhüningen is quietly, reliably late.",
66 price = "Family from CHF 480",
67 nav = DEFAULT_NAV,
68 trialHref = "#trial",
69 buyHref = "#buy",
70}: HeroTypeOnlyProps) {
71 const letters = Array.from(word);
72 const rootRef = useRef<HTMLElement>(null);
73 const wordRef = useRef<HTMLHeadingElement>(null);
74 const spanRefs = useRef<(HTMLSpanElement | null)[]>([]);
75 const markRef = useRef<HTMLSpanElement>(null);
76 const [readout, setReadout] = useState({ w: 500, x: 0.5, live: false });
77 const [menu, setMenu] = useState(false);
78
79 useEffect(() => {
80 const root = rootRef.current;
81 const wordEl = wordRef.current;
82 if (!root || !wordEl) return;
83 const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
84 const spans = spanRefs.current;
85 const n = letters.length;
86
87 if (reduce) {
88 spans.forEach((s, i) => {
89 if (!s) return;
90 const w = Math.round(W_MIN + ((W_MAX - W_MIN) * i) / Math.max(1, n - 1));
91 s.style.fontWeight = String(w);
92 });
93 return;
94 }
95
96 // letter centres as fractions of the word width, measured at rest
97 let centres: number[] = [];
98 const measure = () => {
99 const r = wordEl.getBoundingClientRect();
100 centres = spans.map((s) => {
101 if (!s || r.width === 0) return 0.5;
102 const b = s.getBoundingClientRect();
103 return (b.left + b.width / 2 - r.left) / r.width;
104 });
105 };
106 measure();
107 const ro = new ResizeObserver(measure);
108 ro.observe(wordEl);
109
110 let target = 0.5;
111 let pos = 0.5;
112 let vel = 0;
113 let lastMove = -1e9;
114 let raf = 0;
115 let last = performance.now();
116 let visible = true;
117 let lastReport = 0;
118 const t0 = last;
119
120 const onMove = (e: PointerEvent) => {
121 const r = wordEl.getBoundingClientRect();
122 if (r.width === 0) return;
123 target = Math.min(1.08, Math.max(-0.08, (e.clientX - r.left) / r.width));
124 lastMove = performance.now();
125 };
126 root.addEventListener("pointermove", onMove);
127 root.addEventListener("pointerdown", onMove);
128
129 const frame = (now: number) => {
130 raf = 0;
131 const dt = Math.min(0.032, (now - last) / 1000);
132 last = now;
133 const idle = now - lastMove > 2600;
134 if (idle) target = 0.5 + 0.56 * Math.sin((now - t0) / 2400);
135 // spring k 380, c 30 (softer when idle)
136 const k = idle ? 60 : 380;
137 const c = idle ? 16 : 30;
138 vel += (k * (target - pos) - c * vel) * dt;
139 pos += vel * dt;
140
141 const sigma = 0.2;
142 let focusW = W_MIN;
143 for (let i = 0; i < n; i++) {
144 const s = spans[i];
145 if (!s) continue;
146 const d = (centres[i] ?? 0.5) - pos;
147 const f = Math.exp(-(d * d) / (2 * sigma * sigma));
148 const w = W_MIN + (W_MAX - W_MIN) * f;
149 s.style.fontWeight = w.toFixed(0);
150 if (w > focusW) focusW = w;
151 }
152 const mark = markRef.current;
153 const clamped = Math.min(1, Math.max(0, pos));
154 if (mark) mark.style.transform = `translateX(${(clamped * 100).toFixed(2)}cqw)`;
155 if (now - lastReport > 90) {
156 lastReport = now;
157 setReadout({ w: Math.round(focusW), x: clamped, live: !idle });
158 }
159 if (visible && !document.hidden) raf = requestAnimationFrame(frame);
160 };
161 const start = () => {
162 if (!raf && visible && !document.hidden) {
163 last = performance.now();
164 raf = requestAnimationFrame(frame);
165 }
166 };
167 const io = new IntersectionObserver(([e]) => {
168 visible = e.isIntersecting;
169 if (visible) start();
170 });
171 io.observe(root);
172 const onVis = () => start();
173 document.addEventListener("visibilitychange", onVis);
174 start();
175 return () => {
176 cancelAnimationFrame(raf);
177 ro.disconnect();
178 io.disconnect();
179 document.removeEventListener("visibilitychange", onVis);
180 root.removeEventListener("pointermove", onMove);
181 root.removeEventListener("pointerdown", onMove);
182 };
183 }, [word]); // eslint-disable-line react-hooks/exhaustive-deps
184
185 const ticks = [100, 200, 300, 400, 500, 600, 700, 800, 900];
186
187 return (
188 <section ref={rootRef} className="fd-tyo" aria-labelledby="fd-tyo-word">
189 <style>{css}</style>
190 <header className="fd-tyo-nav">
191 <a className="fd-tyo-logo" href="#top" aria-label="Kessler and Vogt, home">
192 <span className="fd-tyo-logo-k">K</span>
193 <span className="fd-tyo-logo-amp">&amp;</span>
194 <span className="fd-tyo-logo-v">V</span>
195 <span className="fd-tyo-logo-word">Kessler &amp; Vogt<br />Type Foundry, Basel</span>
196 </a>
197 <nav className={`fd-tyo-links${menu ? " is-open" : ""}`} aria-label="Primary" id="fd-tyo-menu">
198 {nav.map((l, i) => (
199 <a key={l.href} href={l.href} className="fd-tyo-link">
200 <span className="fd-tyo-link-n">{String(i + 1).padStart(2, "0")}</span>
201 {l.label}
202 </a>
203 ))}
204 </nav>
205 <a className="fd-tyo-cart" href="#cart">Cart (0)</a>
206 <button
207 type="button"
208 className="fd-tyo-burger"
209 aria-expanded={menu}
210 aria-controls="fd-tyo-menu"
211 onClick={() => setMenu((m) => !m)}
212 >
213 {menu ? "Close" : "Menu"}
214 </button>
215 </header>
216
217 <div className="fd-tyo-strip">
218 <p className="fd-tyo-release">
219 <span className="fd-tyo-dot" aria-hidden="true" />
220 {release}
221 <span className="fd-tyo-sep">/</span>
222 Out 11.11.2026
223 </p>
224 <p className="fd-tyo-readout" aria-live="off">
225 <span>wght</span>
226 <b>{String(readout.w).padStart(3, "0")}</b>
227 <span>x</span>
228 <b>{readout.x.toFixed(2)}</b>
229 <span className="fd-tyo-state">{readout.live ? "pointer" : "auto"}</span>
230 </p>
231 </div>
232
233 <h1 ref={wordRef} id="fd-tyo-word" className="fd-tyo-word" aria-label={`${word}, a new typeface by Kessler and Vogt`}>
234 {letters.map((ch, i) => (
235 <span
236 key={i}
237 aria-hidden="true"
238 className="fd-tyo-ch"
239 ref={(el) => {
240 spanRefs.current[i] = el;
241 }}
242 >
243 {ch}
244 </span>
245 ))}
246 </h1>
247
248 <div className="fd-tyo-ruler" aria-hidden="true">
249 <div className="fd-tyo-ruler-track">
250 {ticks.map((t) => (
251 <span key={t} className={`fd-tyo-tick${t === 900 ? " is-end" : ""}`}>
252 <i />
253 {t}
254 </span>
255 ))}
256 <span ref={markRef} className="fd-tyo-mark" />
257 </div>
258 </div>
259
260 <dl className="fd-tyo-meta">
261 {meta.map((m, i) => (
262 <div key={m.label} className="fd-tyo-cell">
263 <dt className="fd-tyo-dt">
264 <span>{String(i + 1).padStart(2, "0")}</span>
265 {m.label}
266 </dt>
267 <dd className="fd-tyo-dd">{m.value}</dd>
268 </div>
269 ))}
270 </dl>
271
272 <div className="fd-tyo-foot">
273 <p className="fd-tyo-sample">
274 <span className="fd-tyo-sample-tag">{family} Book, 32/36</span>
275 {sample}
276 </p>
277 <div className="fd-tyo-actions">
278 <p className="fd-tyo-price">{price}<br />Desktop, web and app licences</p>
279 <a className="fd-tyo-btn fd-tyo-btn-solid" href={buyHref}>
280 Buy the family
281 <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M3 8h9M8.5 4l4 4-4 4" fill="none" stroke="currentColor" strokeWidth="1.5" /></svg>
282 </a>
283 <a className="fd-tyo-btn" href={trialHref}>Free trial fonts</a>
284 </div>
285 </div>
286 </section>
287 );
288}
289
290const css = `
291.fd-tyo, .fd-tyo *, .fd-tyo *::before, .fd-tyo *::after { box-sizing: border-box; }
292.fd-tyo :where(h1, p, a, dl, dt, dd, div, span, b, i, nav, header, button) {
293 margin: 0; padding: 0; border: 0; background: none; font: inherit; color: inherit;
294 letter-spacing: inherit; text-decoration: none; text-transform: none;
295}
296.fd-tyo {
297 --fd-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
298 --fd-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
299 --fd-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
300 --fd-dur-1: 120ms; --fd-dur-2: 240ms; --fd-dur-3: 420ms; --fd-dur-4: 700ms;
301 --fd-tyo-ink: #0A0A0A;
302 --fd-tyo-paper: #FFFFFF;
303 --fd-tyo-size: clamp(120px, 31vw, 470px);
304 --fd-tyo-sans: "SF Pro Display", ui-sans-serif, -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif;
305 --fd-tyo-serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
306 --fd-tyo-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;
307 position: relative; display: flex; flex-direction: column; margin: 0; padding: 0 40px 36px;
308 min-height: 880px; background: var(--fd-tyo-paper); color: var(--fd-tyo-ink);
309 font: 400 15px/1.4 var(--fd-tyo-sans); letter-spacing: 0; overflow: hidden;
310 touch-action: pan-y;
311}
312
313/* nav */
314.fd-tyo-nav {
315 position: relative; z-index: 3; display: grid; grid-template-columns: 1fr auto auto; align-items: center; gap: 40px;
316 padding: 22px 0 20px; border-bottom: 1px solid var(--fd-tyo-ink);
317}
318.fd-tyo-logo { display: inline-flex; align-items: center; gap: 0; color: var(--fd-tyo-ink); text-decoration: none; }
319.fd-tyo-logo-k, .fd-tyo-logo-v { font: 800 28px/1 var(--fd-tyo-sans); letter-spacing: -0.04em; }
320.fd-tyo-logo-amp { font: italic 400 30px/1 var(--fd-tyo-serif); margin: 0 1px; transform: translateY(1px); display: inline-block; }
321.fd-tyo-logo-word {
322 margin-left: 14px; padding-left: 14px; border-left: 1px solid var(--fd-tyo-ink);
323 font: 500 12px/1.25 var(--fd-tyo-mono); letter-spacing: 0.02em; text-transform: uppercase;
324}
325.fd-tyo-links { display: flex; gap: 28px; }
326.fd-tyo-link { position: relative; display: inline-flex; align-items: baseline; gap: 6px; font: 500 15px/1.2 var(--fd-tyo-sans); color: var(--fd-tyo-ink); text-decoration: none; }
327.fd-tyo-link-n { font: 400 11px/1 var(--fd-tyo-mono); }
328.fd-tyo-link::after {
329 content: ""; position: absolute; left: 0; right: 0; bottom: -4px; height: 1px; background: currentColor;
330 transform: scaleX(0); transform-origin: right; transition: transform var(--fd-dur-2) var(--fd-ease-out);
331}
332.fd-tyo-link:hover::after { transform: scaleX(1); transform-origin: left; }
333.fd-tyo-cart { font: 500 13px/1 var(--fd-tyo-mono); padding: 10px 14px; border: 1px solid var(--fd-tyo-ink); border-radius: 9999px; color: var(--fd-tyo-ink); transition: background var(--fd-dur-1), color var(--fd-dur-1); }
334.fd-tyo-cart:hover { background: var(--fd-tyo-ink); color: var(--fd-tyo-paper); }
335.fd-tyo-burger { display: none; appearance: none; cursor: pointer; font: 500 13px/1 var(--fd-tyo-mono); padding: 10px 14px; border: 1px solid var(--fd-tyo-ink); border-radius: 9999px; color: var(--fd-tyo-ink); background: var(--fd-tyo-paper); }
336
337/* strip */
338.fd-tyo-strip { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 16px 0 0; }
339.fd-tyo-release, .fd-tyo-readout { display: flex; align-items: center; gap: 10px; font: 500 12px/1.2 var(--fd-tyo-mono); letter-spacing: 0.04em; text-transform: uppercase; }
340.fd-tyo-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fd-tyo-ink); display: inline-block; }
341.fd-tyo-sep { opacity: 0.4; }
342.fd-tyo-readout b { font-weight: 700; min-width: 3.4ch; display: inline-block; font-variant-numeric: tabular-nums; }
343.fd-tyo-state { padding: 3px 7px; border: 1px solid var(--fd-tyo-ink); border-radius: 9999px; font-size: 10px; }
344
345/* word */
346.fd-tyo-word {
347 display: flex; justify-content: flex-start; align-items: baseline; margin: 0 0 0 -0.04em; padding: 0;
348 font: 400 var(--fd-tyo-size)/0.86 var(--fd-tyo-sans); letter-spacing: -0.035em; color: var(--fd-tyo-ink);
349 white-space: nowrap; user-select: none; cursor: ew-resize; flex: 1 0 auto; align-content: center;
350 padding-top: clamp(16px, 4vw, 40px);
351}
352.fd-tyo-ch { display: inline-block; font-weight: 400; font-variation-settings: normal; }
353
354/* ruler */
355.fd-tyo-ruler { margin-top: clamp(14px, 2vw, 26px); border-top: 1px solid var(--fd-tyo-ink); container-type: inline-size; }
356.fd-tyo-ruler-track { position: relative; display: flex; justify-content: space-between; height: 34px; }
357.fd-tyo-tick { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 6px; font: 400 10px/1 var(--fd-tyo-mono); width: 0; overflow: visible; white-space: nowrap; }
358.fd-tyo-tick i { display: block; width: 1px; height: 8px; background: var(--fd-tyo-ink); }
359.fd-tyo-tick.is-end { align-items: flex-end; width: auto; }
360.fd-tyo-mark {
361 position: absolute; left: 0; top: -5px; width: 0; height: 0;
362 border-left: 5px solid transparent; border-right: 5px solid transparent; border-top: 7px solid var(--fd-tyo-ink);
363 margin-left: -5px; transform: translateX(50cqw);
364}
365
366/* meta */
367.fd-tyo-meta { display: grid; grid-template-columns: repeat(6, 1fr); border-top: 1px solid var(--fd-tyo-ink); border-bottom: 1px solid var(--fd-tyo-ink); margin: 6px 0 0; }
368.fd-tyo-cell { padding: 14px 16px 16px 0; border-right: 1px solid var(--fd-tyo-ink); margin-right: 16px; }
369.fd-tyo-cell:last-child { border-right: 0; margin-right: 0; }
370.fd-tyo-dt { display: flex; gap: 8px; font: 500 11px/1.2 var(--fd-tyo-mono); letter-spacing: 0.04em; text-transform: uppercase; margin-bottom: 10px; }
371.fd-tyo-dt span { opacity: 0.45; }
372.fd-tyo-dd { font: 400 15px/1.35 var(--fd-tyo-sans); letter-spacing: -0.005em; }
373
374/* foot */
375.fd-tyo-foot { display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 40px; padding-top: 26px; }
376.fd-tyo-sample { font: 400 clamp(1.375rem, 2.3vw, 2rem)/1.12 var(--fd-tyo-sans); letter-spacing: -0.02em; max-width: 30ch; }
377.fd-tyo-sample-tag { display: block; font: 500 11px/1 var(--fd-tyo-mono); letter-spacing: 0.04em; text-transform: uppercase; margin-bottom: 12px; }
378.fd-tyo-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: flex-end; }
379.fd-tyo-price { font: 400 12px/1.4 var(--fd-tyo-mono); margin-right: 12px; text-align: right; }
380.fd-tyo-btn {
381 display: inline-flex; align-items: center; gap: 10px; height: 48px; padding: 0 22px; border-radius: 9999px;
382 border: 1px solid var(--fd-tyo-ink); color: var(--fd-tyo-ink); background: var(--fd-tyo-paper);
383 font: 600 15px/1 var(--fd-tyo-sans); text-decoration: none; white-space: nowrap;
384 transition: background var(--fd-dur-1), color var(--fd-dur-1), transform var(--fd-dur-2) var(--fd-spring);
385}
386.fd-tyo-btn svg { display: block; transition: transform var(--fd-dur-2) var(--fd-ease-out); }
387.fd-tyo-btn:hover { background: var(--fd-tyo-ink); color: var(--fd-tyo-paper); }
388.fd-tyo-btn:hover svg { transform: translateX(3px); }
389.fd-tyo-btn:active { transform: scale(0.96); transition-duration: var(--fd-dur-1); }
390.fd-tyo-btn-solid { background: var(--fd-tyo-ink); color: var(--fd-tyo-paper); }
391.fd-tyo-btn-solid:hover { background: var(--fd-tyo-paper); color: var(--fd-tyo-ink); }
392.fd-tyo :focus-visible { outline: 2px solid var(--fd-tyo-ink); outline-offset: 3px; }
393
394@media (max-width: 1100px) {
395 .fd-tyo-meta { grid-template-columns: repeat(3, 1fr); }
396 .fd-tyo-cell:nth-child(3n) { border-right: 0; margin-right: 0; }
397 .fd-tyo-cell:nth-child(-n + 3) { border-bottom: 1px solid var(--fd-tyo-ink); }
398}
399@media (max-width: 760px) {
400 .fd-tyo { padding: 0 16px 24px; min-height: 0; --fd-tyo-size: 26vw; }
401 .fd-tyo-nav { grid-template-columns: 1fr auto; gap: 12px; padding: 16px 0; }
402 .fd-tyo-logo-word { font-size: 10px; margin-left: 10px; padding-left: 10px; }
403 .fd-tyo-cart { display: none; }
404 .fd-tyo-burger { display: inline-block; }
405 .fd-tyo-links {
406 display: none; position: absolute; left: 0; right: 0; top: 100%; flex-direction: column; gap: 0;
407 background: var(--fd-tyo-paper); border-bottom: 1px solid var(--fd-tyo-ink);
408 }
409 .fd-tyo-links.is-open { display: flex; }
410 .fd-tyo-link { padding: 16px 0; border-top: 1px solid var(--fd-tyo-ink); font-size: 24px; letter-spacing: -0.02em; gap: 12px; }
411 .fd-tyo-link::after { display: none; }
412 .fd-tyo-strip { flex-direction: column; align-items: flex-start; gap: 8px; }
413 .fd-tyo-word { padding-top: 28px; padding-bottom: 6px; flex: 0 0 auto; }
414 .fd-tyo-tick { font-size: 9px; }
415 .fd-tyo-tick:nth-child(even) { color: transparent; }
416 .fd-tyo-meta { grid-template-columns: 1fr 1fr; }
417 .fd-tyo-cell, .fd-tyo-cell:nth-child(3n) { border-right: 0; margin-right: 0; border-bottom: 1px solid var(--fd-tyo-ink); padding: 12px 12px 12px 0; }
418 .fd-tyo-cell:nth-child(odd) { border-right: 1px solid var(--fd-tyo-ink); margin-right: 12px; }
419 .fd-tyo-cell:nth-last-child(-n + 2) { border-bottom: 0; }
420 .fd-tyo-dd { font-size: 14px; }
421 .fd-tyo-foot { grid-template-columns: 1fr; gap: 22px; padding-top: 22px; }
422 .fd-tyo-actions { justify-content: flex-start; }
423 .fd-tyo-price { text-align: left; width: 100%; margin: 0; }
424 .fd-tyo-btn { flex: 1 1 auto; justify-content: center; }
425}
426@media (prefers-reduced-motion: reduce) {
427 .fd-tyo-link::after, .fd-tyo-btn, .fd-tyo-btn svg { transition: none; }
428}
429`;
430
431export function Demo() {
432 return <HeroTypeOnly />;
433}

More Heroes. Same file contract.

Ledger Card Hero, heroes preview: Fintech hero section with a CSS 3D metal card that tilts to the cursor, then flips and docks on scroll as…

Ledger Card Hero

Fintech hero section with a CSS 3D metal card that tilts to the cursor, then flips and docks on scroll as stats count up.

Agent Terminal Hero, heroes preview: AI hero section with a terminal that types a prompt and streams an agent's plan while a dashboard assembles…

Agent Terminal Hero

AI hero section with a terminal that types a prompt and streams an agent's plan while a dashboard assembles beside it.

Maison Editorial Hero, heroes preview: Editorial fashion hero section: a giant serif masthead over three campaign photos, revealed by clip-path wipes

Maison Editorial Hero

Editorial fashion hero section: a giant serif masthead over three campaign photos, revealed by clip-path wipes.

Questions about Type Foundry Hero

Run npx shadcn@latest add https://freedesignmd.com/r/hero-type-only.json, or open the page and copy the file into your project. It has zero dependencies besides React. What you copy is the exact file that renders the live demo.

Typography hero section with no images: a giant specimen word whose letters swell in weight under the pointer. It is a free, MIT-licensed React heroes component from the freedesignmd library.

Yes. It is free and MIT-licensed, use, modify and ship it in personal and commercial projects without attribution.

All evergreen browsers. Weight interpolates continuously on variable system fonts (SF Pro, Segoe UI Variable) and steps on static ones.

Yes. It is a plain React component with its own styles, so it runs in Next.js, Vite, Remix and Lovable projects, and it needs no Tailwind setup. It has zero dependencies besides React.