Skip to content

Commit 564fefb

Browse files
mrosvikmimarz
andauthored
docs: how to write effective labels and questions in forms (#4785)
Co-authored-by: Michael Marszalek <mimarz@gmail.com>
1 parent 7c6a7eb commit 564fefb

18 files changed

Lines changed: 576 additions & 2 deletions

File tree

apps/www/app/_components/do-dont/do-dont.module.css

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,25 @@
4545
color: var(--ds-color-neutral-text-subtle);
4646
contain: inline-size; /*maybe/maybe not do this (footer text does not contribute to card-width but is constrained by content above and maybe a min-width)*/
4747
> p {
48-
margin: 0;
48+
margin: 0 0 var(--ds-size-2);
4949
font-weight: 400;
5050
max-width: 65ch;
5151
text-wrap: pretty;
5252
}
53+
54+
> ul,
55+
> ol {
56+
margin-block: 0;
57+
padding-inline-start: var(--ds-size-5);
58+
}
59+
60+
li + li {
61+
margin-top: var(--ds-size-1);
62+
}
63+
64+
> :is(ul, ol) + p {
65+
margin-top: var(--ds-size-4);
66+
}
5367
}
5468
}
5569
/*TODO: needs more work*/
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { Fieldset, Radio, Textfield } from '@digdir/designsystemet-react';
2+
3+
export const DoLabelEn = () => {
4+
return (
5+
<Fieldset>
6+
<Fieldset.Legend>
7+
Have you applied for housing benefit before?
8+
</Fieldset.Legend>
9+
<Radio label='Yes, I have applied before' value='yes' name='meeting' />
10+
<Radio label='No, I have not applied before' value='no' name='meeting' />
11+
<Radio label='I am not sure' value='not-sure' name='meeting' />
12+
</Fieldset>
13+
);
14+
};
15+
16+
export const DoFormEn = () => {
17+
return (
18+
<Fieldset>
19+
<Textfield label='Name' />
20+
<Textfield
21+
label='Choose a time'
22+
type='time'
23+
style={{ maxWidth: '8rem' }}
24+
/>
25+
<Textfield label='What do you need help with?' multiline rows={4} />
26+
</Fieldset>
27+
);
28+
};
29+
30+
export const DontFormEn = () => {
31+
return (
32+
<Fieldset>
33+
<Textfield label='What is your name?' />
34+
<Textfield label='What time of day works for you?' type='time' />
35+
<Textfield label='Scope' multiline rows={4} />
36+
</Fieldset>
37+
);
38+
};
39+
40+
export const DoClosedEn = () => {
41+
return (
42+
<Fieldset>
43+
<Fieldset.Legend>Do you own the home you live in?</Fieldset.Legend>
44+
<Radio label='Yes' value='yes' />
45+
<Radio label='No' value='no' />
46+
</Fieldset>
47+
);
48+
};
49+
50+
export const DontOpenEn = () => {
51+
return <Textfield label='Describe your housing situation' />;
52+
};
53+
54+
export const DoNegationsEn = () => {
55+
return (
56+
<Fieldset>
57+
<Fieldset.Legend>Do you have a place to live?</Fieldset.Legend>
58+
<Radio label='Yes' value='yes' />
59+
<Radio label='No' value='no' />
60+
</Fieldset>
61+
);
62+
};
63+
64+
export const DontNegationsEn = () => {
65+
return (
66+
<Fieldset>
67+
<Fieldset.Legend>Do you not have a place to live?</Fieldset.Legend>
68+
<Radio label='Yes' value='yes' />
69+
<Radio label='No' value='no' />
70+
</Fieldset>
71+
);
72+
};
73+
74+
export const DoPlaceholderEn = () => {
75+
return (
76+
<Textfield
77+
label='Department code'
78+
description='6 digits. You can find it on your payslip.'
79+
/>
80+
);
81+
};
82+
export const DontPlaceholderEn = () => {
83+
return (
84+
<Textfield
85+
label='Department code'
86+
placeholder='6 digits'
87+
style={{ minWidth: '24rem' }}
88+
/>
89+
);
90+
};
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
---
2+
title: Labels and questions in forms
3+
category: Content work
4+
description: Good labels make it easier for users to understand what to answer.
5+
author: Digdir, KS, Nav ++
6+
search_terms: questions, labels, wording
7+
date: 2026-05-20
8+
order: 10
9+
published: true
10+
---
11+
12+
A label is the text that tells users what to answer for each field in a form. You can use a description to help users understand difficult terms, or explain why they need to answer. Some call labels question text and descriptions help text.
13+
14+
It is especially important to think about how you ask questions in public services, because users are often afraid of answering incorrectly.
15+
16+
<Story story="WithRowsEn" />
17+
18+
Only ask for information you need.
19+
20+
Consider
21+
- whether you can reuse information you already have about the user
22+
- why you need the information
23+
- what you will do with the information
24+
25+
## Think of the text as a conversation
26+
When we address users directly with “you” and “I”, the content feels less technical and more like a conversation. It also makes it easier for users to understand what to answer.
27+
28+
Check that the answer options can be read as a logical response to the label. Consider including answer options users can choose if they do not know what to answer, for example “I am not sure” or “I do not know”.
29+
30+
<div className="dodont-row">
31+
<DoDont story="DoLabelEn">
32+
Use **you** when you
33+
- write labels and questions
34+
- give information to the user
35+
36+
Use **I** when
37+
- the user answers through checkboxes, radio buttons or dropdowns
38+
- the user gives examples of answers
39+
- the user consents to something
40+
</DoDont>
41+
</div>
42+
43+
## Write short and precise labels
44+
Users prefer short texts, especially when they are stressed, busy or reading on a small screen.
45+
Use established wording when you can to keep labels as short as possible.
46+
47+
Choose between established wording, descriptive prompts or questions.
48+
49+
These three ways of writing labels work best in slightly different situations.
50+
- Use established wording when users are not in doubt about what the answer is, for example National identity number and Marital status.
51+
- Use questions when the topic and answer options are less familiar to users. Questions also make the tone feel more approachable.
52+
- Prompts can help make the label shorter and more precise compared to questions.
53+
54+
<div className="dodont-row">
55+
<DoDont story="DoFormEn">
56+
Use established wording and descriptive prompts when they make the label shorter and clearer.
57+
</DoDont>
58+
<DoDont story="DontFormEn">
59+
Avoid questions when a short label or prompt is easier to understand.
60+
</DoDont>
61+
</div>
62+
63+
Questions often lead users to answer briefly. If you need a more detailed answer, a prompt may be better, for example “Describe what you need help with during a day”.
64+
65+
## Use closed questions when you can
66+
Closed questions are easier to answer than open questions. Users have to formulate the answer themselves when the question is open. A closed question can often be answered with Yes or No, or with obvious answer options. For example, “Which municipality do you live in?”.
67+
68+
<div className="dodont-row">
69+
<DoDont story="DoClosedEn">
70+
Use closed questions when you can.
71+
</DoDont>
72+
<DoDont story="DontOpenEn">
73+
Avoid open questions when you can.
74+
</DoDont>
75+
</div>
76+
77+
## Avoid questions with negations
78+
Questions with negations can be difficult to understand. These are often sentences with “not” or “never”.
79+
80+
<div className="dodont-row">
81+
<DoDont story="DoNegationsEn">
82+
Ask the question without negations.
83+
</DoDont>
84+
<DoDont story="DontNegationsEn">
85+
Avoid questions with “not” and other negations.
86+
</DoDont>
87+
</div>
88+
89+
## Give all relevant information before users answer
90+
Users should not have to try and fail, so give them all relevant information before they choose an option or fill in a field. Avoid guidance text inside the input itself, also known as placeholders. Placeholders disappear when the field is filled in, and may conflict with accessibility requirements.
91+
92+
Sometimes descriptions, also known as help text, are useful for explaining something difficult, such as legal terms or specialist language.
93+
94+
Only use descriptions where they are needed, to reduce cognitive load.
95+
96+
<div className="dodont-row">
97+
<DoDont story="DoPlaceholderEn">
98+
Give necessary information before the user answers.
99+
</DoDont>
100+
<DoDont story="DontPlaceholderEn">
101+
Do not use placeholder text as the only guidance.
102+
</DoDont>
103+
</div>
104+
105+
## Build trust by explaining why we ask
106+
107+
It can be useful to briefly explain why we ask for something, to build trust and openness. One way to do this is with an expandable description.
108+
109+
<Story story="WithExpandableDescriptionEn" />
110+
111+
<Contributors
112+
headingLevel={2}
113+
authors={[
114+
'Dorte Drange (Digdir)',
115+
'Marianne Røsvik (Digdir)',
116+
'Gørild Døhl (Digdir)',
117+
'KS',
118+
'Skatteetaten',
119+
'Nav',
120+
'Helsenorge',
121+
'Lånekassen',
122+
'Oslo kommune'
123+
]}
124+
/>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {
2+
Details,
3+
Field,
4+
Input,
5+
Label,
6+
Textfield,
7+
} from '@digdir/designsystemet-react';
8+
import { useState } from 'react';
9+
10+
export const WithRowsEn = () => {
11+
return (
12+
<Textfield
13+
label='Last name (This is a label)'
14+
description='If you have more than one last name, type only the very last. If you have a hyphenated name, type your whole last name. (This is a description)'
15+
multiline
16+
rows={4}
17+
/>
18+
);
19+
};
20+
21+
export const WithExpandableDescriptionEn = () => {
22+
const [open, setOpen] = useState(false);
23+
24+
return (
25+
<Field style={{ width: 'min(100%, 26rem)' }}>
26+
<Label>National identity number</Label>
27+
<Details
28+
open={open}
29+
onToggle={() => setOpen(!open)}
30+
style={{ background: 'transparent', borderBlockWidth: 0 }}
31+
>
32+
<Details.Summary
33+
style={{
34+
background: 'transparent',
35+
minHeight: 'auto',
36+
padding: 0,
37+
}}
38+
>
39+
Why do we ask for this?
40+
</Details.Summary>
41+
<Details.Content
42+
style={{ marginBlockStart: '0.5rem', marginInline: 0 }}
43+
>
44+
We use your national identity number to find the correct information
45+
about you and make sure the application is registered to the right
46+
person.
47+
</Details.Content>
48+
</Details>
49+
<Input inputMode='numeric' />
50+
</Field>
51+
);
52+
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Fieldset, Radio, Textfield } from '@digdir/designsystemet-react';
2+
3+
export const DoLabel = () => {
4+
return (
5+
<Fieldset>
6+
<Fieldset.Legend>Har du søkt om bostøtte før?</Fieldset.Legend>
7+
<Radio label='Ja, jeg har søkt før' value='yes' name='meeting' />
8+
<Radio label='Nei, jeg har ikke søkt før' value='no' name='meeting' />
9+
<Radio label='Jeg er ikke sikker' value='not-sure' name='meeting' />
10+
</Fieldset>
11+
);
12+
};
13+
14+
export const DoForm = () => {
15+
return (
16+
<Fieldset>
17+
<Textfield label='Navn' />
18+
<Textfield
19+
label='Velg tidspunkt'
20+
type='time'
21+
style={{ maxWidth: '8rem' }}
22+
/>
23+
<Textfield label='Hva trenger du hjelp til?' multiline rows={4} />
24+
</Fieldset>
25+
);
26+
};
27+
export const DontForm = () => {
28+
return (
29+
<Fieldset>
30+
<Textfield label='Hva er navnet ditt?' />
31+
<Textfield label='Når på døgnet passer det?' type='time' />
32+
<Textfield label='Omfang' multiline rows={4} />
33+
</Fieldset>
34+
);
35+
};
36+
37+
export const DoClosed = () => {
38+
return (
39+
<Fieldset>
40+
<Fieldset.Legend>Eier du boligen du bor i?</Fieldset.Legend>
41+
<Radio label='Ja' value='yes' />
42+
<Radio label='Nei' value='no' />
43+
</Fieldset>
44+
);
45+
};
46+
export const DontOpen = () => {
47+
return <Textfield label='Beskriv din boligsituasjon' />;
48+
};
49+
50+
export const DoNegations = () => {
51+
return (
52+
<Fieldset>
53+
<Fieldset.Legend>Har du et sted å bo?</Fieldset.Legend>
54+
<Radio label='Ja' value='yes' />
55+
<Radio label='Nei' value='no' />
56+
</Fieldset>
57+
);
58+
};
59+
export const DontNegations = () => {
60+
return (
61+
<Fieldset>
62+
<Fieldset.Legend>Har du ikke et sted å bo?</Fieldset.Legend>
63+
<Radio label='Ja' value='yes' />
64+
<Radio label='Nei' value='no' />
65+
</Fieldset>
66+
);
67+
};
68+
69+
export const DoPlaceholder = () => {
70+
return (
71+
<Textfield
72+
label='Avdelingskode'
73+
description='6 siffer. Du finner koden på lønnsslippen.'
74+
/>
75+
);
76+
};
77+
78+
export const DontPlaceholder = () => {
79+
return (
80+
<Textfield
81+
label='Avdelingskode'
82+
placeholder='6 siffer'
83+
style={{ minWidth: '24rem' }}
84+
/>
85+
);
86+
};

0 commit comments

Comments
 (0)