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.