{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "password-input",
  "title": "Password Input",
  "type": "registry:ui",
  "files": [
    {
      "path": "packages/registry-svelte/components/password-input/PasswordInput.svelte",
      "content": "<script lang=\"ts\" module>\n  import type { HTMLInputAttributes } from 'svelte/elements'\n  import type { PasswordInputVariants } from './password-input.variants'\n\n  export interface PasswordInputProps extends Omit<HTMLInputAttributes, 'size' | 'type' | 'minlength' | 'value'> {\n    /** Controlled value. Pair with `bind:value`. */\n    value?: string | undefined\n    defaultValue?: string\n    size?: PasswordInputVariants['size']\n    variant?: PasswordInputVariants['variant']\n    showStrength?: boolean\n    showToggle?: boolean\n    minLength?: number\n    ref?: HTMLInputElement | null\n  }\n\n  export interface PasswordStrength {\n    score: number\n    label: 'weak' | 'fair' | 'good' | 'strong'\n    color: string\n    barColor: string\n    percent: number\n  }\n\n  export function getPasswordStrength(pwd: string): PasswordStrength {\n    if (!pwd) return { score: 0, label: 'weak', color: '', barColor: 'bg-transparent', percent: 0 }\n\n    let score = 0\n    if (pwd.length >= 6) score++\n    if (pwd.length >= 10) score++\n    if (/[A-Z]/.test(pwd) && /[a-z]/.test(pwd)) score++\n    if (/\\d/.test(pwd)) score++\n    if (/[^A-Za-z0-9]/.test(pwd)) score++\n\n    if (score <= 1) {\n      return { score, label: 'weak', color: 'text-destructive', barColor: 'bg-destructive', percent: 25 }\n    }\n    if (score <= 2) {\n      return {\n        score,\n        label: 'fair',\n        color: 'text-warning',\n        barColor: 'bg-warning',\n        percent: 50,\n      }\n    }\n    if (score <= 3) {\n      return {\n        score,\n        label: 'good',\n        color: 'text-info',\n        barColor: 'bg-info',\n        percent: 75,\n      }\n    }\n    return {\n      score,\n      label: 'strong',\n      color: 'text-success',\n      barColor: 'bg-success',\n      percent: 100,\n    }\n  }\n</script>\n\n<script lang=\"ts\">\n  import { Eye, EyeOff } from '@lucide/svelte'\n  import { cn } from '$lib/utils'\n  import { passwordInputVariants } from './password-input.variants'\n\n  let {\n    class: className,\n    value = $bindable(undefined),\n    defaultValue = '',\n    placeholder = 'Enter password',\n    size = 'default',\n    variant = 'outlined',\n    disabled = false,\n    readonly = false,\n    showStrength = false,\n    showToggle = true,\n    minLength = 0,\n    autocomplete = 'current-password',\n    ref = $bindable(null),\n    ...restProps\n  }: PasswordInputProps = $props()\n\n  let passwordVisible = $state(false)\n  // svelte-ignore state_referenced_locally -- uncontrolled seed: later defaultValue changes must not clobber typing.\n  let internalValue = $state(defaultValue)\n  const inputValue = $derived(value ?? internalValue)\n  const computedType = $derived(passwordVisible ? 'text' : 'password')\n  const strength = $derived(getPasswordStrength(inputValue))\n  const meetsMinLength = $derived(inputValue.length >= minLength)\n\n  function handleInput(event: Event) {\n    const next = (event.target as HTMLInputElement).value\n    if (value === undefined) internalValue = next\n    value = next\n  }\n\n  function toggleVisibility() {\n    if (disabled || readonly) return\n    passwordVisible = !passwordVisible\n    ref?.focus()\n  }\n\n  const wrapperClasses = $derived(\n    cn(\n      passwordInputVariants({ size, variant }),\n      'focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]',\n      disabled && 'pointer-events-none cursor-not-allowed bg-muted/30 opacity-50',\n      className,\n    ),\n  )\n  const inputPadding = $derived(size === 'sm' ? 'px-2.5' : size === 'lg' ? 'px-4' : 'px-3')\n  const togglePadding = $derived(size === 'sm' ? 'pr-2' : size === 'lg' ? 'pr-3' : 'pr-2.5')\n</script>\n\n<div class=\"flex w-full flex-col gap-2\">\n  <div class={wrapperClasses} data-uipkge data-slot=\"password-input\" data-size={size} data-variant={variant}>\n    <input\n      bind:this={ref}\n      value={inputValue}\n      oninput={handleInput}\n      type={computedType}\n      {placeholder}\n      {disabled}\n      {readonly}\n      {autocomplete}\n      {...restProps}\n      class={cn('placeholder:text-muted-foreground w-full min-w-0 flex-1 bg-transparent outline-none', inputPadding)}\n    />\n    {#if showToggle}\n      <div class={cn('flex shrink-0 items-center', togglePadding)}>\n        <button\n          type=\"button\"\n          aria-label={passwordVisible ? 'Hide password' : 'Show password'}\n          aria-pressed={passwordVisible}\n          disabled={disabled || readonly}\n          class=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 shrink-0 rounded p-0.5 transition-colors focus-visible:ring-1 focus-visible:outline-none disabled:cursor-not-allowed\"\n          onmousedown={(e) => e.preventDefault()}\n          onclick={toggleVisibility}\n        >\n          <!-- Visible password → EyeOff (click to hide); hidden → Eye (click to show). Matches Input. -->\n          {#if passwordVisible}\n            <EyeOff class=\"size-4\" aria-hidden=\"true\" />\n          {:else}\n            <Eye class=\"size-4\" aria-hidden=\"true\" />\n          {/if}\n        </button>\n      </div>\n    {/if}\n  </div>\n\n  {#if showStrength && inputValue}\n    <div\n      class=\"flex flex-col gap-1.5\"\n      role=\"status\"\n      aria-live=\"polite\"\n      aria-label={`Password strength: ${strength.label}`}\n    >\n      <div class=\"bg-muted h-1.5 w-full overflow-hidden rounded-full\" aria-hidden=\"true\">\n        <div\n          class={cn('h-full rounded-full transition-[width] duration-300', strength.barColor)}\n          style=\"width: {strength.percent}%\"\n        ></div>\n      </div>\n      <div class=\"flex items-center justify-between text-xs\">\n        <span class={cn(strength.color, 'font-medium capitalize')}>{strength.label}</span>\n        {#if minLength > 0}\n          <span class={meetsMinLength ? 'text-success' : 'text-muted-foreground'}>\n            {inputValue.length} / {minLength} chars\n          </span>\n        {/if}\n      </div>\n    </div>\n  {/if}\n\n  {#if minLength > 0 && !showStrength && inputValue}\n    <p class=\"text-muted-foreground text-xs\">Minimum {minLength} characters</p>\n  {/if}\n</div>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/password-input/PasswordInput.svelte"
    },
    {
      "path": "packages/registry-svelte/components/password-input/password-input.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\nexport const passwordInputVariants = cva(\n  'flex w-full items-center gap-1.5 overflow-hidden border transition-[color,box-shadow] outline-none rounded-md',\n  {\n    variants: {\n      size: {\n        sm: 'h-8 text-xs',\n        default: 'h-9 text-base md:text-sm',\n        lg: 'h-11 text-base',\n      },\n      variant: {\n        outlined: 'border-input bg-transparent shadow-xs',\n        filled: 'border-transparent bg-muted/50 shadow-none',\n        borderless: 'border-transparent bg-transparent shadow-none',\n      },\n    },\n    defaultVariants: {\n      size: 'default',\n      variant: 'outlined',\n    },\n  },\n)\n\nexport type PasswordInputVariants = VariantProps<typeof passwordInputVariants>\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/password-input/password-input.variants.ts"
    },
    {
      "path": "packages/registry-svelte/components/password-input/index.ts",
      "content": "export {\n  default as PasswordInput,\n  getPasswordStrength,\n  type PasswordInputProps,\n  type PasswordStrength,\n} from './PasswordInput.svelte'\nexport { passwordInputVariants, type PasswordInputVariants } from './password-input.variants'\n",
      "type": "registry:ui",
      "target": "~/src/lib/components/ui/password-input/index.ts"
    }
  ],
  "dependencies": [
    "@lucide/svelte",
    "class-variance-authority"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "description": "Password input with show/hide toggle (Eye/EyeOff) and optional strength meter (weak/fair/good/strong with colored bar). Supports min length display, disabled, placeholder, and size variants.",
  "categories": [
    "control",
    "form"
  ]
}