Skip to content
SproutAceve · v3.25.1
Get started
Overview What is your role? Connect to Sprout MCP reference Adopting Sprout
Foundations
Brand & logos Color Typography Spacing & layout Elevation Iconography Accessibility
Components
Overview Buttons Inputs & forms Numeric input Data grid Ribbon toolbar Navigation Cards Alert Avatar Badge Dialog Tabs Floating tabs Menu
Practice
Interaction & motion Voice & content Product patterns
Artificial Intelligence
AI patterns AI transparency & disclosure AI disclosure label
People & ethics
Design principles Personas Trust and ethics Meet the makers
Playground
Build the systemplay Tower of tokensplay
Behind the system
How it works Changelogv3.25 Roadmap Contribute
Done
Components

Numeric input.

A field for a number you calculate with. Quantities on an order row, hours on a timesheet, a unit price, a percentage. It parses while you type and formats when you leave, and it is the only input in Sprout that turns tabular figures on.

What this is not

A number you calculate with is not the same as a number you identify with. Order numbers, phone numbers and postcodes are strings that happen to be digits, and they take a plain text field.

12
pcs
Numeric input
A value · tabular, stepped, summed
021811
Text field
An identifier · keeps its leading zero

The test: would you ever add two of them together? If not, it is text.

Anatomy

Five parts. The label and the field are always there, the unit and the stepper buttons are optional. The label and the helper sit outside the field, so the field height stays exactly one token.

7,5
h
Decimal comma

Hover or focus a row to see that measurement on the component above.

4px LabelAlways visible. Never a placeholder standing in for a label. The required mark is on surface, not danger: required is not an error, and the asterisk should not turn colour into the only carrier of the meaning.
48px FieldHolds the value, an optional unit and optional stepper buttons. The value is 16px at every size, never smaller, because iOS zooms the page when you focus an input below 16px.
12px UnitInside the field, after the number, never a control of its own. h, kr, %, pcs. Nordic convention puts kr after the value, not before it.
48px Stepper buttonsOptional, and rarely earned. Each button is as wide as the field is tall, so the control grows by twice its own height before the value gets any room.
4px HelperOne slot, two jobs: the format hint, then the error message in the same place, so nothing below the field moves when validation runs.

Size

Three heights. Only the largest is usable with a gloved hand, which makes it the only one that belongs in the field apps.

12
pcs
Small · 28
12
pcs
Medium · 38
12
pcs
Large · 48
SizeHeightWhere it belongs
Small
28px
Inside a dense back-office table, one field per cell, mouse and keyboard only.
Medium
38px
The desktop form default. Matches the medium button and the medium select.
Large
48px
Mobile and anything a field worker touches. The only size that meets the 48px minimum.

With stepper buttons the control is height × 3 wide before the value gets any room. At Large that is 144px of chrome.

Kinds of number

The kind of number decides the keypad, the decimals and the unit. It is not a style variant, it is what the field is for, and getting it wrong shows up as the wrong keyboard on a phone.

Try methe buttons work, and the field reformats when you leave it
Kind
Size
Options
12
pcs
48pxinputmode numeric
KindKeypad and decimalsRules
Quantity
inputmode numeric 0 decimals
Unit pcs. Stepper buttons earn their place here, because the range is small and ±1 is the common move.
Hours
inputmode decimal 1 decimal
Unit h, always decimal: 7,5 h, never 7:30. Clock format belongs to planner and schedule views, where a time of day is the subject. Here the subject is a duration, and durations get summed.
Money
inputmode decimal 2 decimals
Unit kr after the number, Nordic convention. Never stepper buttons. Group thousands on blur only.
Percentage
inputmode decimal 1 decimal
Unit %. Clamp 0 to 100 on blur with a message, never by refusing the keystroke.
Measurement
inputmode decimal 1 to 3 decimals
Unit m, m², kg. The unit is fixed by the field, never typed and never chosen by the user mid-entry.

Figures, separators and locale

This is where numeric fields actually break. None of it is visible until a user types a decimal or a total reaches four figures.

Manrope needs tabular figures switched on

Manrope's default figures are proportional: measured at 32px, the digit 1 is 37% narrower than a 6. Sprout does not need a second typeface for numbers, it needs one line of CSS.

Tabular
font-variant-numeric: tabular-nums
11,00
88,00
  • Every figure is handed the same advance, so the boxes above line up row to row.
  • Drawn for columns, and for any value that changes where it stands.
Proportional
the default, no property set
11,00
88,00
  • Each figure keeps its own width, so the 1 is narrow and the boxes drift.
  • Drawn to sit inside a sentence, where even spacing would read as gaps.

The shaded box behind each figure is the advance the font gives it. On the left the boxes stack; on the right the 1s are narrower, so the comma lands somewhere different on each row. Two figure sets in the same file, and the property picks one.

ItemQtyPrice
Plasterboard1111 111,00
Studs68896,50
Screws1 100119,90
tabular-nums · the interior lines up too
ItemQtyPrice
Plasterboard1111 111,00
Studs68896,50
Screws1 100119,90
default · right alignment does most of it
/* every numeric value, in a field or a table cell */
.sprout-numeric,
.sprout-input--numeric input,
td.numeric {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;  /* older Safari */
}

/* parse what a Nordic user actually types */
function parseNordic(raw) {
  return parseFloat(
    raw.replace(/[\s\u00A0]/g, '')  // grouping spaces, incl. non-breaking
       .replace(',', '.')            // decimal comma
  );
}
RuleValueWhy
Decimal separator
comma
Swedish, Danish and Norwegian users type 7,5. Accept both comma and full stop on input and normalise.
Group separator
non-breaking space
Intl.NumberFormat('sv-SE') groups with U+00A0, not a normal space. A naive parser fails on it, which is the single most common bug in a Nordic numeric field.
When to format
on blur
Insert grouping after the field loses focus. Formatting on keystroke moves the caret and makes typing feel broken.
Alignment
left in forms right in tables
In a form the value lines up with the label. In a table the digits line up with each other, which is what lets you compare a column.
Empty
not zero
A blank hours field means not entered. Defaulting it to 0 destroys the difference between nothing worked and nothing recorded.

States

Four blocks, light and dark side by side, each listing only the tokens it changes.

Empty and filledOne token set

The same state wearing different content. Nothing but the value slot changes, so they share one set of tokens.

Light
0,0
h
One decimal
7,5
h
One decimal
Dark
0,0
h
One decimal
7,5
h
One decimal
L / DWhat it coloursToken
Label and value
layers/on surface
neutral/900 · neutral/50
Field fill
layers/surface
neutral/0 · neutral/900
Border, 1px
layers/outline
neutral/200 · neutral/550
Unit, helper, placeholder
layers/supportive text
neutral/650 · neutral/200

Light layers/outline is 1.61:1 against surface, under the 3:1 that WCAG 1.4.11 asks of a control boundary. Dark passes at 3.67:1. Moving light to the same neutral/550 would give 4.74:1 and one step for both modes.

The placeholder takes layers/supportive text, not a loose grey: a placeholder is text and owes the full 4.5:1.

FocusTwo tokens change

Only the boundary changes. The ring is drawn outside the border, so the field does not resize and the number does not shift under the caret.

Light
7,5
h
Dark
7,5
h
L / DWhat it coloursToken
Border, 1px
containers/primary
primary/700 · primary/300
Ring, 2px
containers/primary container
primary/100 · primary/900

The border travels between modes, the ring does not. primary container is #152F1A in dark, 1.20:1 against surface, so the ring is invisible. It should resolve to an alpha of primary in dark.

ErrorIts own pair, and the one asymmetry

Fires on blur, never on keystroke: typing 0,5 passes through 0, and 0 is not an error yet. The message replaces the format hint in place, so nothing below the field moves. Same hexagon wherever an error appears, so the shape carries the meaning too.

Light
26
h
Max 24 hours per day
Dark
26
h
Max 24 hours per day
L / DWhat it coloursToken
Border and icon
containers/danger
danger/600 · danger/400
Message
on danger container / danger
danger/950 · danger/400

The icon carries the signal, so the message can take the token that reads best: on danger container, 14.98:1 on surface, the same hue and saturation as danger but darker. Dark inverts the pair, because there on danger container is nearly white, so the message takes danger at 8.94:1.

Light value on top, dark below. hovered and pressed belong to destructive controls; a field is not destructive. danger container is the block behind a validation summary, never behind a single field.

Disabled and read onlyFill and value change

The border and the label do not move, so the field keeps its shape. Say why in the helper: a locked number with no reason reads as a bug. Read only keeps the surface fill and dashes the same outline border.

Light
7,5
h
Locked after approval
60,0
h
Sum of the week
Dark
7,5
h
Locked after approval
60,0
h
Sum of the week
L / DWhat it coloursToken
Field fill
layers/surface variant
neutral/100 · neutral/800
Value
states/disabled
neutral/350 · neutral/650

The one place a low ratio is correct: WCAG 1.4.3 exempts an inactive control, so the low contrast is the message. Never use this pair for a field that is merely empty.

Do & Don't

Four decisions that come up every time someone builds one.

✓ Do
type="text"
inputmode="decimal"
Use text with an inputmode.

You get the numeric keypad on mobile and full control of parsing, and the user's typing survives when it is briefly invalid.

✗ Don't
type="number"
Don't use type="number".

The scroll wheel and arrow keys change the value by accident, and invalid input returns an empty string, so you lose what was typed.

✓ Do
12
pcs
Put the unit in the field.

Fixed by the field, so nobody types it and nobody has to guess whether the number is metres or millimetres.

✗ Don't
12 pcs
Don't let the unit be typed.

A unit inside the value has to be stripped before you can calculate, and every user spells it differently.

✓ Do
12
pcs
Size the field to the number.

A three digit quantity gets a narrow field. The width is a hint about what is expected, and it keeps a dense row readable.

✗ Don't
12
pcs
Don't stretch it to the container.

A full width field for two digits reads as a text field and wastes the row, which matters most in the tables that hold the most numbers.

✓ Do
4
pcs
Stepper buttons for small counts.

Roughly 1 to 20, where ±1 is the common move and a gloved hand would rather tap than type.

✗ Don't
1 249,50
kr
Don't put stepper buttons on money.

Nobody reaches 1 249,50 by tapping, and the buttons eat the width the value needs.

This page documents the latest Figma spec for Numeric input, which introduces stepper buttons, an in-field unit after the value, left-aligned value text, and a Small (28px) size. The shipped primitive (AceveNumericInput) does not have these yet — it right-aligns, formats on blur only, and has no stepper prop. Every demo above is a page-local mock built to this spec, not the real component. Treat this as the target to build toward, not as what ships today.
← PreviousInputs & formsNext →Data grid
Sprout · Aceve Design System · v3.25.1
Figma library GitLabReleases