v4.0.0-beta.4

Primitive Field

<BasePrimitiveField /> ยท A form control container.

vue
<script setup lang="ts">
const value = ref('')
</script>

<template>
  <BasePrimitiveField required>
    <div class="relative">
      <BasePrimitiveFieldController>
        <BaseInput
          v-model="value"
          rounded="md"
          placeholder="Username"
        />
      </BasePrimitiveFieldController>
      <div class="absolute z-0 end-4 top-3 pointer-events-none">
        <BasePrimitiveFieldLoadingIndicator />
        <BasePrimitiveFieldSuccessIndicator />
        <BasePrimitiveFieldErrorIndicator />
      </div>
    </div>
  </BasePrimitiveField>
</template>

Features

  • Fully customizable
  • Handles all input states
  • Handles accessibiliy
  • Handles input orientation
  • Handles label and description

Anatomy

This component is the atomic version of the Field component. It is meant, unlike the field, to let you compose the structure of your inputs. It has a few sub-components that you can use to build your form fields the way you want them in your project. Take a look at the anatomy of the component:

vue
<template>
  <BasePrimitiveField>
    <BasePrimitiveFieldLabel>
      <!-- Your label here -->
      <BasePrimitiveFieldRequiredIndicator />
    </BasePrimitiveFieldLabel>
    <BasePrimitiveFieldController>
      <!-- Your input component here -->
    </BasePrimitiveFieldController>
    <div>
      <BasePrimitiveFieldLoadingIndicator />
      <BasePrimitiveFieldSuccessIndicator />
      <BasePrimitiveFieldErrorIndicator />
    </div>
    <BasePrimitiveFieldDescription>
      <!-- Your description here -->
    </BasePrimitiveFieldDescription>
  </BasePrimitiveField>
</template>

API Reference

This component has props that you can use to modify its visual style.

Each of the field related components has its own set of props and slots that are detailed in the API reference section. Use them to build your unique input fields.

Field

Prop Type
state
default:"idle"
"error" | "idle" | "loading" | "success"
id
default:-
string
name
default:-
string
required
default:false
boolean
disabled
default:-
boolean
fieldset
default:-
boolean
Slot Type
#default
{ inputRef: (el: any) => void; inputAttrs: Record<string, any>; }

Label

Slot Type
#default
{}

Controller

Slot Type
#default
{ inputRef: (el: any) => void; inputAttrs: { id?: undefined; name?: undefined; required?: undefined; disabled?: undefined; 'aria-labelledby'?: undefined; 'aria-describedby'?: undefined; 'aria-required'?: undefined; 'aria-invalid'?: undefined; 'aria-errormessage'?: undefined; } | { ...; }; }

Loading indicator

Slot Type
#default
{}

Customization

Your can override the component default CSS variables in your main.css file.

css
@theme {
  /* Field elements variables */
  --color-field-label: var(--color-muted-600);
  --color-field-description: var(--color-muted-500);
  --color-field-loading: var(--color-muted-400);
}

Examples

Text input

Fields can be used with any input component. Fields handle all states an input component can go through, such as idle, loading, success and error. Here is an example using the BaseInput component:

Username
Username
Username
Username
vue
<script setup lang="ts">
const states = ['idle', 'loading', 'success', 'error'] as const
</script>

<template>
  <BasePrimitiveField v-for="state in states" :key="state" :state="state">
    <div class="w-full inline-flex">
      <BasePrimitiveFieldLabel class="flex items-center justify-between w-full">
        <div>
          <span>Username</span>
          <BasePrimitiveFieldRequiredIndicator />
        </div>
      </BasePrimitiveFieldLabel>
    </div>
    <div class="relative">
      <BasePrimitiveFieldController>
        <BaseInput placeholder="username..." />
      </BasePrimitiveFieldController>
      <div class="absolute z-0 end-4 top-3 pointer-events-none">
        <BasePrimitiveFieldLoadingIndicator />
        <BasePrimitiveFieldSuccessIndicator />
        <BasePrimitiveFieldErrorIndicator />
      </div>
    </div>
    <div class="mt-2 flex flex-col">
      <BasePrimitiveFieldError class="mb-1 block">
        The input is invalid because ...
      </BasePrimitiveFieldError>
      <BasePrimitiveFieldDescription>
        Lorem ipsum dolor sit amet consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. ...
        <BaseLink to="#" class="text-primary-600 dark:text-primary-400">
          Learn more
        </BaseLink>
      </BasePrimitiveFieldDescription>
    </div>
  </BasePrimitiveField>
</template>

Select

Fields can be used with any input component. Fields handle all states an input component can go through, such as idle, loading, success and error. Here is an example using the BaseSelect component:

Select an option
Select an option
Select an option
Select an option
vue
<script setup lang="ts">
const states = ['idle', 'loading', 'success', 'error'] as const
</script>

<template>
  <BasePrimitiveField v-for="state in states" :key="state">
    <div class="w-full inline-flex">
      <BasePrimitiveFieldLabel class="flex items-center justify-between w-full">
        <div>
          <span>Select an option</span>
          <BasePrimitiveFieldRequiredIndicator />
        </div>
      </BasePrimitiveFieldLabel>
    </div>
    <div class="relative">
      <BasePrimitiveFieldController>
        <BaseSelect v-model="value" placeholder="Select a value...">
          <BaseSelectItem value="1">
            Option 1
          </BaseSelectItem>
          <BaseSelectItem value="2">
            Option 2
          </BaseSelectItem>
          <BaseSelectItem value="3">
            Option 3
          </BaseSelectItem>
          <BaseSelectItem value="4">
            Option 4
          </BaseSelectItem>
        </BaseSelect>
      </BasePrimitiveFieldController>
      <div class="absolute z-0 end-10 top-3 pointer-events-none">
        <BasePrimitiveFieldLoadingIndicator />
        <BasePrimitiveFieldSuccessIndicator />
        <BasePrimitiveFieldErrorIndicator />
      </div>
    </div>
  </BasePrimitiveField>
</template>

Autocomplete

Fields can be used with any input component. Fields handle all states an input component can go through, such as idle, loading, success and error. Here is an example using the BaseAutocomplete component:

Choose an option
Choose an option
Choose an option
Choose an option
vue
<script setup lang="ts">
const states = ['idle', 'loading', 'success', 'error'] as const
</script>

<template>
  <BasePrimitiveField v-for="state in states" :key="state" :state="state">
    <div class="w-full inline-flex">
      <BasePrimitiveFieldLabel class="flex items-center justify-between w-full">
        <div>
          <span>Choose an option</span>
          <BasePrimitiveFieldRequiredIndicator />
        </div>
      </BasePrimitiveFieldLabel>
    </div>
    <div class="relative">
      <BasePrimitiveFieldController>
        <BaseAutocomplete
          placeholder="autocomplete placeholder"
          clearable
        >
          <BaseAutocompleteItem value="1">
            Option 1
          </BaseAutocompleteItem>
          <BaseAutocompleteItem value="2">
            Option 2
          </BaseAutocompleteItem>
          <BaseAutocompleteItem value="3">
            Option 3
          </BaseAutocompleteItem>
          <BaseAutocompleteItem value="4">
            Option 4
          </BaseAutocompleteItem>
          <BaseAutocompleteItem value="5">
            Option 5
          </BaseAutocompleteItem>
          <BaseAutocompleteItem value="6">
            Option 6
          </BaseAutocompleteItem>
        </BaseAutocomplete>
      </BasePrimitiveFieldController>
      <div class="absolute z-0 end-10 top-3 pointer-events-none">
        <BasePrimitiveFieldLoadingIndicator />
        <BasePrimitiveFieldSuccessIndicator />
        <BasePrimitiveFieldErrorIndicator />
      </div>
    </div>
  </BasePrimitiveField>
</template>

File input

Fields can be used with any input component. Fields handle all states an input component can go through, such as idle, loading, success and error. Here is an example using the BaseInputFile component:

Upload
Upload
Upload
Upload
vue
<script setup lang="ts">
const states = ['idle', 'loading', 'success', 'error'] as const
</script>

<template>
  <BasePrimitiveField v-for="state in states" :key="state" :state="state">
    <div class="w-full inline-flex">
      <BasePrimitiveFieldLabel class="flex items-center justify-between w-full">
        <div>
          <span>Upload file</span>
          <BasePrimitiveFieldRequiredIndicator />
        </div>
      </BasePrimitiveFieldLabel>
    </div>
    <div class="relative">
      <BasePrimitiveFieldController>
        <BaseInputFile placeholder="placeholder" />
      </BasePrimitiveFieldController>
      <div class="absolute z-0 end-4 top-3 pointer-events-none">
        <BasePrimitiveFieldLoadingIndicator />
        <BasePrimitiveFieldSuccessIndicator />
        <BasePrimitiveFieldErrorIndicator />
      </div>
    </div>
  </BasePrimitiveField>
</template>

Number input

Fields can be used with any input component. Fields handle all states an input component can go through, such as idle, loading, success and error. Here is an example using the BaseInputNumber component:

Quantity
Quantity
Quantity
Quantity
vue
<script setup lang="ts">
const states = ['idle', 'loading', 'success', 'error'] as const
</script>

<template>
  <BasePrimitiveField v-for="state in states" :key="state" :state="state">
    <div class="w-full inline-flex">
      <BasePrimitiveFieldLabel class="flex items-center justify-between w-full">
        <div>
          <span>Quantity</span>
          <BasePrimitiveFieldRequiredIndicator />
        </div>
      </BasePrimitiveFieldLabel>
    </div>
    <div class="relative">
      <BasePrimitiveFieldController>
        <BaseInputNumber placeholder="placeholder" />
      </BasePrimitiveFieldController>
      <div class="absolute z-0 end-12 top-3 pointer-events-none">
        <BasePrimitiveFieldLoadingIndicator />
        <BasePrimitiveFieldSuccessIndicator />
        <BasePrimitiveFieldErrorIndicator />
      </div>
    </div>
    <div class="mt-2 flex flex-col">
      <BasePrimitiveFieldError class="mb-1 block">
        The input is invalid because ...
      </BasePrimitiveFieldError>
      <BasePrimitiveFieldDescription>
        Lorem ipsum dolor sit amet consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. ...
        <BaseLink to="#" class="text-primary-600 dark:text-primary-400">
          Learn more
        </BaseLink>
      </BasePrimitiveFieldDescription>
    </div>
  </BasePrimitiveField>
</template>

Textarea

Fields can be used with any input component. Fields handle all states an input component can go through, such as idle, loading, success and error. Here is an example using the BaseTextarea component:

Message
Message
Message
Message
vue
<script setup lang="ts">
const states = ['idle', 'loading', 'success', 'error'] as const
</script>

<template>
  <BasePrimitiveField v-for="state in states" :key="state" :state="state">
    <div class="w-full inline-flex">
      <BasePrimitiveFieldLabel class="flex items-center justify-between w-full">
        <div>
          <span>Message</span>
          <BasePrimitiveFieldRequiredIndicator />
        </div>
      </BasePrimitiveFieldLabel>
    </div>
    <div class="relative">
      <BasePrimitiveFieldController>
        <BaseTextarea placeholder="Write a message..." />
      </BasePrimitiveFieldController>
      <div class="absolute z-0 end-4 top-3 pointer-events-none">
        <BasePrimitiveFieldLoadingIndicator />
        <BasePrimitiveFieldSuccessIndicator />
        <BasePrimitiveFieldErrorIndicator />
      </div>
    </div>
  </BasePrimitiveField>
</template>

Horizontal orientation

Fields can render in a horizontal orientation. Here is an example using the BaseInput component:

Username Lorem ipsum dolor sit amet consectetur adipiscing elit.
vue
<script setup lang="ts">
const states = ['idle', 'loading', 'success', 'error'] as const
</script>

<template>
  <div class="grid grid-cols-3 gap-8 gap-x-10 max-w-2xl">
    <BasePrimitiveField class="grid grid-cols-subgrid col-span-3" state="loading">
      <div class="flex flex-col justify-center gap-1 relative">
        <BasePrimitiveFieldLabel>
          <span>Horizontal</span>
          <BasePrimitiveFieldRequiredIndicator />
        </BasePrimitiveFieldLabel>
        <BasePrimitiveFieldDescription>
          Lorem ipsum dolor sit amet consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. ...
          <BaseLink to="#" class="text-primary-600 dark:text-primary-400">
            Learn more
          </BaseLink>
        </BasePrimitiveFieldDescription>
        <div class="absolute z-0 end-0 top-0 pointer-events-none">
          <BasePrimitiveFieldLoadingIndicator />
          <BasePrimitiveFieldSuccessIndicator />
          <BasePrimitiveFieldErrorIndicator />
        </div>
      </div>

      <div class="col-span-2">
        <div class="relative">
          <BasePrimitiveFieldController>
            <BaseInput v-model="value" />
          </BasePrimitiveFieldController>
          <div class="absolute z-0 end-4 top-3 pointer-events-none">
            <BasePrimitiveFieldLoadingIndicator />
            <BasePrimitiveFieldSuccessIndicator />
            <BasePrimitiveFieldErrorIndicator />
          </div>
        </div>
      </div>
    </BasePrimitiveField>
  </div>
</template>