Input
A text field with a label, placeholder, validation and reactive feedback.

use blinc_cn::input;
use blinc_core::reactive::{Signal, computed};
use blinc_layout::{binding::IntoReactive, prelude::*, widgets::text_input::SharedTextInputData};
pub fn input_demo(name: Signal<String>, input_data: SharedTextInputData, disabled: Signal<bool>) -> Div {
div()
.class("demo-stack")
.child(
input(&input_data)
.label("Your name")
.placeholder("Ada Lovelace")
.disabled(disabled)
.on_change(move |value| name.set(value.to_string())),
)
.child(
bound_text(computed(move |graph| {
let value = graph.get(name).unwrap_or_default();
if value.is_empty() {
"Say hello.".to_string()
} else {
format!("Hello, {value}.")
}
}))
.class("demo-caption"),
)
}
fn bound_text(value: impl IntoReactive<String>) -> Text {
Text::bound(value.into_reactive())
}
@layer components {
.blinc-ime-composition {
text-decoration: underline;
}
.blinc-input {
background: var(--input-bg);
border-color: var(--border-secondary);
cursor: text;
transition:
background-color var(--duration-fast) var(--ease-state),
border-color var(--duration-fast) var(--ease-state);
}
.blinc-input:hover:not(:disabled) {
background: var(--input-bg-hover);
border-color: var(--border-hover);
}
.blinc-input:focus:not(:disabled) {
background: var(--input-bg-focus);
border-color: var(--border-focus);
}
.blinc-input[data-invalid]:not(:disabled) {
border-color: var(--border-error);
}
.blinc-input:disabled {
background: var(--input-bg-disabled);
border-color: var(--border-secondary);
cursor: not-allowed;
}
.cn-input,
.cn-label {
font-family: var(--font-sans);
}
.cn-input {
letter-spacing: var(--tracking-tight);
}
.cn-input {
box-shadow: var(--shadow-sm);
background: var(--cn-input-bg, var(--input-bg));
border-color: var(--border);
border-radius: var(--radius-default);
color: var(--text-primary);
outline: 2px solid transparent;
outline-offset: 1px;
transition:
background-color var(--duration-fast) var(--ease-state),
border-color var(--duration-fast) var(--ease-state),
outline-color var(--duration-fast) var(--ease-state),
outline-offset var(--duration-fast) var(--ease-out);
}
.cn-input:focus:not(:disabled) {
outline: 2px solid var(--focus-ring);
outline-offset: 2px;
}
.cn-input:disabled {
box-shadow: none;
}
.cn-input[data-invalid]:not(:disabled),
.cn-input[data-invalid]:hover:not(:disabled),
.cn-input[data-invalid]:focus:not(:disabled) {
border-color: var(--border-error);
}
.cn-input[data-invalid]:focus:not(:disabled) {
outline: 2px solid var(--focus-ring-error);
}
.cn-input.cn-input--success:not(:disabled),
.cn-input.cn-input--success:hover:not(:disabled),
.cn-input.cn-input--success:focus:not(:disabled) {
border-color: var(--success);
}
.cn-input.cn-input--success:focus:not(:disabled) {
outline: 2px solid var(--focus-ring-success);
}
.cn-input--sm {
font-size: var(--text-xs);
}
.cn-input--md {
font-size: var(--text-sm);
}
.cn-input--lg {
font-size: var(--text-lg);
}
.cn-label {
color: var(--cn-label-color, var(--text-primary));
}
.cn-label-text {
color: var(--cn-label-color, var(--text-primary));
}
.cn-label:disabled > .cn-label-text {
color: var(--text-tertiary);
}
.cn-label-required {
color: var(--error);
}
}
Usage and properties
Keep SharedTextInputData across rebuilds. The browser host forwards committed text, composition and clipboard events into the Blinc editor. Its hidden textarea is an input bridge; the visible field is rendered by Blinc.
Use the installation guide to add blinc_cn and its component stylesheet. Create the fixture bindings once, and return the demo from your UI builder. Values marked Bound accept Signal/Computed sources where the API supports them; structural options configure the component at construction.
| Property | Type | Update model | This example |
|---|---|---|---|
data | SharedTextInputData | Persistent editor state | Empty text |
label / placeholder | String | Bound | Your name / Ada Lovelace |
disabled | bool | Bound | false |
size | InputSize | Structural | Medium |
on_change | Fn(&str) | Callback | Update the greeting Signal |
Keyboard and interaction
:hover, :focus, :disabled, :focus-visible and [data-invalid]. Tab focuses the editor; ordinary editing keys, selection and clipboard shortcuts apply.
Customizing it
Add application CSS after the default component layer. These selectors are the restyling surface for this component:
.cn-input-group.cn-input.cn-input--sm.cn-input--md.cn-input--lg.cn-label.cn-label-text.blinc-ime-composition
Theme and component variables: --input-bg, --input-bg-hover, --input-bg-focus, --input-bg-disabled, --border, --focus-ring, --text-primary.
Built-in themes
The Live theme control installs Blinc's own theme bundles, including their colours, focus rings, corner shapes and motion tokens.
pub fn theme_bundle_for(name: &str) -> blinc_theme::ThemeBundle {
match name {
"restrained" => blinc_theme::RestrainedTheme::bundle(),
"expressive" => blinc_theme::ExpressiveTheme::bundle(),
_ => blinc_theme::HybridTheme::bundle(),
}
.with_css(blinc_cn::cn_styles::CN_STYLES)
}Download the default widget and component stylesheets. Sample assets and licenses are available in the asset guide.
Documentation
From the canonical component guide.
Text input field, bound to a SharedTextInputData:
use blinc::cn::prelude::*;
use blinc::layout::widgets::text_input::text_input_data;
let name = text_input_data();
cn::input(&name)
.label("Name")
.placeholder("Enter your name...")
.on_change(|value| println!("name: {value}"))
Input Types
use blinc::layout::widgets::text_input::InputType;
cn::input(&email).input_type(InputType::Email).placeholder("Email")
cn::input(&password).password().placeholder("Password")
cn::input(&age).input_type(InputType::Number).placeholder("Age")
cn::input(&query).input_type(InputType::Search).placeholder("Search...")
Input States
// Disabled
cn::input(&name).disabled(true)
// With an error message; the border turns red, also while hovered or focused
cn::input(&name).error("Name is required")
// Text that follows signals: an empty error clears it
let placeholder = signal("you@example.com".to_string());
let error = signal(String::new());
cn::input(&email)
.placeholder(placeholder)
.description("We never share it")
.error(error)
The placeholder, description and error each take a string or a signal and change in place. While the error is non-empty it shows in place of the description.
Styling
The field is one element with the class cn-input. Its border and fill while
hovered, focused, invalid or disabled are rules, eased by a CSS transition:
:hover, :focus, [data-invalid] (set while the error is non-empty, and
while a value fails validation) and :disabled.
Restyle it with rules of your own:
.my-field:focus { border-color: #16a34a; outline-color: #16a34a; }
idle_border_color(..) and idle_bg_color(..) set the idle values as inline
style, which the component rules do not replace. The hover_*, focused_*,
error_* and grouped colour setters are removed; use rules.
Platforms and version
The component API is available in Blinc 0.6.0. The Live example runs in a dedicated worker and requires a compatible GPU surface. Preview and Code remain available when Live cannot start. The canvas has a descriptive label; a full screen-reader DOM mirror is not provided. The gallery integration is based on revision e2e55c24e078.
Recordings and live demos use the default Hybrid theme with bundled Noto Sans. The recording uses a 560 × 320 logical viewport at twice that resolution. Live follows its container and actual display density. Source base revision · Exact build source · Artifact versions and checksums · Font license.