How to Style the OTP Input with is-complete Class in vue3-otp-input

The vue3-otp-input component automatically applies the is-complete CSS class to each input field once it contains a value, allowing you to style filled OTP boxes by targeting the combined selector .your-input-class.is-complete in your stylesheet.

The vue3-otp-input library by ejirocodes provides a customizable one-time password input component for Vue 3 applications. When implementing OTP inputs, providing visual feedback for filled fields improves user experience significantly. The component handles this automatically through its built-in is-complete class mechanism, which triggers specific styling when an input contains a value.

Understanding the is-complete Class Mechanism in vue3-otp-input

The automatic styling behavior originates in the SingleOtpInput component. In src/components/single-otp-input.vue, the input element uses a dynamic class binding that conditionally applies the is-complete class based on the input's current value.

At lines 154-155, the template contains:

<input
  ...
  :class="[inputClasses, conditionalClass, { 'is-complete': model }]"
  ...
/>

The model ref stores the current character value for that specific input field. When model contains any non-empty string, the object { 'is-complete': model } evaluates to truthy, causing Vue to apply the is-complete class to the element.

Implementing Custom Styles with input-classes and conditionalClass Props

The parent vue3-otp-input component exposes two key props that control CSS class distribution: input-classes and conditionalClass.

In src/components/vue3-otp-input.vue (lines 37-38), the component passes these props to each SingleOtpInput instance:

<SingleOtpInput
  ...
  :input-classes="inputClasses"
  :conditionalClass="conditionalClass?.[i]"
  ...
/>
  • input-classes: Applies base classes to every OTP input field
  • conditionalClass: Accepts an array of classes, applying a specific class to each corresponding input position

Basic Styling Example

To style filled OTP inputs, define CSS rules targeting the combination of your base input class and the state class. For example, if you passed input-classes="otp-box", use .otp-box.is-complete to style filled states:

<script setup lang="ts">
import { ref } from "vue";
import VOtpInput from "vue3-otp-input";

const otpValue = ref("");
</script>

<template>
  <v-otp-input
    v-model:value="otpValue"
    :num-inputs="4"
    input-classes="otp-box"
    :should-auto-focus="true"
  />
</template>

<style scoped>
.otp-box {
  width: 3rem;
  height: 3rem;
  font-size: 1.5rem;
  text-align: center;
  border: 2px solid #d1d5db;
  border-radius: 0.5rem;
  transition: all 0.2s ease;
}

.otp-box.is-complete {
  background-color: #dbeafe;
  border-color: #3b82f6;
  color: #1e40af;
}
</style>

Per-Input Styling with conditionalClass

For differentiated styling across OTP fields, such as color-coding each position when filled, combine conditionalClass with the is-complete selector:

<script setup lang="ts">
import { ref } from "vue";
import VOtpInput from "vue3-otp-input";

const otpValue = ref("");
const positionClasses = ["digit-1", "digit-2", "digit-3", "digit-4"];
</script>

<template>
  <v-otp-input
    v-model:value="otpValue"
    :num-inputs="4"
    input-classes="base-otp"
    :conditionalClass="positionClasses"
  />
</template>

<style scoped>
.base-otp {
  width: 40px;
  height: 40px;
  text-align: center;
  border: 1px solid #ccc;
  margin: 0 4px;
  border-radius: 4px;
}

/* Default filled state */
.base-otp.is-complete {
  font-weight: bold;
}

/* Position-specific filled states */
.digit-1.is-complete { background-color: #fee2e2; border-color: #ef4444; }
.digit-2.is-complete { background-color: #dcfce7; border-color: #22c55e; }
.digit-3.is-complete { background-color: #dbeafe; border-color: #3b82f6; }
.digit-4.is-complete { background-color: #f3e8ff; border-color: #a855f7; }
</style>

The conditionalClass array is forwarded to each SingleOtpInput via the index-specific binding conditionalClass?.[i], allowing you to target individual input positions with CSS.

Summary

  • The vue3-otp-input component automatically applies the is-complete CSS class to each input field when it contains a value.
  • This behavior is implemented in src/components/single-otp-input.vue through the conditional class binding { 'is-complete': model } at lines 154-155.
  • Use the input-classes prop to apply base styling to all OTP fields, and target .your-class.is-complete for filled states.
  • The conditionalClass prop accepts an array to apply unique classes to specific input positions, enabling per-field styling variations as implemented in src/components/vue3-otp-input.vue lines 37-38.

Frequently Asked Questions

How do I change the background color only when an OTP input is filled?

Target the is-complete class in combination with your base input class. If you passed input-classes="otp-box" to the component, define .otp-box.is-complete { background-color: #e0e0e0; } in your stylesheet. The component automatically toggles this class when the internal model ref contains a non-empty string.

Can I apply different styles to each OTP input field when filled?

Yes. Use the conditionalClass prop to pass an array of class names, where each index corresponds to an OTP input position. Then define CSS rules combining these position classes with is-complete, such as .position-1.is-complete { border-color: red; } and .position-2.is-complete { border-color: green; }.

Why isn't the is-complete class appearing on my OTP inputs?

Verify that you are using the correct CSS selector that combines your custom class (passed via input-classes) with is-complete. Also ensure the component is receiving the input-classes prop value correctly. The class only appears when the input actually contains a character, as determined by the model ref in single-otp-input.vue.

Does the is-complete class work with Tailwind CSS?

Yes. When using Tailwind, pass your utility classes via the input-classes prop, and use the @apply directive in your style block or arbitrary variants to style the is-complete state. For example: <v-otp-input input-classes="w-12 h-12 text-center border rounded" /> and in your CSS: .is-complete { @apply bg-blue-100 border-blue-500; }.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →