Skip to content

Commit 8722374

Browse files
Jeffrey Lauwersclaude
andcommitted
refactor(storybook): remove placeholder by default from all input stories
Placeholder tekst verdwijnt bij typen en kan het veld er ingevuld laten uitzien. Dit design system ontmoedigt placeholders als primaire manier om hints te geven — gebruik FormFieldDescription daarvoor. - Verwijder placeholder uit meta.args en AllStates "Default" rij voor TextInput, TextArea, SearchInput, EmailInput, PasswordInput, TelephoneInput en NumberInput - Verwijder placeholder uit Width variants renders (vervangen door <p> labels boven elk veld) - Voeg WithPlaceholder story toe aan elk component om te laten zien dat het technisch wel mogelijk is - Verwijder placeholder uit alle FormField story renders - Update docs: "Vermijd placeholders" in TextInput/TextArea best practices; "Gebruik FormFieldDescription voor formaathints" in EmailInput, TelephoneInput en NumberInput; nuance voor SearchInput - Verscherp Accessibility noot in TextInput en TextArea Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2c22994 commit 8722374

14 files changed

Lines changed: 171 additions & 88 deletions

packages/storybook/src/EmailInput.docs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ De EmailInput component is een gespecialiseerd invoerveld voor e-mailadressen. H
2020

2121
## Best practices
2222

23-
- **Gebruik een duidelijke placeholder.** Geef een voorbeeld e-mailadres (bijv. `naam@voorbeeld.nl`).
23+
- **Gebruik FormFieldDescription voor formaathints.** Als het e-mailadresformaat toelichting behoeft, gebruik dan [FormFieldDescription](/docs/components-formfielddescription--docs) — niet een placeholder. Placeholder tekst verdwijnt bij typen en is daarna niet meer zichtbaar.
2424
- **Laat browser-autocomplete aan.** De standaard `autocomplete="email"` helpt gebruikers snel invullen. Zet alleen op `off` als daar een goede reden voor is.
2525
- **Combineer met FormField.** Gebruik altijd een label via `FormField` of `FormFieldLabel` voor toegankelijkheid.
2626
- **Geef validatie feedback.** Gebruik de `invalid` prop in combinatie met `aria-invalid` en een `FormFieldErrorMessage`.

packages/storybook/src/EmailInput.stories.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ const meta: Meta<typeof EmailInput> = {
2929
},
3030
},
3131
args: {
32-
placeholder: 'naam@voorbeeld.nl',
3332
disabled: false,
3433
readOnly: false,
3534
invalid: false,
@@ -55,6 +54,11 @@ export const WithValue: Story = {
5554
args: { defaultValue: 'jan@voorbeeld.nl' },
5655
};
5756

57+
export const WithPlaceholder: Story = {
58+
name: 'With placeholder',
59+
args: { placeholder: 'naam@voorbeeld.nl' },
60+
};
61+
5862
export const Disabled: Story = {
5963
args: { disabled: true, value: 'jan@voorbeeld.nl' },
6064
};
@@ -71,13 +75,21 @@ export const Invalid: Story = {
7175
export const Widths: Story = {
7276
name: 'Width variants',
7377
render: () => (
74-
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
75-
<EmailInput width="xs" placeholder="xs" />
76-
<EmailInput width="sm" placeholder="sm" />
77-
<EmailInput width="md" placeholder="md" />
78-
<EmailInput width="lg" placeholder="lg" />
79-
<EmailInput width="xl" placeholder="xl" />
80-
<EmailInput width="full" placeholder="full" />
78+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
79+
{(['xs', 'sm', 'md', 'lg', 'xl', 'full'] as const).map((w) => (
80+
<div key={w}>
81+
<p
82+
style={{
83+
margin: '0 0 0.25rem',
84+
fontWeight: 'bold',
85+
fontSize: '0.875rem',
86+
}}
87+
>
88+
{w}
89+
</p>
90+
<EmailInput width={w} />
91+
</div>
92+
))}
8193
</div>
8294
),
8395
};
@@ -107,7 +119,7 @@ export const AllStates: Story = {
107119
>
108120
Default
109121
</label>
110-
<EmailInput placeholder="naam@voorbeeld.nl" />
122+
<EmailInput />
111123
</div>
112124
<div>
113125
<label

packages/storybook/src/FormField.stories.tsx

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export const Default: Story = {
5151
},
5252
render: (args) => (
5353
<FormField {...args}>
54-
<TextInput id="input-1" placeholder={TEKST} />
54+
<TextInput id="input-1" />
5555
</FormField>
5656
),
5757
};
@@ -60,7 +60,7 @@ export const WithDescription: Story = {
6060
name: 'With description',
6161
render: (args) => (
6262
<FormField {...args} label={TEKST} htmlFor="input-desc" description={TEKST}>
63-
<TextInput id="input-desc" placeholder={TEKST} />
63+
<TextInput id="input-desc" />
6464
</FormField>
6565
),
6666
};
@@ -69,7 +69,7 @@ export const WithError: Story = {
6969
name: 'With error',
7070
render: (args) => (
7171
<FormField {...args} label={TEKST} htmlFor="input-err" error={TEKST}>
72-
<TextInput id="input-err" invalid placeholder={TEKST} />
72+
<TextInput id="input-err" invalid />
7373
</FormField>
7474
),
7575
};
@@ -78,7 +78,7 @@ export const WithStatus: Story = {
7878
name: 'With status',
7979
render: (args) => (
8080
<FormField {...args} label={TEKST} htmlFor="input-status" status={TEKST}>
81-
<TextInput id="input-status" placeholder={TEKST} />
81+
<TextInput id="input-status" />
8282
</FormField>
8383
),
8484
};
@@ -90,25 +90,25 @@ export const AllStates: Story = {
9090
<div>
9191
<h3 style={{ marginBlockEnd: '0.5rem' }}>Basic</h3>
9292
<FormField label={TEKST} htmlFor="s1">
93-
<TextInput id="s1" placeholder={TEKST} />
93+
<TextInput id="s1" />
9494
</FormField>
9595
</div>
9696
<div>
9797
<h3 style={{ marginBlockEnd: '0.5rem' }}>With description</h3>
9898
<FormField label={TEKST} htmlFor="s2" description={TEKST}>
99-
<TextInput id="s2" placeholder={TEKST} />
99+
<TextInput id="s2" />
100100
</FormField>
101101
</div>
102102
<div>
103103
<h3 style={{ marginBlockEnd: '0.5rem' }}>With optional suffix</h3>
104104
<FormField label={TEKST} htmlFor="s3" labelSuffix="(niet verplicht)">
105-
<TextInput id="s3" placeholder={TEKST} />
105+
<TextInput id="s3" />
106106
</FormField>
107107
</div>
108108
<div>
109109
<h3 style={{ marginBlockEnd: '0.5rem' }}>With error</h3>
110110
<FormField label={TEKST} htmlFor="s4" error={TEKST}>
111-
<TextInput id="s4" invalid placeholder={TEKST} />
111+
<TextInput id="s4" invalid />
112112
</FormField>
113113
</div>
114114
<div>
@@ -119,7 +119,7 @@ export const AllStates: Story = {
119119
status={TEKST}
120120
statusVariant="positive"
121121
>
122-
<TextInput id="s5" placeholder={TEKST} />
122+
<TextInput id="s5" />
123123
</FormField>
124124
</div>
125125
<div>
@@ -130,13 +130,13 @@ export const AllStates: Story = {
130130
status={TEKST}
131131
statusVariant="warning"
132132
>
133-
<TextInput id="s6" placeholder={TEKST} />
133+
<TextInput id="s6" />
134134
</FormField>
135135
</div>
136136
<div>
137137
<h3 style={{ marginBlockEnd: '0.5rem' }}>With TextArea</h3>
138138
<FormField label={TEKST} htmlFor="s7" description={TEKST}>
139-
<TextArea id="s7" rows={4} placeholder={TEKST} />
139+
<TextArea id="s7" rows={4} />
140140
</FormField>
141141
</div>
142142
<div>

packages/storybook/src/NumberInput.docs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ De NumberInput component is een gespecialiseerd invoerveld voor het invoeren van
2121

2222
## Best practices
2323

24-
- **Gebruik een duidelijke placeholder.** Geef aan wat de verwachte invoer is (bijv. `0` voor gehele getallen, `0,00` voor bedragen).
24+
- **Gebruik FormFieldDescription voor formaathints.** Als je wilt toelichten wat de verwachte invoer is (bijv. "Voer een bedrag in, gebruik een komma voor decimalen"), gebruik dan [FormFieldDescription](/docs/components-formfielddescription--docs) — niet een placeholder. Placeholder tekst verdwijnt bij typen en is daarna niet meer zichtbaar.
2525
- **Gebruik `allowDecimals` voor bedragen.** Dit schakelt `inputmode="decimal"` in zodat ook een kommatoets beschikbaar is op mobiel.
2626
- **Combineer met FormField.** Gebruik altijd een label via `FormField` of `FormFieldLabel` voor toegankelijkheid.
2727
- **Geef validatie feedback.** Gebruik de `invalid` prop in combinatie met `aria-invalid` en een `FormFieldErrorMessage`.

packages/storybook/src/NumberInput.stories.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ const meta: Meta<typeof NumberInput> = {
2424
},
2525
},
2626
args: {
27-
placeholder: '0',
2827
disabled: false,
2928
readOnly: false,
3029
invalid: false,
@@ -50,11 +49,15 @@ export const WithDecimals: Story = {
5049
name: 'With decimals (allowDecimals)',
5150
args: {
5251
allowDecimals: true,
53-
placeholder: '0,00',
5452
defaultValue: '1234',
5553
},
5654
};
5755

56+
export const WithPlaceholder: Story = {
57+
name: 'With placeholder',
58+
args: { placeholder: '0' },
59+
};
60+
5861
export const WithValue: Story = {
5962
name: 'With value',
6063
args: { defaultValue: '42' },
@@ -76,13 +79,21 @@ export const Invalid: Story = {
7679
export const Widths: Story = {
7780
name: 'Width variants',
7881
render: () => (
79-
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
80-
<NumberInput width="xs" placeholder="xs" />
81-
<NumberInput width="sm" placeholder="sm" />
82-
<NumberInput width="md" placeholder="md" />
83-
<NumberInput width="lg" placeholder="lg" />
84-
<NumberInput width="xl" placeholder="xl" />
85-
<NumberInput width="full" placeholder="full" />
82+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
83+
{(['xs', 'sm', 'md', 'lg', 'xl', 'full'] as const).map((w) => (
84+
<div key={w}>
85+
<p
86+
style={{
87+
margin: '0 0 0.25rem',
88+
fontWeight: 'bold',
89+
fontSize: '0.875rem',
90+
}}
91+
>
92+
{w}
93+
</p>
94+
<NumberInput width={w} />
95+
</div>
96+
))}
8697
</div>
8798
),
8899
};
@@ -112,7 +123,7 @@ export const AllStates: Story = {
112123
>
113124
Default
114125
</label>
115-
<NumberInput placeholder="0" />
126+
<NumberInput />
116127
</div>
117128
<div>
118129
<label

packages/storybook/src/PasswordInput.stories.tsx

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const meta: Meta<typeof PasswordInput> = {
2727
},
2828
},
2929
args: {
30-
placeholder: 'Wachtwoord',
3130
disabled: false,
3231
readOnly: false,
3332
invalid: false,
@@ -53,10 +52,14 @@ export const NewPassword: Story = {
5352
name: 'New password (registratie)',
5453
args: {
5554
passwordAutocomplete: 'new-password',
56-
placeholder: 'Nieuw wachtwoord',
5755
},
5856
};
5957

58+
export const WithPlaceholder: Story = {
59+
name: 'With placeholder',
60+
args: { placeholder: 'Wachtwoord' },
61+
};
62+
6063
export const Disabled: Story = {
6164
args: { disabled: true, value: 'geheim123' },
6265
};
@@ -73,13 +76,21 @@ export const Invalid: Story = {
7376
export const Widths: Story = {
7477
name: 'Width variants',
7578
render: () => (
76-
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
77-
<PasswordInput width="xs" placeholder="xs" />
78-
<PasswordInput width="sm" placeholder="sm" />
79-
<PasswordInput width="md" placeholder="md" />
80-
<PasswordInput width="lg" placeholder="lg" />
81-
<PasswordInput width="xl" placeholder="xl" />
82-
<PasswordInput width="full" placeholder="full" />
79+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
80+
{(['xs', 'sm', 'md', 'lg', 'xl', 'full'] as const).map((w) => (
81+
<div key={w}>
82+
<p
83+
style={{
84+
margin: '0 0 0.25rem',
85+
fontWeight: 'bold',
86+
fontSize: '0.875rem',
87+
}}
88+
>
89+
{w}
90+
</p>
91+
<PasswordInput width={w} />
92+
</div>
93+
))}
8394
</div>
8495
),
8596
};
@@ -109,7 +120,7 @@ export const AllStates: Story = {
109120
>
110121
Default
111122
</label>
112-
<PasswordInput placeholder="Wachtwoord" />
123+
<PasswordInput />
113124
</div>
114125
<div>
115126
<label

packages/storybook/src/SearchInput.docs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ De SearchInput component is een gespecialiseerd invoerveld voor zoekfunctionalit
2121

2222
## Best practices
2323

24-
- **Gebruik een duidelijke placeholder.** Geef aan wat gebruikers kunnen zoeken (bijv. `Zoek producten...`, `Zoek in artikelen...`).
24+
- **Gebruik een zichtbaar label of `aria-label`.** Voeg altijd een label toe via `FormField` of `FormFieldLabel`, of gebruik `aria-label` als het visuele design geen zichtbaar label toestaat (bijv. een zoekveld in de siteheader). Een placeholder is niet verplicht en verdwijnt bij typen — gebruik het optioneel alleen als extra context over de zoekscope nuttig is (bijv. `Zoek in producten...`).
2525
- **Gebruik `role="search"` op een wrapper element.** Dit helpt screen readers de zoekfunctionaliteit te identificeren.
2626
- **Implementeer live search of debouncing.** Update resultaten tijdens het typen, maar niet bij elke toetsaanslag.
2727
- **Geef feedback bij geen resultaten.** Toon een melding als er geen resultaten zijn gevonden.

packages/storybook/src/SearchInput.stories.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ const meta: Meta<typeof SearchInput> = {
3030
},
3131
},
3232
args: {
33-
placeholder: 'Zoeken...',
3433
disabled: false,
3534
readOnly: false,
3635
invalid: false,
@@ -56,6 +55,11 @@ export const WithValue: Story = {
5655
args: { defaultValue: TEKST },
5756
};
5857

58+
export const WithPlaceholder: Story = {
59+
name: 'With placeholder',
60+
args: { placeholder: 'Zoeken...' },
61+
};
62+
5963
export const Disabled: Story = {
6064
args: { disabled: true, value: TEKST },
6165
};
@@ -72,13 +76,21 @@ export const Invalid: Story = {
7276
export const Widths: Story = {
7377
name: 'Width variants',
7478
render: () => (
75-
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
76-
<SearchInput width="xs" placeholder="xs" />
77-
<SearchInput width="sm" placeholder="sm" />
78-
<SearchInput width="md" placeholder="md" />
79-
<SearchInput width="lg" placeholder="lg" />
80-
<SearchInput width="xl" placeholder="xl" />
81-
<SearchInput width="full" placeholder="full" />
79+
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>
80+
{(['xs', 'sm', 'md', 'lg', 'xl', 'full'] as const).map((w) => (
81+
<div key={w}>
82+
<p
83+
style={{
84+
margin: '0 0 0.25rem',
85+
fontWeight: 'bold',
86+
fontSize: '0.875rem',
87+
}}
88+
>
89+
{w}
90+
</p>
91+
<SearchInput width={w} />
92+
</div>
93+
))}
8294
</div>
8395
),
8496
};
@@ -108,7 +120,7 @@ export const AllStates: Story = {
108120
>
109121
Default
110122
</label>
111-
<SearchInput placeholder="Zoeken..." />
123+
<SearchInput />
112124
</div>
113125
<div>
114126
<label

packages/storybook/src/TelephoneInput.docs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ De TelephoneInput component is een gespecialiseerd invoerveld voor telefoonnumme
2020

2121
## Best practices
2222

23-
- **Gebruik een duidelijke placeholder.** Geef een voorbeeld in het verwachte formaat (bijv. `06 12345678` of `+31 6 12345678`).
23+
- **Gebruik FormFieldDescription voor formaathints.** Als je het verwachte formaat wilt toelichten (bijv. `06 12345678` of `+31 6 12345678`), gebruik dan [FormFieldDescription](/docs/components-formfielddescription--docs) — niet een placeholder. Placeholder tekst verdwijnt bij typen en is daarna niet meer zichtbaar.
2424
- **Dwing geen specifiek formaat af.** Gebruikers typen telefoonnummers op verschillende manieren. Valideer lengte en tekens, maar accepteer variaties in opmaak.
2525
- **Laat browser-autocomplete aan.** De standaard `autocomplete="tel"` helpt gebruikers snel invullen.
2626
- **Combineer met FormField.** Gebruik altijd een label via `FormField` of `FormFieldLabel` voor toegankelijkheid.

0 commit comments

Comments
 (0)