Use it for
Use BbAsterisk for the small red * next to a required field. It has no
behaviour props, slots or events: it does not read required and it does not
validate anything. It draws the asterisk and exposes only root passthrough.
Use something else when
BbForm, if the question is how required fields get marked across a whole form
Also see: Validated forms.
Most projects never place it by hand: the form controls add it themselves,
because the requiredAsterisk plugin option is on by default. You need this
component in three cases: a legend that says what the marker means, a control
whose label you replaced, or an app that turned the option off and marks
required fields itself.
Passthrough
Hover or tap a part to outline it. The toggles turn on loading, errors and warnings where the component has them; only parts get a row.
The marker
The markup is one span:
<span aria-hidden="true" class="bb-asterisk">*</span>
Fields marked with an asterisk are required.
<template>
<div class="flex max-w-sm flex-col gap-4">
<!--
Say once what the marker means, above the first field. The fields draw
their own marker from `required`; this sentence is the one you place.
The marker is aria-hidden, so the legend spells it out for screen readers.
-->
<p class="m-0 text-sm text-[color:var(--bb-text-muted)]">
Fields marked<BbAsterisk /><span class="sr-only"> with an asterisk</span>
are required.
</p>
<BbTextInput
id="asterisk-workspace"
v-model="workspace"
label="Workspace name"
name="workspace"
required
/>
<BbTextInput
id="asterisk-billing-email"
v-model="billingEmail"
label="Billing email"
name="billing-email"
required
type="email"
/>
<!-- Optional: no marker, and no `required`. -->
<BbTextInput
id="asterisk-po"
v-model="purchaseOrder"
label="Purchase order number"
name="purchase-order"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { BbAsterisk, BbTextInput } from 'bitboss-ui';
const workspace = ref<string | null>('Vantera Studio');
const billingEmail = ref<string | null>('[email protected]');
const purchaseOrder = ref<string | null>(null);
</script>
aria-hidden is intentional: sighted users see the asterisk, screen readers do
not. A lone * read aloud in a label is noise. Assistive tech learns that a
field is required from the control: required outside a form, a required rule
or the schema on a field joined to a BbForm.
The marker and the requirement are separate. BbAsterisk does not make a field
required, and inside a BbForm the asterisk a field draws checks nothing on its
own.
Where it appears on its own
Every form control puts the marker after its label or legend when the field is
required. The requiredAsterisk plugin option is on by default; turning it off
is a project-wide choice, not a per-field one, because some required fields
marked and others not is worse than no markers:
// nuxt.config.ts
export default defineNuxtConfig({
bitboss: { requiredAsterisk: false },
});
// vite.config.ts, for a plain Vue app
bitbossUi({ requiredAsterisk: false });
A field shows it when it is marked
required. On a field
joined to a BbForm that prop is a hint: it marks the
field and checks nothing (the form posts novalidate), which suits a form the
server validates. To check on the client too, write rules="required", which
also raises the marker. The rule counts in either shape: the string form ('required|email', including 'required:true') or the
object form ({ required: true }; { required: false } shows nothing).
Nothing else counts. Rules like max:5 pass on an empty field, so an asterisk
for "any rule" would mark optional fields. required_if and relatives do not
count either: conditionally required is not required.
With a form schema, the schema
decides. A key that is not optional, has no default and is not nullable gets the
marker, and its field gets aria-required="true" as well. A validator
function passed as rules is opaque to the form and never raises the marker:
add the required hint beside it, or register it with defineRule and write
rules="required|…".
In your own label
A control's label slot replaces the label text and anything the control was
drawing beside it. If the field is required, put the marker back.
<template>
<div class="flex max-w-sm flex-col gap-4">
<!--
A bespoke label: the field name, the marker, and a unit note that has
to sit on the same line rather than in `description`. Replacing the
label replaces the marker too, so it goes back on the slot's
`showAsterisk`, which follows `required` and `requiredAsterisk`.
-->
<BbNumberInput
id="custom-label-quota"
v-model="quota"
label="Storage quota"
:max-precision="0"
:min="1"
name="quota"
required
>
<template #label="{ text, hasErrors, showAsterisk }">
<span :class="hasErrors ? 'font-semibold' : undefined">{{ text }}</span>
<BbAsterisk v-if="showAsterisk" />
<span class="ms-2 text-xs text-[color:var(--bb-text-muted)]">
per member
</span>
</template>
<template #suffix>GB</template>
</BbNumberInput>
<!-- No marker here: the field is genuinely optional. -->
<BbNumberInput
id="custom-label-overage"
v-model="overage"
label="Overage allowance"
:max-precision="0"
:min="0"
name="overage"
>
<template #label="{ text }">
<span>{{ text }}</span>
<span class="ms-2 text-xs text-[color:var(--bb-text-muted)]">optional</span>
</template>
<template #suffix>GB</template>
</BbNumberInput>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { BbAsterisk, BbNumberInput } from 'bitboss-ui';
const quota = ref<number | null>(50);
const overage = ref<number | null>(null);
</script>
On the input family the slot scope is { text, hasErrors, hasWarnings }, plus
showAsterisk while the marker would show. It is already computed from
required, the rules, the schema and the plugin option, so bind to it instead
of deciding again:
<BbTextInput v-model="email" label="Work email" name="email" required>
<template #label="{ text, showAsterisk }">
{{ text }}<BbAsterisk v-if="showAsterisk" />
<span class="ms-2 text-xs opacity-70">we never share it</span>
</template>
</BbTextInput>
With requiredAsterisk off, showAsterisk never turns on: place
<BbAsterisk /> yourself on the required fields.
Group controls (BbCheckboxGroup, BbRadioGroup, BbSwitchGroup) expose the
same scope on legend.
Colour and spacing
The component ships these declarations and inherits nothing else:
.bb-asterisk {
color: var(--bb-danger);
font-weight: 600;
margin-inline-start: 0.15em;
user-select: none;
}
The colour is --bb-danger: retheme that token and the marker follows everything
else that means "wrong". Do not hard-code a red here.
Use class or pt:root for a deliberate local adjustment. Neither changes the
field's required state.
Font size comes from the label, so the asterisk stays in proportion on a
compact field. If the default margin does not fit, wrap the component and space
that wrapper instead of overriding the class for one form.
If your product marks optional fields instead of required ones, do not
repurpose the asterisk. Write "optional", as the Overage allowance field in
In your own label does. A
red * already means one thing to anyone who has filled in a form.