Pixel Heading (Word)

PreviousNext

Whole-word pixel-font heading that swaps between two fonts or cycles through all five on hover. Uses Geist pixel fonts with zero animation dependencies.

Installation

pnpm dlx shadcn@latest add https://cult-ui.com/r/pixel-heading-word.json

Font Setup

This component requires the Geist Pixel fonts. Follow these steps to configure them in your project.

1. Register font variables in your root layout

Import the pixel font variants from geist/font/pixel and apply their CSS variable classes to <body>:

app/layout.tsx
import { GeistSans } from "geist/font/sans" import { GeistMono } from "geist/font/mono" import { GeistPixelSquare, GeistPixelGrid, GeistPixelCircle, GeistPixelTriangle, GeistPixelLine, } from "geist/font/pixel" export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body className={`${GeistSans.variable} ${GeistMono.variable} ${GeistPixelSquare.variable} ${GeistPixelGrid.variable} ${GeistPixelCircle.variable} ${GeistPixelTriangle.variable} ${GeistPixelLine.variable}`} > {children} </body> </html> ) }

Each import exposes a .variable property that injects a CSS custom property:

ImportCSS Variable
GeistSans--font-geist-sans
GeistMono--font-geist-mono
GeistPixelSquare--font-geist-pixel-square
GeistPixelGrid--font-geist-pixel-grid
GeistPixelCircle--font-geist-pixel-circle
GeistPixelTriangle--font-geist-pixel-triangle
GeistPixelLine--font-geist-pixel-line

2. Map CSS variables in your Tailwind CSS theme

Add the font mappings to your global CSS file so the font-pixel-* utility classes resolve correctly:

globals.css (Tailwind v4)
@theme { --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); --font-pixel-square: var(--font-geist-pixel-square); --font-pixel-grid: var(--font-geist-pixel-grid); --font-pixel-circle: var(--font-geist-pixel-circle); --font-pixel-triangle: var(--font-geist-pixel-triangle); --font-pixel-line: var(--font-geist-pixel-line); }

For Tailwind v3, use the extend key in tailwind.config.ts:

tailwind.config.ts (Tailwind v3)
export default { theme: { extend: { fontFamily: { sans: ["var(--font-geist-sans)"], mono: ["var(--font-geist-mono)"], "pixel-square": ["var(--font-geist-pixel-square)"], "pixel-grid": ["var(--font-geist-pixel-grid)"], "pixel-circle": ["var(--font-geist-pixel-circle)"], "pixel-triangle": ["var(--font-geist-pixel-triangle)"], "pixel-line": ["var(--font-geist-pixel-line)"], }, }, }, }

Usage

import { PixelHeading } from "@/components/ui/pixel-heading-word"

Swap Mode

Set initialFont and hoverFont to swap between two specific fonts on hover:

<PixelHeading initialFont="square" hoverFont="circle" className="text-6xl"> Swap on hover </PixelHeading>

Cycle Mode

Omit hoverFont to cycle through every pixel font on hover:

<PixelHeading initialFont="square" className="text-6xl"> Cycle on hover </PixelHeading>

Custom Cycle Speed

Control how fast fonts cycle in cycle mode:

<PixelHeading cycleInterval={150} className="text-6xl"> Fast cycle </PixelHeading>

Show Font Label

Display the current font name beneath the heading:

<PixelHeading showLabel className="text-6xl"> With label </PixelHeading>

Custom Heading Level

Render as any heading element (h1h6):

<PixelHeading as="h3" className="text-4xl"> H3 heading </PixelHeading>

Font Change Callback

Listen for font index changes:

<PixelHeading onFontIndexChange={(index) => console.log("Font index:", index)} className="text-6xl" > With callback </PixelHeading>

API Reference

PixelHeading Props

PropTypeDefaultDescription
as"h1" | "h2" | "h3" | "h4" | "h5" | "h6""h1"HTML heading level to render
initialFont"square" | "grid" | "circle" | "triangle" | "line""square"The resting pixel font displayed by default
hoverFont"square" | "grid" | "circle" | "triangle" | "line"Font to show on hover. When set, swaps instead of cycling
cycleIntervalnumber300Interval in ms between font cycles on hover (cycle mode only)
defaultFontIndexnumber0Initial font index (0–4). Ignored when initialFont is set
showLabelbooleanfalseShow the active font name label beneath the heading
onFontIndexChange(index: number) => voidCallback fired when the active font changes
classNamestringAdditional CSS classes applied to the heading element

Swap Mode vs Cycle Mode

BehaviorSwap ModeCycle Mode
ConfigurationSet both initialFont and hoverFontSet initialFont only (omit hoverFont)
On hoverInstantly swaps to hoverFontCycles through all 5 fonts at cycleInterval speed
On leaveReturns to initialFontStops cycling, stays on last font
KeyboardNo keyboard step (instant swap)Enter/Space advances one font

Available Pixel Fonts

Font NameTailwind ClassCSS Variable
squarefont-pixel-square--font-geist-pixel-square
gridfont-pixel-grid--font-geist-pixel-grid
circlefont-pixel-circle--font-geist-pixel-circle
trianglefont-pixel-triangle--font-geist-pixel-triangle
linefont-pixel-line--font-geist-pixel-line

Features

  • Two interaction modes — swap between two fonts or cycle through all five
  • Whole-word animation — the entire heading changes font at once for a bold effect
  • Accessible — keyboard support (Enter/Space to step), focus management, and aria-live label
  • Zero animation dependencies — uses CSS transitions only, no motion library needed
  • Composable — renders as any heading level with full className support

Pixel Heading (Character) vs Pixel Heading (Word)

FeatureCharacter variantWord variant
Animation granularityEach character animates independentlyWhole heading changes at once
Modesuniform, multi, wave, randomswap, cycle
Auto-playYesNo (hover/focus only)
Prefix supportYesNo
Character isolationYesNo
Stagger controlYesN/A
Best forHero text, large display headingsButtons, labels, smaller headings

Notes

  • The component uses five Geist pixel font variants: Square, Grid, Circle, Triangle, and Line
  • In swap mode, the transition between fonts uses a CSS transition-all duration-150 for smoothness
  • In cycle mode, fonts advance every cycleInterval ms while the heading is hovered or focused
  • Keyboard users can step through fonts one at a time with Enter or Space (cycle mode only)
  • The showLabel feature uses an <output> element with aria-live="polite" for screen reader announcements