Trial Countdown Banner — Free HTML CSS JS Snippet

Trial Countdown Banner · Pricing · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Trial days remaining: computed from TRIAL_START_DATE and TRIAL_DAYS at runtime
Progress bar: elapsed/total*100 fill, indigo→amber gradient matching urgency
Expiry date: computed and formatted as "Jun 7" from start + trial length
Dark indigo gradient banner: high contrast, amber highlight for urgency signals
Amber upgrade CTA: colour-coordinated with the days remaining urgency signal
Dismiss: max-height + opacity + padding collapse animation via JS
Feature limit warning card: contextual yellow-border card in content area
Both dismiss and limit card link to same upgrade URL for conversion consistency

About this UI Snippet

Trial Countdown Banner — Days Remaining, Trial Progress Bar, Expiry Date & Upgrade CTA

Screenshot of the Trial Countdown Banner snippet rendered live

A trial countdown banner keeps free trial users aware of their remaining trial time throughout their product session — the most effective in-product conversion mechanism for SaaS products. This snippet provides a complete in-app trial countdown system: a top banner with days remaining, a trial progress bar (Day X of 14), an expiry date, a high-contrast amber upgrade CTA, a dismiss button, and a separate feature limit warning card that appears in the main content area. For a persistent page-wide variant, see the upgrade banner snippet.

The trial countdown calculation

The initTrial() function computes elapsed and remaining days from the trial start date. elapsed = floor((now - start) / 86400000) — the difference in milliseconds divided by milliseconds per day. remaining = TRIAL_DAYS - elapsed. The progress bar fill percentage = elapsed / TRIAL_DAYS * 100. The expire date is computed by adding TRIAL_DAYS to the start date.

The banner design

A dark indigo gradient banner (linear-gradient from #1e1b4b to #312e81) sits at the top of the app shell. White text on the dark background provides high contrast. The days remaining number is highlighted in amber (#fbbf24) — the most urgent visual signal without red. The upgrade button also uses amber, creating visual alignment between the urgency signal and the action.

The trial progress bar

A horizontal progress bar shows how far through the trial the user is. The gradient fill goes from indigo to amber — moving toward the amber urgency colour as the trial nears its end. "Trial started" and "Trial ends" labels frame the bar. "Day X of 14" below gives exact context.

The feature limit warning card

A separate limit card appears in the main content area when the user approaches a free plan limit (4 of 5 projects used). This contextual prompt converts at a higher rate than the persistent banner alone — it triggers at the exact moment the user encounters the constraint.

Dismiss handling

The dismiss button collapses the banner via JavaScript animation (max-height from scrollHeight to 0). Uncommenting the localStorage line in dismissBanner() persists the dismiss across page loads. For server-rendered apps, save the dismiss timestamp to the user's account instead. For a fully server-rendered approach, add a dismissed_at timestamp to the user table and skip the banner HTML entirely when within the TTL.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Instead of working out the date math on your own, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why initTrial() zeroes out the hours on both the now and start Date objects with setHours(0,0,0,0) before subtracting them, and what bug would appear if that step were skipped near a daylight-saving transition. It's also a good candidate for an optimization pass — ask whether recalculating and re-rendering the whole banner on every page load is the right approach versus computing elapsed/remaining once on the server and passing it down as props. For extending it, have it add a color progression that shifts the banner from indigo to red as remaining days approach zero, wire the dismiss button to localStorage with a re-show timeout, or add a second banner variant driven by usage percentage instead of days. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a SaaS trial countdown banner in plain HTML, CSS, and vanilla JavaScript with no libraries.

Requirements:
- Configure a trial start Date and a trial length in days as top-level constants.
- Compute elapsed days as the floor of the millisecond difference between today and the start date divided by 86400000, after zeroing the hours/minutes/seconds/milliseconds on both Date objects so the calculation is immune to time-of-day drift.
- Compute remaining days as the trial length minus elapsed, clamped to a minimum of zero, and compute a fill percentage as elapsed divided by trial length times 100, clamped to a maximum of 100.
- Display the remaining days with correct singular/plural wording ("1 day" vs "3 days"), update a progress bar's width to the computed percentage, show "Day X of N" text, and compute and format the expiry date (start date plus trial length days) as a short human-readable date like "Jun 7".
- Include a dismiss button that collapses the banner with an animated transition of max-height, opacity, and padding down to zero, driven by first setting max-height to the banner's current scrollHeight, then on the next animation frame collapsing it to zero so the CSS transition actually animates instead of jumping instantly.
- Include a separate feature-limit warning card elsewhere on the page showing usage against a plan limit (e.g. "4 of 5 free projects used"), with its own upgrade call-to-action pointing to the same destination as the banner's upgrade button.

Step by step

How to Use

  1. 1
    Set your trial start date and lengthIn the JS panel, set TRIAL_START_DATE to your user's actual trial start (from your database) and TRIAL_DAYS to your trial length (14 or 30 days). The banner auto-computes days remaining, progress bar fill, and expiry date.
  2. 2
    Wire the Upgrade CTASet href="#" on .upgrade-btn and .limit-cta to your billing page, Stripe checkout session, or upgrade flow URL. Use the same destination for both to keep the upgrade path consistent.
  3. 3
    Update the feature limit cardEdit .limit-title ("You've used 4 of 5 free projects") and .limit-sub to match your actual free plan limits. Show or hide this card based on the user's actual usage relative to the free tier threshold.
  4. 4
    Persist dismiss state across page loadsUncomment the localStorage line inside dismissBanner(): localStorage.setItem("trial_banner_dismissed","1"). On page load, check: if (localStorage.getItem("trial_banner_dismissed")) { banner.style.display = "none"; }
  5. 5
    Change the banner colour schemeThe banner uses a dark indigo gradient. For a lighter scheme, change background to linear-gradient(135deg, #eff6ff, #dbeafe) and text colour to #1e40af. Update the upgrade button to background: #1e40af and colour: #fff.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component using a server-fetched trialStartDate prop and useState for dismissed state, or "Tailwind" for a Tailwind CSS version.

Real-world uses

Common Use Cases

SaaS free trial expiry and upgrade conversion
Show the banner to all users during their trial period. The amber days remaining and the progress bar create escalating urgency as the trial progresses. Users who see contextual upgrade prompts during their trial convert at 2–3× the rate of those who only see email reminders.
Feature-gated limit warning prompts
Show the limit card when users approach a free tier threshold (4/5 projects, 80% storage, 9/10 API calls). The contextual timing — at the moment the user hits the limit — captures them when they have the strongest motivation to upgrade.
Freemium plan limit and plan awareness banners
Adapt for freemium products without a trial: replace "days left in trial" with "free plan features". Show current usage (API calls this month, storage used, team seats), or pair it with a usage calculator so users can model the upgrade. The progress bar becomes a usage meter rather than a time countdown.
Multi-tier plan progression and upsell prompts
Show different banners per plan tier: Starter users see a Pro upsell, Pro users see a Team upsell. Customise the banner colour, CTA label, and feature list per tier. The consistent banner position means users learn to check it for their current usage status.
Study trial duration calculation and contextual conversion patterns
The trial countdown calculation — date arithmetic with Math.floor — demonstrates how to compute days elapsed and remaining from a stored start date. The contextual limit card demonstrates the product-led conversion pattern of triggering upgrade prompts at points of friction.
Server-rendered trial data injection for Next.js and server frameworks
In a server-rendered app, compute elapsed, remaining, and pct on the server from the user's trialStartDate in the database. Pass these as props to the banner component. The banner renders with the correct values immediately — no client-side calculation needed, no flash of uncalculated state.

Got questions?

Frequently Asked Questions

initTrial() computes elapsed days: const elapsed = Math.floor((now - start) / 86400000). Both dates are set to midnight (setHours(0,0,0,0)) before subtraction to avoid time-zone and hour-of-day drift. 86400000 is the number of milliseconds in one day. remaining = TRIAL_DAYS - elapsed. Progress percentage = elapsed / TRIAL_DAYS * 100. The expiry date is computed by adding TRIAL_DAYS days to the start date using setDate(start.getDate() + TRIAL_DAYS).

Uncomment the localStorage line in dismissBanner(): localStorage.setItem("trial_banner_dismissed", "1"). At the end of initTrial(), add: if (localStorage.getItem("trial_banner_dismissed")) { document.getElementById("trial-banner").style.display = "none"; return; }. For a more sophisticated approach, store a timestamp and re-show the banner after 3 days: const dismissed = localStorage.getItem("trial_dismissed"); if (dismissed && Date.now() - parseInt(dismissed) < 3*24*3600000) return.

In initTrial(), check the remaining days and apply a different gradient class: if (remaining <= 3) { banner.style.background = "linear-gradient(135deg, #7f1d1d, #b91c1c)"; } else if (remaining <= 7) { banner.style.background = "linear-gradient(135deg, #78350f, #92400e)"; }. This creates a colour progression from indigo (comfortable) through amber (urgent) to red (critical) as the trial countdown reaches its final days.

Click "JSX" to download. Accept trialStartDate and trialDays as props from the server (fetched from your database in getServerSideProps or a Server Component). Compute elapsed, remaining, and pct in the component body or with useMemo. Manage dismissed with useState(false). Set the dismiss handler to call setDismissed(true) and optionally write to localStorage. Conditionally render: {!dismissed && <TrialBanner ... />}.