Input
Single-line text field on the card surface with a focus ring; elevation adds sunken, raised and floating depth.
import { Input } from "@/components/ui/input";
export default function Demo() { return ( <div className="grid w-full max-w-sm gap-3"> <Input placeholder="Magnificent Four" /> <Input defaultValue="MAG4" /> <Input placeholder="Invalid" aria-invalid defaultValue="mag 4" /> <Input placeholder="Disabled" disabled /> <Input type="file" /> </div> );}<script setup lang="ts">import { Input } from "@/components/ui/input";</script>
<template> <div class="grid w-full max-w-sm gap-3"> <Input placeholder="Magnificent Four" /> <Input default-value="MAG4" /> <Input placeholder="Invalid" aria-invalid="true" default-value="mag 4" /> <Input placeholder="Disabled" disabled /> <Input type="file" /> </div></template><script lang="ts"> import { Input } from "$lib/components/ui/input";
let name = $state("MAG4");</script>
<div class="grid w-full max-w-sm gap-3"> <Input placeholder="Magnificent Four" /> <Input bind:value={name} /> <Input placeholder="Invalid" aria-invalid="true" value="mag 4" /> <Input placeholder="Disabled" disabled /> <Input type="file" /></div>Installation
Section titled “Installation”npx shadcn@latest add @edmi-ui/inputpnpm dlx shadcn@latest add @edmi-ui/inputyarn dlx shadcn@latest add @edmi-ui/inputbunx shadcn@latest add @edmi-ui/inputPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install @base-ui/reactTerminal window pnpm add @base-ui/reactTerminal window yarn add @base-ui/reactTerminal window bun add @base-ui/reactAdd the Edmi components this one builds on:
elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-vue@latest add @edmi-ui/inputpnpm dlx shadcn-vue@latest add @edmi-ui/inputyarn dlx shadcn-vue@latest add @edmi-ui/inputbunx shadcn-vue@latest add @edmi-ui/inputPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Install the dependencies:
Terminal window npm install @vueuse/coreTerminal window pnpm add @vueuse/coreTerminal window yarn add @vueuse/coreTerminal window bun add @vueuse/coreAdd the Edmi components this one builds on:
elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
npx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/input.jsonpnpm dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/input.jsonyarn dlx shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/input.jsonbunx --bun shadcn-svelte@latest add https://viandwi24.github.io/edmi-ui/r/svelte/input.jsonPick your package manager above; the choice is remembered. Add the @edmi-ui registry first, see Getting started.
Add the Edmi components this one builds on:
elevation.Copy the code from Component source into your project and update the import paths to match your aliases.
import { Input } from "@/components/ui/input";
<div className="grid w-full max-w-sm gap-3"> <Input placeholder="Magnificent Four" /> <Input defaultValue="MAG4" /> <Input placeholder="Invalid" aria-invalid defaultValue="mag 4" /> <Input placeholder="Disabled" disabled /> <Input type="file" /></div><script setup lang="ts">import { Input } from "@/components/ui/input";</script>
<template> <div class="grid w-full max-w-sm gap-3"> <Input placeholder="Magnificent Four" /> <Input default-value="MAG4" /> <Input placeholder="Invalid" aria-invalid="true" default-value="mag 4" /> <Input placeholder="Disabled" disabled /> <Input type="file" /> </div></template><script lang="ts"> import { Input } from "$lib/components/ui/input";</script>
<div class="grid w-full max-w-sm gap-3"> <Input placeholder="Magnificent Four" /> <Input /> <Input placeholder="Invalid" aria-invalid="true" value="mag 4" /> <Input placeholder="Disabled" disabled /> <Input type="file" /></div>Component source
Section titled “Component source”"use client";
import { Input as InputPrimitive } from "@base-ui/react/input";import { cn } from "cn";import type * as React from "react";
import { type Elevation, useElevation } from "@/registry/edmi/ui/elevation";
// ✦ depth (v4): fields sink (-1) in layered mode; focus swaps the edge for the ringconst fieldElevation = { sunken: "border-sk-bd bg-sk-bg shadow-sunken focus-visible:bg-card", flat: "", raised: "border-transparent bg-[image:linear-gradient(var(--bv-face-b),var(--bv-face-b))] shadow-raised focus-visible:border-ring focus-visible:shadow-ring", floating: "border-transparent bg-[image:linear-gradient(var(--bv-face-b),var(--bv-face-b))] shadow-floating focus-visible:border-ring focus-visible:shadow-ring",};
function Input({ className, type, elevation, ...props}: React.ComponentProps<"input"> & { /** ✦ depth: sunken -1, flat 0, raised +1, floating +2. */ elevation?: Elevation;}) { const level = useElevation(elevation, "field"); return ( <InputPrimitive type={type} data-slot="input" className={cn( "flex h-9 w-full min-w-0 items-center gap-2 rounded-md border border-input bg-card px-3 text-sm text-foreground outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:shadow-ring disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-50 aria-invalid:border-destructive aria-invalid:shadow-ring-error data-[invalid]:border-destructive data-[invalid]:shadow-ring-error", fieldElevation[level], className, )} {...props} /> );}
export { Input };<script setup lang="ts">import type { HTMLAttributes } from 'vue'import { useVModel } from '@vueuse/core'import { cn } from '@/registry/edmi/lib/utils'import { type Elevation, useElevation } from '@/registry/edmi/ui/elevation'
const props = withDefaults(defineProps<{ defaultValue?: string | number modelValue?: string | number /** ✦ depth: sunken -1, flat 0, raised +1, floating +2 */ elevation?: Elevation class?: HTMLAttributes['class']}>(), { elevation: undefined })
const emits = defineEmits<{ (e: 'update:modelValue', payload: string | number): void}>()
const modelValue = useVModel(props, 'modelValue', emits, { passive: true, defaultValue: props.defaultValue,})
// ✦ depth (v4): fields sink (-1) in layered mode; focus swaps the edge for the ringconst fieldElevation = { sunken: 'border-sk-bd bg-sk-bg shadow-sunken focus-visible:bg-card', flat: '', raised: 'border-transparent bg-[image:linear-gradient(var(--bv-face-b),var(--bv-face-b))] shadow-raised focus-visible:border-ring focus-visible:shadow-ring', floating: 'border-transparent bg-[image:linear-gradient(var(--bv-face-b),var(--bv-face-b))] shadow-floating focus-visible:border-ring focus-visible:shadow-ring',}
const level = useElevation(() => props.elevation, 'field')</script>
<template> <input v-model="modelValue" data-slot="input" :class="cn( 'flex h-9 w-full min-w-0 items-center gap-2 rounded-md border border-input bg-card px-3 text-sm text-foreground outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:shadow-ring disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-50 aria-invalid:border-destructive aria-invalid:shadow-ring-error', fieldElevation[level], props.class, )" ></template>export { default as Input } from "./Input.vue";<script lang="ts"> import { cn, type WithElementRef } from "$lib/utils.js"; import { type Elevation, useElevation } from "$lib/registry/ui/elevation/index.js"; import type { HTMLInputAttributes, HTMLInputTypeAttribute } from "svelte/elements";
type InputType = Exclude<HTMLInputTypeAttribute, "file">;
type Props = WithElementRef< Omit<HTMLInputAttributes, "type"> & ({ type: "file"; files?: FileList } | { type?: InputType; files?: undefined }) > & { /** ✦ depth: sunken -1, flat 0, raised +1, floating +2. */ elevation?: Elevation; }; // ✦ depth (v4): fields sink (-1) in layered mode; focus swaps the edge for the ring const fieldElevation = { sunken: "border-sk-bd bg-sk-bg shadow-sunken focus-visible:bg-card", flat: "", raised: "border-transparent bg-[image:linear-gradient(var(--bv-face-b),var(--bv-face-b))] shadow-raised focus-visible:border-ring focus-visible:shadow-ring", floating: "border-transparent bg-[image:linear-gradient(var(--bv-face-b),var(--bv-face-b))] shadow-floating focus-visible:border-ring focus-visible:shadow-ring", };
let { ref = $bindable(null), value = $bindable(), type, files = $bindable(), class: className, elevation = "auto", "data-slot": dataSlot = "input", ...restProps }: Props = $props();
const level = useElevation(() => elevation, "field");</script>
{#if type === "file"} <input bind:this={ref} data-slot={dataSlot} class={cn( "flex h-9 w-full min-w-0 items-center gap-2 rounded-md border border-input bg-card px-3 text-sm text-foreground outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:shadow-ring disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-50 aria-invalid:border-destructive aria-invalid:shadow-ring-error", fieldElevation[level.current], className )} type="file" bind:files bind:value {...restProps} />{:else} <input bind:this={ref} data-slot={dataSlot} class={cn( "flex h-9 w-full min-w-0 items-center gap-2 rounded-md border border-input bg-card px-3 text-sm text-foreground outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:shadow-ring disabled:cursor-not-allowed disabled:bg-muted disabled:opacity-50 aria-invalid:border-destructive aria-invalid:shadow-ring-error", fieldElevation[level.current], className )} {type} bind:value {...restProps} />{/if}import Root from "./input.svelte";
export { Root, // Root as Input,};Elevation ✦
Section titled “Elevation ✦”See the Elevation guide for the levels, roles and layered mode.
Fields are flat by default and sink (-1) in layered mode. sunken shows a soft inset well (focus swaps the edge for the ring), raised (+1) a bevel and floating (+2) a bevel with a soft drop.
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
Sunken (-1)
Flat (0)
Raised (+1)
Floating (+2)
import { Input } from "@/components/ui/input";
const levels = [ { value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" },] as const;
export default function Demo() { return ( <div className="flex flex-col gap-5"> {levels.map(({ value, label }) => ( <div key={value} className="flex flex-col gap-2"> <p className="text-xs font-medium text-muted-foreground">{label}</p> <Input elevation={value} placeholder="Magnificent Four" className="max-w-sm" /> </div> ))} </div> );}<script setup lang="ts">import { Input } from "@/components/ui/input";
const levels = [{ value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" }];</script>
<template> <div class="flex flex-col gap-5"> <div v-for="level in levels" :key="level.value" class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{{ level.label }}</p> <Input :elevation="level.value" placeholder="Magnificent Four" class="max-w-sm" /> </div> </div></template><script lang="ts"> import { Input } from "$lib/components/ui/input";
const levels = [{ value: "sunken", label: "Sunken (-1)" }, { value: "flat", label: "Flat (0)" }, { value: "raised", label: "Raised (+1)" }, { value: "floating", label: "Floating (+2)" }] as const;</script>
<div class="flex flex-col gap-5"> {#each levels as level (level.value)} <div class="flex flex-col gap-2"> <p class="text-xs font-medium text-muted-foreground">{level.label}</p> <Input elevation={level.value} placeholder="Magnificent Four" class="max-w-sm" /> </div> {/each}</div>| Prop | Type | Default |
|---|---|---|
| Props | React.ComponentProps<"input"> (Base UI Input) |
none |
| aria-invalid | boolean |
false |
elevation |
"auto" | "sunken" | "flat" | "raised" | "floating" |
"auto" |
✦ Edmi additions
Section titled “✦ Edmi additions”- No size prop; the state comes from
disabledandaria-invalid. - A ring halo on focus and error; the sunken level swaps its edge for the ring on focus.