Lucent Wash LogoLucent Wash

Animated Stack

Smoothly animate items in and out of a vertical stack

AnimatedItem

Use AnimatedItem to wrap content that should animate in/out

This content stays in place while the above animates.

<AnimatedItem visible={false}>
<div>Your content</div>
</AnimatedItem>

AnimatedStack

Use AnimatedStack to manage multiple items

Stack Item 1

Click the buttons above to toggle visibility

Stack Item 2

Click the buttons above to toggle visibility

<AnimatedStack
gap={12}
items={[
{ key: "1", visible: true, content: ... },
{ key: "2", visible: false, content: ... },
]}
/>

Usage

import { AnimatedStack, AnimatedItem } from "@/components/AnimatedStack";

// Single item toggle
<AnimatedItem visible={isVisible}>
  <div>Content that animates in/out</div>
</AnimatedItem>

// Multiple items in a stack
<AnimatedStack
  gap={16}           // Gap between items (px)
  duration={0.3}     // Animation duration (seconds)
  items={[
    { key: "unique-id", visible: true, content: <div>...</div> },
  ]}
/>

Badges

Status and feature badges

Licensed Insured 5-Star Rated Eco-FriendlyNewPopular

Banners & Prompts

System status banners and action prompts

Impersonation Banner

Fixed amber banner shown when an admin is viewing the app as a customer. Displays customer info and a "Stop Viewing" button.

Viewing as:Sarah Johnson (sarah@example.com)
<ImpersonationBanner customerName="..." customerEmail="..." />Import from @/components/ImpersonationBanner

Passkey Setup Prompt

Bottom-sheet style toast that prompts users to set up a passkey. Has loading, success (animated check), error, and dismiss states. Auto-appears after 1.5s delay, dismissible for 1 or 7 days via localStorage.

Enable Faster Sign-in

Set up a passkey for instant access

Use Face ID, Touch ID, or your device's security to sign in faster next time. No more waiting for email codes!

<PasskeySetupPrompt onDismiss={fn} onComplete={fn} />Import from @/components/PasskeySetupPrompt

Buttons

All button variants, sizes, states, and border-radius tiers

Border Radius Tiers

All action buttons use .btn which sets border-radius: 0.75rem (12px, equivalent to rounded-xl). Use modifiers for other shapes.

Default — 12px

Action buttons, CTAs, form submits

Pill — 9999px

Tags, chips, toggle pills

Rounded — 16px

Card-style selection buttons

.btn → border-radius: 0.75rem (12px)
.btn.btn-pill → border-radius: 9999px
.btn.btn-rounded → border-radius: 1rem (16px)
Icon buttons → use rounded-full via Tailwind (circular)

Primary Button

Sizes

With Icons

States

Secondary Button

Sizes

With Icons

States

Ghost Button

Sizes

With Icons

Success Button

Sizes

With Icons

States

Icon Buttons

Cards

Card components for services, features, and testimonials

Service Cards

Residential

Interior & exterior cleaning

Commercial

Office buildings & high-rises

Pricing Cards

One-Time

$185

Monthly

$167

10% off

Most Popular

Bi-Weekly

$157

15% off

Weekly

$148

20% off

Testimonial Card

“Lucent Wash transformed our office building. The windows have never looked better. Professional, punctual, and perfect results!”

Sarah M.

Dallas, TX

Feature Cards

Licensed & Insured

Full coverage for peace of mind

Eco-Friendly

Safe for your family & pets

Flexible Scheduling

Book at your convenience

Charts

Data visualization components

MiniBarChart

A tiny 12-bar sparkline chart. Accepts an array of ISO date strings, auto-buckets them by month for the past 12 months, and renders proportional bars. Hover each bar for the month label and count.

Size Variations

96x24 (default)

160x40

240x64

Color Variations

bg-blue-400 (default)

bg-emerald-400

bg-amber-400

Empty State

No data — flat placeholder bars

import MiniBarChart from "@/components/MiniBarChart"; <MiniBarChart dates={["2026-01-10", "2026-02-01", ...]} // ISO date strings barColor="bg-blue-400" // Tailwind bg class width={96} // Chart width in px height={24} // Chart height in px />

Color Palette

Electric blue and tropical cyan create a fresh, clean aesthetic

Primary (Electric Blue)

50

#EFF6FF

100

#DBEAFE

200

#BFDBFE

300

#93C5FD

400

#60A5FA

500

#3B82F6

600

#2563EB

700

#1D4ED8

800

#1E40AF

900

#1E3A8A

950

#172554

Accent (Cyan / Water)

50

#ECFEFF

100

#CFFAFE

200

#A5F3FC

300

#67E8F9

400

#22D3EE

500

#06B6D4

600

#0891B2

700

#0E7490

800

#155E75

900

#164E63

950

#083344

Success (Emerald)

50

#ECFDF5

100

#D1FAE5

200

#A7F3D0

300

#6EE7B7

400

#34D399

500

#10B981

600

#059669

700

#047857

800

#065F46

900

#064E3B

950

#022C22

Warning (Amber)

50

#FFFBEB

100

#FEF3C7

200

#FDE68A

300

#FCD34D

400

#FBBF24

500

#F59E0B

600

#D97706

700

#B45309

800

#92400E

900

#78350F

950

#451A03

Error (Red)

50

#FEF2F2

100

#FEE2E2

200

#FECACA

300

#FCA5A5

400

#F87171

500

#EF4444

600

#DC2626

700

#B91C1C

800

#991B1B

900

#7F1D1D

950

#450A0A

Gradients

Hero Gradient

Button Gradient

Success Button Gradient

Water Gradient

Date & Time

Centralized formatters from @/lib/format — all date/time display should use these.

Date Formatters

formatDate()

Short date for tables & cards

Thu, Feb 19
formatDateLong()

Full date for headings & confirmations

Thursday, February 19, 2026
formatDateWithYear()

Short date + year for reviews & payments

Feb 19, 2026
formatDateFormal()

Long date without weekday for emails

February 19, 2026
formatMonthYear()

Calendar headers

February 2026

Time Formatters

formatTime

Time from ISO datetime

2:30 PM
formatScheduledTime("14:30")

HH:MM → 12-hour

2:30 PM
formatScheduledTime("morning")

Time window label

8 AM – 12 PM
formatScheduledTime("afternoon")

Time window label

12 – 4 PM
formatScheduledTime("flexible")

Time window label

Flexible
formatDateTime

Date + time for timestamps

Feb 19, 2:30 PM

Relative & Duration

formatRelativeDate("2026-02-25")

Admin table style (with color)

3 weeks ago
formatRelativeDate("2026-02-26")

Admin table style

3 weeks ago
formatRelativeDateLabel("2026-02-25")

Crew-facing simple label

Wed, Feb 25
formatRelativeDateLabel("2026-02-26")

Crew-facing simple label

Thu, Feb 26
formatRelativeDateLabel("2026-03-04")

Crew-facing (next week)

Wed, Mar 4
formatTimeAgo (5m)

Compact time-ago

24d ago
formatTimeAgo (2h)

Compact time-ago

24d ago
formatElapsed (45 min)

Elapsed duration

45 min
formatElapsed (1h 23m)

Elapsed duration

1h 23m
formatCountdown (2h 15m)

Countdown timer

2h 15m
formatCountdown (0)

Countdown at zero

Starting soon

Usage

import { formatDate, formatTime, formatElapsed } from "@/lib/format";

// In admin files, you can also import via the re-export:
import { formatDate } from "@/lib/admin/format";

// Dates — always pass date-only strings ("2026-02-19")
formatDate("2026-02-19")        // "Thu, Feb 19"
formatDateLong("2026-02-19")    // "Thursday, February 19, 2026"
formatDateWithYear("2026-02-19") // "Feb 19, 2026"

// Times — pass ISO datetime strings or HH:MM
formatTime("2026-02-19T14:30:00Z") // "2:30 PM"
formatScheduledTime("14:30")        // "2:30 PM"
formatScheduledTime("morning")      // "8 AM – 12 PM"

// Durations — pass milliseconds, no seconds shown
formatElapsed(2700000)  // "45 min"
formatCountdown(8100000) // "2h 15m"

Detail Sections

Consistent section cards for admin detail views with built-in edit/view state management

Card Variant (default)

Status

Active

Properties

3
123 Main St
456 Oak Ave
789 Elm Dr

Notes

Internal admin notes

VIP customer, prefers morning appointments.

Plain Variant

Bookings

5
Mar 15, 2026
Apr 2, 2026
Apr 20, 2026

With Actions Badge

Contact

Active
Jane Smith
jane@example.com
(555) 123-4567

Editable (click the pencil)

Sections start in view mode. Click the pencil to edit. Save/Revert buttons appear in edit mode. Border highlights blue while editing.

Contact

Jane Smith
jane@example.com

Notes

VIP customer, prefers morning appointments.

Danger Zone

Danger Zone

Props

PropTypeDescription
titlestringSection heading
subtitlestring?Secondary text below title
countnumber?Badge count next to title
variant"card" | "plain"Card with border (default) or plain heading
padding"default" | "compact"Content padding size
actionsReactNode?Extra header content (badges, buttons)
editableboolean?Enable built-in edit pencil / save+revert
isEditingboolean?Whether section is in edit mode
onEdit / onSave / onRevert() => voidEdit lifecycle callbacks
isSavingboolean?Disables save button, shows loading text

Effects & Animations

Sparkles, glass shimmer, and squeegee streak effects

Sparkles

Crystal Clear Views

Twinkling sparkle animation

<Sparkles count={15} minSize={16} maxSize={32} />Import from @/components/Sparkles

Glass Panel with Shimmer

3D Glass Shimmer

Watch the light sweep across this frosted glass panel

Squeegee Streak Effect

Horizontal streak animation like a squeegee wipe

3D Glass Cards

Residential

Hover to see 3D lift effect

Commercial

Hover to see 3D lift effect

Loading Shimmer

Soap Suds Shader

WebGL shader effect with animated pastel rainbow soap-bubble blobs using simplex noise and fractal brownian motion. Falls back to a CSS gradient if WebGL context is lost.

Soap Suds

Animated WebGL shader overlay

<SoapSudsShader className="..." />Import from @/components/SoapSudsShader

Video Background

Full-bleed background video with dark overlay and smooth fade-in. Falls back to solid dark background while loading. Used across hero sections on Residential, Commercial, and Careers pages.

Video Background

Auto-playing, muted, looping video

<VideoBackground />Import from @/components/VideoBackground

Fetches video URL from landing page settings. Props: none — self-contained.

Favicon

Browser tab icons matching the AppIcon design — swirl logo on gradient background

icon.png (Primary Favicon)

Lucent Wash Logo

32x32 PNG

/app/icon.png

apple-icon.png (Apple Touch Icon)

Lucent Wash Logo

180x180 PNG

/app/apple-icon.png

Favicon Files

  • /app/icon.png - PNG favicon for modern browsers (32x32)
  • /app/apple-icon.png - Apple Touch Icon (180x180)
  • Uses the AppIcon design — white swirl logo on cyan-teal gradient with rounded corners
  • Next.js automatically generates favicon meta tags from these files

Form Elements

Inputs, selects, and form controls

Select Component

Custom dropdown select with keyboard navigation, animations, and multiple variants.

Status Select

Compact pill-style select for status fields with color indicators.

pending

import { Select, StatusSelect } from "@/components/Select"; // Basic usage <Select value={value} onChange={setValue} options={[ { value: "residential", label: "Residential" }, { value: "commercial", label: "Commercial" }, ]} placeholder="Select..." size="md" // "sm" | "md" | "lg" variant="default" // "default" | "minimal" | "pill" /> // Status select with colors <StatusSelect value={status} onChange={setStatus} options={[ { value: "pending", label: "Pending", color: "bg-amber-100 text-amber-700" }, { value: "confirmed", label: "Confirmed", color: "bg-blue-100 text-blue-700" }, ]} />

TimePicker

Two-step time selector: first pick Morning or Afternoon, then choose a 30-minute slot from a grid. Animated transitions between steps using AnimatedItem.

9:00 AM

import TimePicker from "@/components/TimePicker"; <TimePicker value="09:00" // "HH:MM" format or "" onChange={setTime} // (time: string) => void className="..." // optional wrapper class />

Toggle Switch

Accessible toggle switch pattern with role="switch" and aria-checked. Used in crew member availability schedules.

Enabled
Disabled

Note: This is currently an inline pattern (not yet extracted into a standalone component). Consider creating a Toggle component for reuse.

OTP Input

6-digit verification code input with numeric filtering, paste button, and monospace styling. Used across login, signup, and invite flows.

Current value: (empty)

import OtpInput from "@/components/OtpInput"; <OtpInput value={otpCode} onChange={setOtpCode} autoFocus // optional disabled={false} // optional />

Other Form Elements

Fun Slider

An interactive range slider with spring physics, glow effects, and particle bursts.

Color Variants

Three color themes that map to the brand palette. Drag or tap anywhere on the track. Particles burst from the thumb as the value increases.

Blue (default)25
Cyan40
Emerald60

Sizes

Small for inline controls, medium (default) for forms, large for hero/landing sections.

Small30
Medium (default)25
Large35

States

Disabled50
No particles50

import FunSlider from "@/components/FunSlider"; <FunSlider value={value} onChange={setValue} min={10} // minimum value max={60} // maximum value step={1} // increment (default 1) variant="blue" // "blue" | "cyan" | "emerald" size="lg" // "sm" | "md" | "lg" particles={true} // show particle bursts (default true) disabled={false} // disabled state />

Headers

Navigation header variants used across the site. These render the actual Header component.

Main Site Header

Used on: Landing page, service pages
<Header variant="main" />

Full Width (resize browser to see breakpoints: xl=1280px+, lg=1024px, mobile=<1024px)

Lucent Wash Logo

xl (1280px+): All nav items visible, full phone number, "My Account" with icon, "Get Free Quote"

lg (1024-1279px): Services, Pricing + "More" dropdown, phone & account as icons only, "Get Quote"

<lg: Hamburger menu

Subpage Header

Used on: Residential, Commercial, Design System
<Header variant="subpage" backHref="/" pageLabel="Page Name" />

Lucent Wash LogoLucent Wash

Resize browser to see responsive behavior: back label and wordmark hide on small screens

Dashboard Header

Used on: Customer dashboard, Admin dashboard, Quote flow
<Header variant="dashboard" userEmail="user@example.com" />

Lucent Wash LogoLucent Wash

Resize browser to see responsive behavior: email hides on small screens

Header Component API

Import:

import Header from "@/components/Header";

Props:

  • variant: "main" | "subpage" | "dashboard"
  • backHref: Back button destination (subpage only)
  • backLabel: Back button text (subpage only)
  • pageLabel: Page name shown after logo (subpage only)
  • userEmail: User email to display (dashboard only)
  • onClose: Close button callback (dashboard only)

Responsive Breakpoints (Main Header):

  • xl (1280px+): Full text for phone, account, "Get Free Quote"
  • lg (1024px): Icons for phone/account, "Get Quote", "More" dropdown
  • <lg: Hamburger menu with full mobile nav

Maps

Leaflet-based map components with lazy loading to avoid SSR issues. All maps support dark mode via CSS filter inversion.

ServiceAreaMap

Isochrone drive-time visualization centered on White Settlement. Three concentric zones (15/30/45 min) rendered as GeoJSON overlays on grayscale Voyager tiles. Non-interactive.

Tile: voyager + grayscale · View: MAP_VIEWS.serviceArea· Used on: Landing page

import ServiceAreaMap from "@/components/ServiceAreaMap"; <ServiceAreaMap /> // No props — loads /data/isochrone.json

PropertyMap

Single-marker map centered on a property location. Includes marker with address popup and "Get Directions" buttons for Google Maps and Apple Maps. Supports satellite/map toggle.

Tile: light + satellite · View: MAP_VIEWS.property· Used on: Booking detail, admin property drawer

import PropertyMap from "@/components/PropertyMap"; <PropertyMap lat={32.7595} lng={-97.3933} address="123 White Settlement Rd, Fort Worth, TX" />

AllPropertiesMap

Multi-marker map showing all properties, color-coded by type (blue = residential, gold = commercial). Auto-fits bounds to markers. Includes click callbacks per marker and a color legend.

Tile: light + satellite · View: MAP_VIEWS.allProperties· Used on: Admin properties tab

import AllPropertiesMap from "@/components/AllPropertiesMap"; <AllPropertiesMap properties={[ { id: "1", address: "123 Main St", property_type: "residential", lat: 32.77, lng: -96.79 }, ... ]} onPropertyClick={(id) => openDrawer(id)} />

AddressMapPreview

Interactive map with draggable marker for address selection. Click or drag to set location, with reverse geocoding for address lookup. Supports satellite/map toggle.

Tile: light + satellite · View: MAP_VIEWS.addressPicker· Used on: Booking form, admin property edit

import AddressMapPreview from "@/components/AddressMapPreview"; <AddressMapPreview lat={32.7595} lng={-97.3933} onPositionChange={({ lat, lng }) => setCoords({ lat, lng })} />

Tile Layers & Map Views

Centralized in lib/constants.ts as MAP_TILES, MAP_ATTRIBUTION, and MAP_VIEWS.

Tile Layers

  • light — Low-contrast, subtle roads. Good for overlays.
  • voyager — Higher-contrast colored roads. Navigation maps.
  • voyagerGrayscale — Voyager + .leaflet-grayscale CSS class. Grayscale roads with colored overlays.
  • satellite — Esri satellite imagery.

Preset Views

  • dfw — Arlington center, zoom 9. Full metroplex.
  • serviceArea — White Settlement, zoom 8. Isochrone display.
  • fortWorth — Fort Worth, zoom 12. Crew/sales maps.
  • property — Zoom 15. Single property close-up.
  • addressPicker — Zoom 16. Address selection.
  • allProperties — Zoom 11. Multi-property overview.

Modals

Animated modal dialogs with spring physics and smooth transitions

Centered Modal

Scales up with fade, backdrop blur. Good for forms and content.

Dialog Modal

Confirmation dialogs with title, description, and action buttons.

Fullscreen Modal

Slides up from bottom, covers the whole screen. Used in quote flow.

Usage

import { Modal, FullscreenModal, DialogModal, useModal } from "@/components/Modal";

// Hook for managing modal state
const modal = useModal();

// Centered Modal (forms, content)
<Modal isOpen={modal.isOpen} onClose={modal.close}>
  <div className="p-6">Modal content</div>
</Modal>

// Dialog Modal (confirmations)
<DialogModal
  isOpen={modal.isOpen}
  onClose={modal.close}
  title="Confirm"
  description="Are you sure?"
  actions={<>
    <button onClick={modal.close}>Cancel</button>
    <button onClick={handleConfirm}>Confirm</button>
  </>}
/>

// Fullscreen Modal (multi-step flows)
<FullscreenModal
  isOpen={modal.isOpen}
  onClose={modal.close}
  title="Step 1 of 3"
>
  <div>Full page content</div>
</FullscreenModal>

OpenGraph Images

Social media preview images for public-facing pages (1200x630px recommended)

Home Page

/opengraph-image.png

LogoLucent Wash

Crystal Clear Views, Every Time

Professional Window Washing | DFW Metroplex

Careers Page

/careers/opengraph-image.png

LogoLucent Wash

Join Our Team

Now Hiring | Part-Time Window Washers | $18-22/hr

Residential Page

/residential/opengraph-image.png

LogoLucent Wash

Residential Window Cleaning

Streak-Free Windows for Your Home | DFW

Commercial Page

/commercial/opengraph-image.png

LogoLucent Wash

Commercial Window Cleaning

Professional Service for Your Business | DFW

Quote Page

/quote/opengraph-image.png

LogoLucent Wash

Get Your Free Quote

Instant Pricing in Under 2 Minutes | Lucent Wash

OpenGraph Image Guidelines

  • Recommended size: 1200x630px (1.91:1 aspect ratio)
  • File format: PNG or JPG, under 8MB
  • Place as opengraph-image.png in each route folder
  • Next.js automatically generates og:image meta tags from these files

Pricing Breakdown

Shared read-only pricing display with itemized discounts. Used in admin BookingDrawer and customer BookingDetailClient.

With Itemized Discounts

30 windows × $8/ea$240.00
0 screens × $3/ea$0.00
Subtotal$240.00
Quarterly discount-$24.00 (10%)
Referral reward-$21.60 (9.0%)
Travel Fee$15.00
Total$209.40
Tip$20.00

Fallback (No Itemization)

Subtotal$160.00
Discount-$16.00 (10%)
Total$144.00

Prepay Discount Only

Subtotal$200.00
Prepay discount-$10.00 (5%)
Total$190.00

All Discount Types

35 windows × $8/ea$280.00
8 screens × $3/ea$24.00
Subtotal$320.00
Monthly discount-$48.00 (15%)
Referral reward-$27.20 (8.5%)
Prepay discount-$12.24 (3.8%)
Travel Fee$20.00
Total$252.56
Tip$35.00

No Discounts

15 windows × $8/ea$120.00
Subtotal$120.00
Travel Fee$10.00
Total$130.00

Manual Admin Discount

Subtotal$200.00
Discount-$25.00 (12.5%)
Prepay discount-$8.75 (4.4%)
Total$166.25
Tip$15.00

Sales % Discount (15% off)

30 windows × $8/ea$240.00
Subtotal$240.00
Sales discount-$36.00 (15%)
Total$204.00

Sales Fixed Price Override ($150)

25 windows × $8/ea$200.00
Subtotal$200.00
Sales discount-$50.00 (25%)
Total$150.00

Sales + Frequency Discount Stacked

35 windows × $8/ea$280.00
8 screens × $5/ea$40.00
Subtotal$320.00
Monthly discount-$48.00 (15%)
Sales discount-$27.20 (8.5%)
Total$244.80

Grandfathered Pricing (Locked Rate)

Grandfathered pricing — saving $15.00 vs current rates
25 windows × $8/ea$200.00
Subtotal$200.00
Quarterly discount-$20.00 (10%)
Total$180.00

Grandfathered (No Savings Yet)

Grandfathered pricing
Subtotal$200.00
Monthly discount-$30.00 (15%)
Total$170.00

High-Value Commercial

100 windows × $10/ea$1000.00
40 screens × $5/ea$200.00
Subtotal$1200.00
Monthly discount-$180.00 (15%)
Sales discount-$102.00 (8.5%)
Travel Fee$35.00
Total$953.00
Tip$100.00

Small Job + Travel Fee

6 windows × $8/ea$48.00
Subtotal$48.00
Travel Fee$25.00
Total$73.00

Kitchen Sink (All Discount Types)

40 windows × $8/ea$320.00
16 screens × $5/ea$80.00
Subtotal$400.00
Monthly discount-$60.00 (15%)
Referral reward-$34.00 (8.5%)
Sales discount-$30.60 (7.6%)
Prepay discount-$13.77 (3.4%)
Travel Fee$15.00
Total$276.63
Tip$40.00

Props Reference

PropTypeDescription
subtotalnumberPre-discount subtotal
discountItemsDiscountItem[]Itemized discounts (frequency, referral, prepay, manual)
totalDiscountnumberFallback flat discount when discountItems is empty
travelFeenumberTravel surcharge (shown only if > 0)
totalnumberFinal total after discounts + travel fee
tipnumber?Tip amount (shown only when showTip is true and tip > 0)
showTipboolean?Whether to display the tip line
lineItems{label, amount}[]?Optional line items shown above subtotal (e.g. windows, screens)
grandfatherInfo{isGrandfathered, savings}?Shows locked-in rate badge when isGrandfathered is true. Savings shows delta vs current rates.

Rolling Number

An odometer-style animated counter. Each digit rolls independently with spring physics.

Interactive Demo

Click the buttons to change the value and watch each digit column roll to its new position.

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

Prefix & Suffix

Attach static text before or after the rolling digits.

$
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

prefix="$"

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
%

suffix="%"

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
min

padStart=2

Spring Tuning

Adjust stiffness and damping for different feels.

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

Snappy (400/20)

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

Default (200/25)

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

Bouncy (80/15)

Any Size

Inherits font-size and line-height from className, so it works at any scale.

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

text-sm

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

text-2xl

0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9

text-5xl

import RollingNumber from "@/components/RollingNumber"; <RollingNumber value={42} prefix="$" // optional static prefix suffix="%" // optional static suffix padStart={2} // pad with leading zeros stiffness={200} // spring stiffness (default 200) damping={25} // spring damping (default 25) className="text-3xl font-bold text-blue-600 leading-[40px]" />

Shadows

Elevation system using Tailwind shadows

Standard Shadows

shadow-sm

shadow-sm

Subtle shadow for cards and inputs

shadow

shadow

Default shadow for elevated elements

shadow-md

shadow-md

Medium shadow - used on headers

shadow-lg

shadow-lg

Large shadow for modals and dropdowns

shadow-xl

shadow-xl

Extra large shadow for prominent cards

shadow-2xl

shadow-2xl

Largest shadow for hero elements

Colored Shadows

Blue Shadow

shadow-lg shadow-blue-500/25

Primary CTA buttons

Cyan Shadow

shadow-lg shadow-cyan-500/25

Accent elements

Slate Shadow

shadow-lg shadow-slate-200/50

Service cards

Inner Shadows

shadow-inner

shadow-inner

Inset shadow for pressed states or inputs

shadow-none

shadow-none

Remove shadow (useful for hover states)

Shadow Usage Guidelines

  • shadow-md - Headers (fixed/sticky navigation)
  • shadow-lg - Cards, dropdowns, modals
  • shadow-lg shadow-blue-500/25 - Primary CTA buttons
  • shadow-xl - Hover states on cards

Sub-Headers

Standardized header for sub-pages (Schedule, Properties, Profile, etc.). Back button left, logo centered, nothing right. Page title goes in the body, not the header.

Live Demo

Used on: Schedule, Properties, Profile, and all customer sub-pages

Desktop (sm+)

Dashboard
Lucent Wash LogoLucent Wash

Page Title Here

Mobile (<sm)

Lucent Wash LogoLucent Wash

Page Title Here

On mobile, the back label hides and the spacer collapses — logo stays centered

Sub-Header Pattern

Structure:

  • Left: Back arrow + label (label hidden on mobile via hidden sm:inline)
  • Center: LogoWordmark iconSize={36}
  • Right: Empty spacer w-[88px] hidden sm:block to balance the back button

Body title:

  • Page title is an h1 placed inside <main>, not in the header
  • Optionally includes a contextual icon (Calendar, MapPin, etc.)
  • Style: text-2xl font-bold

Container:

  • sticky top-0 z-50
  • bg-white dark:bg-black with border-b
  • Padding: px-4 sm:px-6 py-3 sm:py-4
  • Max-width matches page content (max-w-2xl to max-w-5xl)

Used on:

  • Schedule page (/schedule/[propertyId])
  • Properties page (/properties)
  • Profile page (/profile)

Typography

Clean, modern type scale with Inter font family

Display / 60px

Crystal Clear Views

H1 / 48px

Professional Window Cleaning

H2 / 36px

Our Services

H3 / 24px

Residential Cleaning

Body Large / 18px

We provide streak-free, sparkling clean windows for your home or business.

Body / 16px

Serving the entire DFW metroplex with reliable, professional window cleaning services.

Small / 14px

© 2025 Lucent Wash. All rights reserved. Privacy Policy | Terms of Service

Gradient Text

Crystal Clear Views, Every Time