@fvc/input
v3.0.3
Published
`@fvc/input` provides FE-VIS styled input primitives on top of Ant Design. It keeps the Ant Design input API familiar while adding the design-system behavior used across FE-VIS applications: labels, required markers, error messaging, custom clear icons, p
Readme
@fvc/input
@fvc/input provides FE-VIS styled input primitives on top of Ant Design. It keeps the Ant Design input API familiar while adding the design-system behavior used across FE-VIS applications: labels, required markers, error messaging, custom clear icons, prefix/suffix handling, value transforms, and masked input support.
Installation
bun add @fvc/inputPeer Dependencies
The package expects these dependencies to be available in the consuming application:
bun add react antd antd-mask-input @fvc/icons @fvc/utilsImport
import { Input } from '@fvc/input';Quick Start
import { useState } from 'react';
import { Input } from '@fvc/input';
export function AccountForm() {
const [username, setUsername] = useState('');
return (
<Input
name="username"
label="Username"
placeholder="Enter username"
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
);
}Components
| Component | Use case |
| --- | --- |
| Input | Standard text, number, email, telephone, or URL input. |
| Input.Textarea | Multi-line text entry. |
| Input.Search | Search input with optional Ant Design search button behavior. |
| Input.Password | Password input with Ant Design visibility toggle behavior. |
| Input.Masked | Masked input powered by antd-mask-input. |
All variants share the FE-VIS input styling and support the common props where applicable.
Common Usage
Label and Required Marker
Use label to render the FE-VIS label above the field. Add withAsterisk when the field is required.
<Input
name="email"
label="Email"
withAsterisk
placeholder="[email protected]"
/>Error State
error can be a string or a boolean.
| Value | Behavior |
| --- | --- |
| string | Applies error styling and renders the message below the field. |
| true | Applies error styling without rendering a message. |
| false or undefined | Uses the default state. |
<Input
name="email"
label="Email"
error="Invalid email address"
placeholder="[email protected]"
/>Controlled and Uncontrolled Inputs
Use value and onChange for controlled fields:
<Input
name="tin"
label="TIN"
value={tin}
onChange={(event) => setTin(event.target.value)}
/>Use defaultValue when the field does not need to be controlled by React state:
<Input
name="country"
label="Country"
defaultValue="Azerbaijan"
/>Prefix and Suffix
Use prefix and suffix for compact adornments such as currency, units, or static hints.
<Input
name="price"
label="Price"
prefix="$"
suffix="USD"
placeholder="Enter amount"
/>suffixType controls suffix positioning.
| suffixType | Behavior |
| --- | --- |
| fixed | Renders the suffix as a standard input suffix. |
| sticky | Keeps the suffix visually aligned with the typed value. |
<Input
name="weight"
label="Weight"
suffix="kg"
suffixType="sticky"
value="100"
/>Text Transforms
textTransform transforms the entered value before the component stores it internally and before onChange receives the event.
| Transform | Result |
| --- | --- |
| uppercase | Converts text to uppercase. |
| lowercase | Converts text to lowercase. |
| capitalize | Capitalizes each word. |
| integer | Keeps digits only. |
<Input
name="code"
label="Code"
textTransform="uppercase"
placeholder="Enter code"
/>Number Input Without Native Controls
Set controls={false} to hide native number spinner controls.
<Input
name="amount"
label="Amount"
type="number"
controls={false}
placeholder="Enter amount"
/>Clear Button
allowClear enables the FE-VIS clear icon.
<Input
name="search"
label="Search"
allowClear
placeholder="Search..."
/>Subcomponents
Textarea
<Input.Textarea
name="description"
label="Description"
placeholder="Enter description"
/>Input.Textarea does not accept suffix, suffixType, or controls.
Search
<Input.Search
placeholder="Search records"
enterButton="Search"
/>Password
<Input.Password placeholder="Enter password" />Masked
<Input.Masked
name="phone"
label="Phone number"
mask="(999) 999-9999"
placeholder="Enter phone number"
/>Props
The base Input accepts Ant Design input props plus the FE-VIS additions below.
| Prop | Type | Description |
| --- | --- | --- |
| label | ReactNode | Renders a label above the field. |
| withAsterisk | boolean | Shows a required marker next to the label. |
| error | string \| boolean | Applies error styling and optionally renders an error message. |
| suffixType | 'fixed' \| 'sticky' | Controls suffix positioning. Defaults to fixed. |
| textTransform | 'uppercase' \| 'lowercase' \| 'capitalize' \| 'integer' | Transforms the value before change handling. |
| controls | boolean | Controls native number spinners. Defaults to true. |
| testId | string | Maps to data-testid. |
| variant | 'outlined' | Visual variant. Currently only outlined is supported. |
Consumer Example
import { Input } from '@fvc/input';
export function SettingsForm() {
return (
<form>
<Input
name="title"
label="Title"
withAsterisk
placeholder="Enter title"
/>
<Input.Textarea
name="description"
label="Description"
placeholder="Write a short description"
/>
<Input.Search placeholder="Search records" />
</form>
);
}Testing
Use testId when a stable test selector is needed.
<Input
name="customerName"
label="Customer name"
testId="customer-name-input"
/>screen.getByTestId('customer-name-input');CSS Classes
The component applies BEM-style classes to its DOM elements. These can be used for targeted styling overrides in consuming applications.
Root and layout
| Class | Applied when |
| --- | --- |
| .fvc-input-root | Always — the outermost wrapper <div>. |
| .fvc-input-label | Always — the <label> element above the field. |
| .fvc-input-label-asterisk | When withAsterisk={true} — the * span inside the label. |
| .fvc-input-error-text | When error is a string — the error message container below the field. |
Input element modifiers
| Class | Applied when |
| --- | --- |
| .fvc-input-outlined | Always — the only supported variant value. |
| .fvc-input-error | When error is truthy (string or true). |
| .fvc-input-empty | When the current value is empty or undefined. |
| .fvc-input-with-prefix | When prefix is provided. |
| .fvc-input-no-controls | When controls={false}. |
| .fvc-input-suffix-sticky | When suffixType="sticky" and suffix is provided. |
Variant root classes
Each compound component variant adds its own rootClassName to the antd wrapper element:
| Class | Variant |
| --- | --- |
| .fvc-input-textarea | Input.Textarea |
| .fvc-input-search | Input.Search |
| .fvc-input-password | Input.Password |
| .fvc-input-masked | Input.Masked |
Example override
// Target the error message text color for a specific form
.checkout-form .fvc-input-error-text {
color: var(--danger-700);
}
// Hide the spinner controls only inside a specific container
.summary-panel .fvc-input-no-controls input[type='number'] {
appearance: textfield;
}CSS Custom Properties
Override these variables in your global stylesheet. All variables are declared on :root in src/styles/variables.scss.
Base
| Variable | Default |
| --- | --- |
| --input-font-family | 'Roboto', sans-serif |
| --input-border-width | 1px |
| --input-addon-text-color | var(--blue-gray-600) |
| --input-addon-min-width | 56px |
| --input-description-spacing | 4px |
| --input-description-error-color | var(--red-800) |
| --input-asterisk-color | var(--red-800) |
Size (md)
| Variable | Default |
| --- | --- |
| --input-md-size-height | 32px |
| --input-md-size-border-radius | 4px |
| --input-md-size-spacing | 7px 12px |
| --input-md-size-fz | 14px |
| --input-md-size-fw | 400 |
| --input-md-size-lh | 16px |
| --input-md-size-label-fw | 700 |
| --input-md-size-label-spacing | 6px |
Outlined variant — default state
| Variable | Default |
| --- | --- |
| --input-outlined-text-color | var(--black-1000) |
| --input-outlined-placeholder-color | var(--blue-300) |
| --input-outlined-bg-color | var(--neutral-0) |
| --input-outlined-border-color | var(--gray-400) |
| --input-outlined-hover-bg-color | var(--neutral-0) |
| --input-outlined-hover-border-color | var(--gray-600) |
| --input-outlined-focus-border-color | var(--blue-400) |
| --input-outlined-valid-bg-color | var(--neutral-0) |
| --input-outlined-valid-border-color | var(--gray-400) |
| --input-outlined-disabled-bg-color | var(--gray-50) |
| --input-outlined-disabled-border-color | var(--gray-400) |
| --input-outlined-disabled-text-color | var(--blue-gray-600) |
| --input-outlined-label-color | var(--black-1000) |
| --input-outlined-addon-bg-color | var(--blue-20) |
Error state
| Variable | Default |
| --- | --- |
| --input-error-text-color | var(--black-1000) |
| --input-error-placeholder-color | var(--blue-300) |
| --input-error-bg-color | var(--red-100) |
| --input-error-border-color | var(--red-800) |
| --input-error-hover-bg-color | var(--red-100) |
| --input-error-hover-border-color | var(--red-600) |
| --input-error-focus-border-color | var(--red-800) |
| --input-error-valid-bg-color | var(--red-100) |
| --input-error-valid-border-color | var(--red-800) |
| --input-error-disabled-bg-color | var(--red-100) |
| --input-error-disabled-border-color | var(--red-800) |
| --input-error-disabled-text-color | var(--blue-gray-600) |
| --input-error-label-color | var(--black-1000) |
| --input-error-addon-bg-color | #f9cbd166 |
Global override example
// globals.scss
:root {
--input-font-family: 'Inter', sans-serif;
--input-md-size-height: 36px;
--input-outlined-border-color: var(--gray-300);
--input-outlined-focus-border-color: var(--brand-500);
--input-error-border-color: var(--danger-600);
--input-error-bg-color: var(--danger-50);
}Development
bun run lint
bun run type-check
bun run test
bun run build