Survey Design
Question Types
decimal

The decimal question type collects numbers that may include a fractional part (e.g. 3.14, 72.5). It triggers a numeric keyboard on mobile and supports the same appearance options as the text type for layout and formatting.

DECIMAL appearance variants — thousandsep, digisep, embed

Basic XLSForm Specification

typenamelabel
decimalweight_kgWeight (kg)
decimalheight_mHeight (m)
decimaltemperatureBody temperature (°C)

For the standard specification see the XLSForm documentation (opens in a new tab).


Appearance options

number-hspinner — horizontal spinner

Replaces the input with a horizontal − value + spinner. Tapping minus/plus adjusts the value by the configured step.

number-hspinner

The displayed value is formatted with one decimal place (e.g. 3.0, 72.5). Default step is 1; use hspinner() to set a decimal step.

Customize button shape and step using hspinner():

number-hspinner hspinner('left-small-square','right-small-square','0.1')

Parameters: left button shape, right button shape, step value. Use a decimal step like 0.5 or 0.1 for fine-grained control.

Custom colors using colors():

number-hspinner colors(0099FF,FFFFFF,FF5500,FFFFFF)

Four values in order: left-button background, left-button icon, right-button background, right-button icon.


thousandsep — thousands separator

Formats the displayed number with comma grouping (e.g. 1,234.56). The raw number is stored without commas. Input type is switched to "text" to allow the formatted display.

thousandsep

digisep(n, "sep") — custom digit grouping

Groups integer-part digits into blocks of n separated by a custom separator.

digisep(3," ")      → 1 234 567.89

Input type is switched to "text" when this appearance is active.


textonly — force text keyboard

Forces type="text" on the input element. Prevents browser number-input quirks (hiding trailing zeros, desktop spin arrows) while still accepting decimal values.

textonly

floating_hint — hint floats as label

The hint text renders inside the input as a placeholder. When the user starts typing, it floats above the field as a label.

floating_hint

embed — hint inside input boundary

Similar to floating_hint — the hint sits inside the input boundary when empty. It does not float when the user types.

embed

textpopup — modal dialog input

Tapping the field opens a modal dialog with a larger input. Useful on small screens.

textpopup

charcount — live character counter

Displays a live (n) character count below the input, updated as the user types.

charcount

prefix(expr) — prepend a prefix

Prepends a static or calculated string to the displayed value. The stored value does not include the prefix.

prefix('USD ')          → displays "USD 72.50", stores "72.50"
prefix(${currency})     → dynamic prefix from another field

align_answer — input text alignment

Controls the horizontal alignment of the typed value inside the input.

align_answer = left
align_answer = right
align_answer = center

inputs{qrscan} — QR / barcode scan into field

Adds a QR code scan button next to the input. Opens the camera scanner and populates the field with the scanned value.

inputs{qrscan}

Appearance quick reference

AppearanceWidget rendered
(none)Number input (type="number") — numeric keyboard on mobile
number-hspinnerHorizontal − value + spinner, one decimal place displayed
number-hspinner hspinner(...)Spinner with custom button shape and step
number-hspinner colors(...)Spinner with custom button colors
thousandsepComma-grouped number display
digisep(n,"sep")Custom digit grouping
textonlyForce text keyboard
floating_hintHint floats above field while typing
embedHint inside input boundary
textpopupTap to open modal dialog
charcountLive character count below input
prefix(expr)Prepend static or dynamic prefix
align_answer = left/right/centerInput text alignment
inputs{qrscan}QR/barcode scan button

Constraints and validation

typenamelabelconstraintconstraint_message
decimalweightWeight (kg). > 0 and . <= 500Enter a value between 0 and 500
decimalheightHeight (m). > 0 and . <= 3Enter a value between 0 and 3
decimaltempTemperature (°C). >= 35 and . <= 42Normal temperature: 35°C–42°C
decimalpercentagePercentage. >= 0 and . <= 100Enter a value between 0 and 100

Example — measurement form

typenamelabelappearanceconstraintconstraint_message
decimalweight_kgWeight (kg)number-hspinner hspinner('left-small-square','right-small-square','0.5'). > 0 and . <= 300Enter a valid weight
decimalheight_mHeight (m)number-hspinner hspinner('left-small-square','right-small-square','0.01'). > 0 and . <= 3Enter a valid height
calculatebmi

BMI calculation column:

${weight_kg} div (${height_m} * ${height_m})

Best Practices

  1. Use number-hspinner hspinner(..., '0.1') for measurements requiring one decimal of precision — the spinner auto-formats to one decimal place, keeping values consistent.
  2. Use textonly when browsers add unwanted spin controls or truncate trailing zeros (e.g. 3.0 displayed as 3).
  3. Use thousandsep for large financial values so respondents can verify they entered the correct magnitude.
  4. Enforce range constraints rather than relying on mobile keyboard type alone.

Limitations

  • The spinner always displays one decimal place — if you need more precision in the displayed value, use the plain input instead.
  • thousandsep and digisep() store the raw number — do not read the formatted display value in calculations.