Componentform1.25 KB gzipZero dependencies
Password Strength
Segmented security verification meter with animated strength bars and criteria checklist.
Preview
Live Preview
CURRENT KEY:ticket
SECURITY METERREVOKED / WEAK
✓MIN 8 CHARACTERS
✓UPPERCASE & LOWERCASE
✓NUMERIC DIGIT (0-9)
✓SPECIAL SYMBOL (#$@!)
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add password-strengthOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { PasswordStrength } from "@/components/ui/password-strength";
<PasswordStrength value="TicketSecret!2026" />Usage guidance
Use Password Strength when a ticket stub surface needs Segmented security verification meter with animated strength bars and criteria checklist. Keep props minimal and prefer composition with sibling stubs.
Avoid Password Strength for long form flows or dense data grids where a dedicated layout block fits better.
Source code
password-strength.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface PasswordStrengthProps extends React.HTMLAttributes<HTMLDivElement> {
value?: string;
className?: string;
showCriteria?: boolean;
}
interface Criteria {
label: string;
met: boolean;
}
export function PasswordStrength({
value = "",
className,
showCriteria = true,
...props
}: PasswordStrengthProps) {
const criteria: Criteria[] = React.useMemo(() => [
{ label: "MIN 8 CHARACTERS", met: value.length >= 8 },
{ label: "UPPERCASE & LOWERCASE", met: /[A-Z]/.test(value) && /[a-z]/.test(value) },
{ label: "NUMERIC DIGIT (0-9)", met: /[0-9]/.test(value) },
{ label: "SPECIAL SYMBOL (#$@!)", met: /[^A-Za-z0-9]/.test(value) },
], [value]);
const metCount = criteria.filter((c) => c.met).length;
const strengthLevel = React.useMemo(() => {
if (value.length === 0) return { label: "NO TICKET KEY", color: "bg-muted", width: "0%" };
if (metCount <= 1) return { label: "REVOKED / WEAK", color: "bg-destructive", width: "25%" };
if (metCount === 2) return { label: "STANDBY / FAIR", color: "bg-warning", width: "50%" };
if (metCount === 3) return { label: "VALID / GOOD", color: "bg-warning", width: "75%" };
return { label: "SECURE / ADMITTED", color: "bg-success", width: "100%" };
}, [value, metCount]);
return (
<div className={cn("space-y-3 font-mono", className)} {...props}>
{/* Header Info */}
<div className="flex items-center justify-between text-xs">
<span className="text-[10px] font-bold uppercase tracking-[0.2em] text-muted-foreground">
SECURITY METER
</span>
<span className="font-bold text-foreground transition-colors duration-300">
{strengthLevel.label}
</span>
</div>
{/* Segmented Meter Bar */}
<div className="grid grid-cols-4 gap-1.5 p-1 rounded-md border border-border bg-secondary/50">
{[0, 1, 2, 3].map((step) => {
const isActive = step < metCount;
return (
<div
key={step}
className={cn(
"h-2 rounded-sm transition-all duration-300",
isActive ? strengthLevel.color : "bg-muted opacity-40",
isActive && "shadow-xs"
)}
/>
);
})}
</div>
{/* Criteria Checklist */}
{showCriteria && (
<div className="space-y-1.5 pt-1 border-t border-dashed border-border/80 text-[11px]">
{criteria.map((c, i) => (
<div
key={i}
className={cn(
"flex items-center gap-2 transition-colors duration-200",
c.met ? "text-foreground font-medium" : "text-muted-foreground/70"
)}
>
<span
className={cn(
"size-4 rounded-sm border flex items-center justify-center text-[10px] transition-all",
c.met
? "border-accent bg-accent text-accent-foreground font-bold scale-110"
: "border-border bg-card text-transparent"
)}
>
✓
</span>
<span>{c.label}</span>
</div>
))}
</div>
)}
</div>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| value? | string | |
| className? | string | |
| showCriteria? | boolean |