Skip to content
NuvoUI

Form field

Three labeled form fields: an email input with a hint, a select, and an input showing an error message.

Preview

We send the sign-in link to this address.

Enter the name of your company.

With utility classes

The markup of the preview. Classes come from the prebuilt dist/nuvoui.min.css, or from your own build with $generate-utility-classes: true.

HTML
<div class="flex col gap-6 max-w-sm">
    <div class="flex col gap-1">
        <label class="text-sm font-medium" for="signup-email">Work email</label>
        <input id="signup-email" name="email" type="email" autocomplete="email" aria-describedby="signup-email-hint" required>
        <p class="text-sm text-muted m-0" id="signup-email-hint">We send the sign-in link to this address.</p>
    </div>
    <div class="flex col gap-1">
        <label class="text-sm font-medium" for="signup-size">Team size</label>
        <select id="signup-size" name="size">
            <option>1 to 10 people</option>
            <option>11 to 50 people</option>
            <option>More than 50 people</option>
        </select>
    </div>
    <div class="flex col gap-1">
        <label class="text-sm font-medium" for="signup-company">Company</label>
        <input id="signup-company" name="company" type="text" autocomplete="organization" aria-invalid="true" aria-describedby="signup-company-error">
        <p class="text-sm text-danger m-0" id="signup-company-error">Enter the name of your company.</p>
    </div>
</div>

With apply()

The same result with BEM class names and no utility classes. @use "@nuvoui/core" as *; emits the tokens and base styles, and each apply() writes one rule.

SCSS
@use "@nuvoui/core" as *;

// Inputs and selects take their border, focus ring and invalid state from the base layer
.fields {
    @include apply(
        flex, // display: flex
        col, // flex direction: column
        gap(6), // gap: spacing key 6 (1.5rem)
        max-width(sm) // max width: the sm breakpoint, 640px
    );
}

.field {
    @include apply(
        flex, // display: flex
        col, // flex direction: column
        gap(1) // gap: spacing key 1 (0.25rem)
    );
}

.field__label {
    @include apply(
        font-size(sm), // font size key sm (0.875rem)
        font(medium) // font weight: medium (500)
    );
}

.field__hint {
    @include apply(
        m(0), // margin: 0
        font-size(sm), // font size key sm (0.875rem)
        text-color(muted) // muted text color, follows the theme
    );
}

.field__error {
    @include apply(
        m(0), // margin: 0
        font-size(sm), // font size key sm (0.875rem)
        text-color(danger) // danger (error) text color
    );
}
HTML
<div class="fields">
    <div class="field">
        <label class="field__label" for="signup-email">Work email</label>
        <input id="signup-email" name="email" type="email" autocomplete="email" aria-describedby="signup-email-hint" required>
        <p class="field__hint" id="signup-email-hint">We send the sign-in link to this address.</p>
    </div>
    <div class="field">
        <label class="field__label" for="signup-size">Team size</label>
        <select id="signup-size" name="size">
            <option>1 to 10 people</option>
            <option>11 to 50 people</option>
            <option>More than 50 people</option>
        </select>
    </div>
    <div class="field">
        <label class="field__label" for="signup-company">Company</label>
        <input id="signup-company" name="company" type="text" autocomplete="organization" aria-invalid="true" aria-describedby="signup-company-error">
        <p class="field__error" id="signup-company-error">Enter the name of your company.</p>
    </div>
</div>

Tokens used

Every color comes from a theme token, so the recipe follows the light, dark and inverse themes with no extra CSS.

bg-surface, text-regular
Field background and typed text, from the base layer.
border-control, border-control-hover
Field edge at rest and on hover, at 3:1 contrast against the page.
border-focus, border-focus-width, border-focus-offset
The focus ring the base layer draws on :focus-visible.
danger, text-danger
The border of a field with aria-invalid="true" or :user-invalid, and the error message.
text-muted, text-subtle
The hint, and placeholder text.