Float Label
float-labeluiFloating label wrapper for any input element. The label floats up when the input is focused or has a value. Wrap it around any input, select, or textarea. Supports required indicator and disabled state.
Installation
$pnpm dlx shadcn-svelte@latest add https://uipkge.dev/r/svelte/float-label.json$npx shadcn-svelte@latest add https://uipkge.dev/r/svelte/float-label.json$yarn dlx shadcn-svelte@latest add https://uipkge.dev/r/svelte/float-label.json$bunx shadcn-svelte@latest add https://uipkge.dev/r/svelte/float-label.jsonManifest:
https://uipkge.dev/r/svelte/float-label.jsonInstalls to:src/lib/components/ui/float-label/Examples
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
label | string | — | required |
required | boolean | false | optional |
disabled | boolean | false | optional |
ref | HTMLDivElement | null | — | optional |
Files installed (2)
src/lib/components/ui/float-label/FloatLabel.svelte3.1 kB
<script lang="ts" module> import type { HTMLAttributes } from 'svelte/elements' export interface FloatLabelProps extends HTMLAttributes<HTMLDivElement> { label: string required?: boolean disabled?: boolean ref?: HTMLDivElement | null } // Client-only counter for generated control ids. Assigned in onMount (never // during SSR) so server and client markup stay identical. let nextControlId = 0 </script> <script lang="ts"> import { onMount } from 'svelte' import { cn } from '$lib/utils' let { class: className, label, required = false, disabled = false, children, ref = $bindable(null), ...restProps }: FloatLabelProps = $props() let isFocused = $state(false) let hasValue = $state(false) let controlId = $state<string | undefined>(undefined) const isFloating = $derived(isFocused || hasValue) function checkValue(target: EventTarget | null) { const el = target as HTMLElement | null if (!el) return if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) { hasValue = !!el.value } else if (el instanceof HTMLSelectElement) { hasValue = !!el.value } else { const input = el.querySelector?.('input, textarea, select') as | HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null if (input) hasValue = !!input.value } } function handleFocusin(event: FocusEvent) { isFocused = true checkValue(event.target) } function handleFocusout(event: FocusEvent) { isFocused = false checkValue(event.target) } function handleInput(event: Event) { checkValue(event.target) } // Check for prefilled value on mount + associate label with the control. onMount(() => { if (!ref) return const input = ref.querySelector('input, textarea, select') as | HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null if (!input) return hasValue = !!input.value if (input.id) { controlId = input.id } else { nextControlId += 1 input.id = `float-label-${nextControlId}` controlId = input.id } }) const wrapperClasses = $derived(cn('relative flex flex-col', disabled && 'cursor-not-allowed opacity-50', className)) const labelClasses = $derived( cn( 'text-muted-foreground pointer-events-none absolute left-3 z-10 bg-transparent px-1 text-sm transition-[color,background-color,top,translate,scale] duration-200', !isFloating && 'top-1/2 -translate-y-1/2', isFloating && 'bg-background text-foreground top-0 -translate-y-1/2 scale-75', isFocused && 'text-ring', required && "after:text-destructive after:ml-0.5 after:content-['*']", ), ) </script> <div bind:this={ref} class={wrapperClasses} data-uipkge data-slot="float-label" data-floating={isFloating} onfocusin={handleFocusin} onfocusout={handleFocusout} oninput={handleInput} onchange={handleInput} {...restProps} > <label for={controlId} class={labelClasses}> {label} </label> {@render children?.()} </div>src/lib/components/ui/float-label/index.ts0.1 kB
export { default as FloatLabel, type FloatLabelProps } from './FloatLabel.svelte'
Raw manifest:https://uipkge.dev/r/svelte/float-label.json