Braille Loader
dominik-portfolio/braille-loaderFreeComponent
An animated braille loading indicator with wave, typewriter, shimmer, and pulse variants.
Install it
npx shadcn@latest add https://dominikkoch.dev/r/braille-loader.jsonSource
1import type { CSSProperties } from "react";2import { getBrailleCharacters } from "@/lib/text-to-braille";3import { cn } from "@/lib/utils";45interface BrailleLoaderProps {6 text: string;7 className?: string;8 variant?: "wave" | "typewriter" | "shimmer" | "pulse";9 speed?: number;10}1112const STEP_MS = 120;13const MIN_SPEED = 0.1;1415const variantStyles: Record<16 NonNullable<BrailleLoaderProps["variant"]>,17 string18> = {19 wave: `@keyframes braille-wave {20 0%, 100% { opacity: 0.15; }21 12%, 25% { opacity: 1; }22 37% { opacity: 0.15; }23 }`,24 typewriter: `@keyframes braille-typewriter {25 0%, 10% { opacity: 0; }26 12% { opacity: 1; }27 80% { opacity: 1; }28 90%, 100% { opacity: 0; }29 }`,30 shimmer: `@keyframes braille-shimmer {31 0%, 100% { opacity: 0.15; }32 12%, 25% { opacity: 1; }33 37% { opacity: 0.4; }34 50% { opacity: 0.15; }35 }`,36 pulse: `@keyframes braille-pulse {37 0%, 100% { opacity: 0.15; }38 50% { opacity: 1; }39 }`,40};4142function getAnimation(43 variant: NonNullable<BrailleLoaderProps["variant"]>,44 index: number,45 total: number,46 speed: number47): CSSProperties {48 const stepMs = STEP_MS / Math.max(speed, MIN_SPEED);49 const totalDuration = (total + 2) * stepMs;50 const delay = index * stepMs;5152 if (variant === "pulse") {53 return {54 animation: `braille-pulse ${total * stepMs * 1.5}ms ease-in-out ${delay}ms infinite`,55 };56 }5758 return {59 animation: `braille-${variant} ${totalDuration}ms ease-in-out ${delay}ms infinite`,60 };61}6263export function BrailleLoader({64 text,65 className,66 variant = "wave",67 speed = 1,68}: BrailleLoaderProps) {69 const chars = getBrailleCharacters(text);7071 return (72 <span73 aria-hidden="true"74 className={cn("inline-flex font-mono text-muted-foreground", className)}75 >76 <style>{variantStyles[variant]}</style>77 {chars.map(({ char, key }, i) => (78 <span key={key} style={getAnimation(variant, i, chars.length, speed)}>79 {char === " " ? "\u00A0" : char}80 </span>81 ))}82 </span>83 );84}
Files it writes
More from dominik-portfolio
All 4 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Command Tabscommand-tabs | dominik-portfolio | Components | Free | 1 dep | |
| Expandable Tabsexpandable-tabs | dominik-portfolio | Components | Free | no deps | |
| Permission Selectorpermission-selector | dominik-portfolio | Components | Free | 1 dep |
