Hardcore Input
The ultimate form input validator. Synthesizes 8-bit retro electronic sound effects on the fly via Web Audio API (zero audio dependencies), while triggering neobrutalist emoji bounces and physical shake on validation state changes.
Demo
基础用法(Blur 校验)
多规则校验(Input 实时校验)
禁用音效
关闭抖动 (shakeOnError=false)
只读状态 (readonly)
Installation
pnpm dlx brutx-vue@latest add hardcore-inputUsage
<script setup>
import { ref } from 'vue'
import { HardcoreInput } from 'brutx-ui-vue'
const value = ref('')
const minLength = (val) => val.length >= 5 || 'At least 5 characters required'
const hasNumber = (val) => /\d/.test(val) || 'Must contain a number'
</script>
<template>
<HardcoreInput
v-model="value"
placeholder="Enter content..."
:rules="[minLength, hasNumber]"
validate-on="blur"
/>
</template>Validation Trigger Modes
| Mode | Description |
|---|---|
blur | Validate on blur (default) |
input | Validate in real-time on every input |
submit | Validate when validate() is called externally |
Sound System
Uses Web Audio API via the useAudioEngine composable to synthesize three sound effects:
| Sound | Waveform | Description |
|---|---|---|
type | Triangle | Typing sound with 50ms throttle |
success | Sine | Validation passed, rising frequency |
fail | Square | Validation failed, falling frequency |
Sound can be disabled via the sound prop.
Visual Feedback
- Success: Input border changes to
--brutal-successcolor, cool emoji SVG appears on the right - Error: Input border changes to
--brutal-destructivecolor, angry emoji SVG appears on the right, input shakes with animation - Default: No additional feedback
Form Integration
HardcoreInput integrates with the Form system. When placed inside a FormField, it automatically syncs validation state:
<Form v-slot="{ handleSubmit }">
<FormField v-slot="{ componentField }" name="username">
<FormItem>
<FormLabel>Username</FormLabel>
<HardcoreInput v-bind="componentField" :rules="[minLength]" />
<FormMessage />
</FormItem>
</FormField>
</Form>Composables
useFormFieldValidation
The internal validation state machine of HardcoreInput has been extracted as a standalone useFormFieldValidation composable. It can be reused in any custom form control to share the same validation logic (rule evaluation, state machine transitions, error message management, trigger timing control). It does not depend on vee-validate and is suitable for scenarios where the Form system cannot be used or lightweight inline validation is needed.
import { useFormFieldValidation } from 'brutx-ui-vue'
import type { UseFormFieldValidationOptions, ValidationRule, ValidateOn } from 'brutx-ui-vue'
// Validation rules: return true for pass, return string for error message
const minLength: ValidationRule<string> = (val) => val.length >= 5 || 'At least 5 characters required'
const hasNumber: ValidationRule<string> = (val) => /\d/.test(val) || 'Must contain a number'
const {
validationState, // Current validation state
errorMessage, // Current error message
validate, // Trigger validation manually
reset, // Reset to default state
shouldValidateOnInput, // Whether to validate on input
shouldValidateOnBlur, // Whether to validate on blur
} = useFormFieldValidation({
rules: [minLength, hasNumber],
validateOn: 'blur',
defaultErrorMessage: 'Validation failed',
onValidationChange: (state, message) => {
// State change callback, can be used for side effects like sound and shake
},
})UseFormFieldValidationOptions
| Prop | Type | Default | Description |
|---|---|---|---|
rules | MaybeRefOrGetter<ValidationRule<TValue>[]> | [] | Array of validation rules; when empty, validate always returns true and sets state to default |
validateOn | MaybeRefOrGetter<'input' | 'blur' | 'submit'> | 'submit' | Validation trigger timing (only affects shouldValidateOnInput / shouldValidateOnBlur determination; the caller must invoke validate in the corresponding event) |
defaultErrorMessage | MaybeRefOrGetter<string> | 'Invalid value' | Default error message when a rule returns false |
onValidationChange | (state: ValidationState, message?: string) => void | — | Callback on validation state change (only triggered when state actually changes, avoiding duplicates) |
Return Values
| Property | Type | Description |
|---|---|---|
validationState | Ref<'default' | 'success' | 'error'> | Current validation state (reactive) |
errorMessage | Ref<string> | Current error message (reactive, empty string when no error) |
validate(value) | (value: TValue) => boolean | Validates the given value; returns true on pass, false on failure and writes to errorMessage |
reset() | () => void | Resets to default state and clears error message |
shouldValidateOnInput() | () => boolean | Whether configured to validate on input |
shouldValidateOnBlur() | () => boolean | Whether configured to validate on blur |
ValidationRule
type ValidationRule<TValue> = (value: TValue) => boolean | stringA rule function receives the current value. Return true to pass, return string to fail with that string as the error message, return false to fail using defaultErrorMessage.
Usage Example
Reuse the same validation logic in a custom input control:
<script setup lang="ts">
import { ref } from 'vue'
import { useFormFieldValidation } from 'brutx-ui-vue'
const value = ref('')
const { validationState, errorMessage, validate, shouldValidateOnBlur } = useFormFieldValidation({
rules: [(v) => v.length >= 3 || 'At least 3 characters'],
validateOn: 'blur',
})
function onBlur() {
if (shouldValidateOnBlur()) validate(value.value)
}
</script>
<template>
<input
v-model="value"
@blur="onBlur"
:aria-invalid="validationState === 'error'"
:class="{
'border-brutal-success': validationState === 'success',
'border-brutal-destructive': validationState === 'error',
}"
/>
<p v-if="errorMessage" class="text-brutal-destructive text-sm">{{ errorMessage }}</p>
</template>Note:
useFormFieldValidationis not bound to a specific control. All timing checks (shouldValidateOnInput/shouldValidateOnBlur) only return boolean values, and the caller decides in which event to invokevalidate. For thesubmittiming, the caller must explicitly invokevalidateexternally (e.g. on form submission).
Programmatic Control
Use the component ref to trigger validation and read the current validation state and error message; see API Reference for member types and behavior.
API Reference
| Name | Type | Default | Description |
|---|---|---|---|
modelValue | string | undefined | undefined | The current input string, available through v-model for controlled binding. |
DetailsDeclared typestring | undefined Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
sound | boolean | undefined | true | Enables or disables typing sounds and success/failure sounds on validation-state transitions. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
rules | ((val: string) => string | boolean)[] | undefined | []Factory evaluated per instance | String validation functions evaluated in order. Return true to pass, a string to fail with that message, or false to use the localized generic error. An empty array produces no validation error. |
DetailsDeclared type((val: string) => string | boolean)[] | undefined Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
shakeOnError | boolean | undefined | true | Enables the input shake feedback after validation fails. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
type | string | undefined | "text" | The HTML input type passed to the native input element. |
DetailsDeclared typestring | undefined Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
placeholder | string | undefined | undefined | The native placeholder text shown when the input is empty. |
DetailsDeclared typestring | undefined Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
disabled | boolean | undefined | false | Disables the native input element. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
readonly | boolean | undefined | false | Makes the native input element read-only. |
DetailsDeclared typeboolean | undefined Literal values
Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
validateOn | "input" | "submit" | "blur" | undefined | "blur" | Selects when the component automatically validates. For submit, the component does not listen for form submission; call the exposed validate() method explicitly. |
DetailsDeclared type"input" | "submit" | "blur" | undefined Literal values
Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
class | string | undefined | undefined | Custom CSS classes appended to the outer container around the input and validation message. |
DetailsDeclared typestring | undefined Default value details
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | |||
| Name | Signature or type | Description |
|---|---|---|
@update:modelValue | [value: string] | Emits the input string on user input or when IME composition completes. Intermediate composition values are not emitted. |
DetailsDeclared type[value: string] Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | ||
@validation-change | [state: "default" | "success" | "error", message?: string | undefined] | Emitted when the validation state changes or the error message changes while in the error state. Unchanged state/message pairs are not repeated; returning to default with empty rules is also reported.
|
DetailsDeclared type[state: "default" | "success" | "error", message?: string | undefined] Literal values
Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | ||
| Name | Signature or type | Description |
|---|---|---|
#default | {} | Replaces the default validation-face area to the right of the input; the slot has no parameters. |
DetailsDeclared type{}Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | ||
| Name | Signature or type | Description |
|---|---|---|
validate | () => boolean | Validates the current modelValue and returns whether it passes. Within a Form context, the form value is synchronized first. |
DetailsDeclared type() => boolean Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | ||
validationState | ValidationState | The validation state exposed on the component instance. Vue automatically unwraps its internal Ref, so read default, success, or error through hardcoreInputRef.value.validationState. |
DetailsDeclared typeValidationState Literal values
Referenced types ValidationState Type definition — ValidationStateexport type ValidationState = 'default' | 'success' | 'error' Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | ||
errorMessage | string | The current validation error exposed on the component instance. Vue automatically unwraps its internal Ref; the value is an empty string when there is no error. |
DetailsDeclared typestring Sourcepackages/ui/src/components/hardcore-input/HardcoreInput.vue | ||
Accessibility
- Input has
:aria-invalidand:aria-describedbyset - Error message has
aria-live="polite" - Shake and bounce animations are disabled when user prefers
prefers-reduced-motion: reduce