• Organism
  • CORE COMPONENT
  • Taxonomic name: O-PROGRESSIVE-DISCLOSURE
  • Added on: v4.8.0 (29/03/19)
  • Updated on: v5.5.0 (23/03/21)

Progressive disclosure

Progressive disclosure opens acts to reveal nested questions within forms and provide positive friction in a user's journey to better help them to progroess through and complete a form.


Design and usage

Structure

  • Always appears immediately below the form component that triggers it
  • Content that appears after the progressive disclosure area is spaced 30px away from the bottom of the box
  • Can only be used on white (#fff) and lightest grey (#f9f9f9) with a white background to the progressive disclosure area, and light grey (#eee) with a white progressive disclosure area.
  • Do not use on any other accent colours
  • Do not use within cards
  • Note: Nested content is not indented further
  • Mobile: Progressive disclosure extends to full width of the browser; all other structure rules still apply
  • Non-JS: When JS is disabled, notifications appear to guide the user; these use the new notification pattern and appear below the question that has been answered.

Usage

Currently only for use with:

  • Radio
  • Radio single select cards (all versions without any sub form fields)
  • Dropdown
  • Checkbox toggle
  • Single checkbox
  • Single checkbox within checkbox group e.g. 'other' option

Errors

  • Follows standard rules for content within progressive disclosure

Non-JS requirements and considerations

We are providing two options for non-JS to give you the flexibility to build as you see fit. The preferred UX approach is option 1, but if your project doesn't have the capabilities to build the pages up server-side, then option 2 is available. It's important that, if you use option 2, you keep your form content minimal.

  • Option 1 - Server-side approach - The journey is built up server-side, with buttons reloading the page with the next set of relevant questions when triggered. Included in this would be in-page anchoring to automatically take you to the next set of questions.

    1. A page that contains progressive disclosure loads all the content up to the form component that triggers further questions
    2. A 'Next' button is positioned after that form component
    3. The page reloads with the new questions revealed and auto scrolls to an anchor point above the trigger question.
    4. A notification is visible between the trigger question and the revealed questions to guide the user on what might happen if the answer above is changed.
    5. Any further nested questions are revealed in the same way
    6. If there are no further progressive disclosure questions the rest of the page content is loaded and the user can proceed
  • Option 2 - Client-side approach - When Javascript isn't available, all progressive disclosure questions are revealed and a notification is included between the form and the green line to give the user direction on which questions to answer.

Caution: Be mindful of the length of your question sets - even when JS is available, you might see some of the non-JS detail before the script loads, which could be confusing to the user; shorter question sets will minimise this effect.

Animation

  • Animation of progressive disclosure starts when the triggers animation stops
  • All animations have an ease in and out
  • Green line and arrow fade in over 0.3 seconds at the same time as the background colour fades in and extends to the desired height for whatever amount of questions are revealed
  • Each form question revealed fades in over 0.5 seconds (starting from the top) and is staggered by 0.1 seconds to the end of the one that appears before
  • If there are a lot of questions, the background fade in and height grow speed changes; rule of thumb is 0.1 second for each form component (so 3 questions would mean 0.3 seconds)
  • The same animation rules apply for nested content
  • When selecting a question that closes progressive disclosure all form components, green line and background colour and height fade out over 0.3 seconds

Accessibility

  • Aria-invalid must be used to indicate when an error has occurred with this element
  • Aria-describedby must be used to link any help, instructional and/or error text with the element/form fields

Interactive examples

Simple example - Option 1 (server-side approach)

This simple example uses a king question revealing options which lead to one further level of nesting only. Examples areas are shown which are controlled by segmented controls (king question), checkboxes (question 1.2) and dropdowns (question 1.3), however the example below only displays the first two steps (king question and first disclosure area) as an example.

Step one

Step two

Step two - errored

Simple example - Option 2 (client-side approach)

This simple example uses a king question revealing options which lead to one further level of nesting only. Examples areas are shown which are controlled by segmented controls (king question), checkboxes (question 1.2) and dropdowns (question 1.3).

Complex example (using Option 2 - server-side approach)

This complex example uses up to five levels of nesting to create an intricate journey. Care should be taken when creating forms with this level of complexity, and it should be noted that the disclosure areas work like a tree in terms of being able to go back up towards the trunk, but not jumping across branches. Be particularly careful with numbering of questions - while it may make sense to number questions 1, 2, 3, etc in each branch, this would result in duplicate question numbering if everything is exposed when JavaScript is not available.

Code examples

Segemented control example

<fieldset>
	<legend>...</legend>
	<fieldset class="m-form-row m-form-row--disclosure [ Modifiers ]" data-module="o-progressive-disclosure">
		<legend class="a-label">...</legend>
		<div class="m-form-row__content">
			<ul class="m-form-row-group m-form-row-group--option-group m-radio-segmented--2" data-module="m-radio-segmented">
				<li class="m-form-row-group-item m-form-row-group-item--full-width">
					<label class="a-radio" for="...">
						<input class="a-radio__input" type="radio" name="..." id="..." value="..." data-disclosure="..." />
						<span class="a-radio__label"><span class="a-radio__label-inner">...</span></span>
						<span class="a-radio__ui"></span>
					</label>
				</li>
				<li class="m-form-row-group-item m-form-row-group-item--full-width">
					<label class="a-radio" for="...">
						<input class="a-radio__input" type="radio" name="..." id="..." value="..." />
						<span class="a-radio__label"><span class="a-radio__label-inner">...</span></span>
						<span class="a-radio__ui"></span>
					</label>
				</li>
			</ul>
		</div>
	</fieldset>	<div class="o-progressive-disclosure [ Modifiers ]" id="...">
		<div class="o-progressive-disclosure__message m-card m-card-notification m-card-notification--neutral t-accent-light t-accent-light--1" role="status">
			<div class="m-card-content">
				<div class="m-card-content__inner">
					<p>...</p>
				</div>
			</div>
		</div>		<div class="o-progressive-disclosure-container">
			<fieldset>
				<legend>...</legend>
				<div class="m-form-row m-form-row--full-width u-margin--top-none">
					...
				</div>
				...				<div class="o-progressive-disclosure-container__additional-content">
					<!-- Any additional content that is required -->
				</div>
				<div class="m-form-row m-form-row--full-width u-margin--bottom-none">
					...
				</div>
			</fieldset>
		</div>
	</div>	<div class="m-form-row">
		<button class="a-button a-button--primary a-button--icon a-button--next" type="submit"><span class="a-button__inner">...</span></button>
	</div>
</fieldset>

Dropdown example

<div class="m-form-row m-form-row--disclosure [ Modifiers ]" data-module="o-progressive-disclosure">
	<label class="a-label" for="...">...</label>
	<div class="m-form-row__content">
		<span class="a-dropdown">
			<select id="..." class="a-dropdown__select">
				<option value="">Please select</option>
				<optgroup label="...">
					<option value="..." data-disclosure="...">...</option>
					<option value="...">...</option>
					...
				</optgroup>
				<option value="..." data-disclosure="...">...</option>
				<option value="...">...</option>
				...
			</select>
			<span class="a-dropdown__ui"></span>
		</span>
	</div>
</div>
...

Enhanced example

Careful nesting can be used to allow multiple answers to link to the same set of follow-up questions. Currently, multiple answers in different sections cannot link to question sets within different nesting groups.

<!-- King question -->
<fieldset>
	<legend>...</legend>
	<fieldset class="m-form-row m-form-row--disclosure" data-module="o-progressive-disclosure">
		<legend class="a-label">...</legend>
		<div class="m-form-row__content">
			<!-- Progressive disclosure control -->
		</div>
	</fieldset>	<!-- Level 1 -->
	<div class="o-progressive-disclosure" id="...">
		<div class="o-progressive-disclosure__message m-card m-card-notification m-card-notification--neutral t-accent-light t-accent-light--1" role="status">
			...
		</div>
		<div class="o-progressive-disclosure-container">
			<fieldset>
				<legend>...</legend>
				<div class="m-form-row m-form-row--full-width u-margin--top-none">
					<!-- Standard form control -->
				</div>
				<fieldset class="m-form-row m-form-row--disclosure m-form-row--full-width" data-module="o-progressive-disclosure">
					<legend class="a-label">...</legend>
					<div class="m-form-row__content">
						<!-- Progressive disclosure control -->
					</div>
				</fieldset>				<!-- Level 2 - option 1 -->
				<div class="o-progressive-disclosure" id="...">
					<!-- Progressive disclosure message -->
					<div class="o-progressive-disclosure-container">
						<fieldset>
							<legend>...</legend>
							<div class="m-form-row m-form-row--full-width u-margin--top-none">
								<!-- Standard form control -->
							</div>
							<fieldset class="m-form-row m-form-row--disclosure m-form-row--full-width" data-module="o-progressive-disclosure">
								<legend class="a-label">...</legend>
								<div class="m-form-row__content">
									<!-- Progressive disclosure control -->
									<!-- Multiple inputs can use the same data-disclosure="..." attribute to link to further nested levels within this nesting block, ie Level 3 options 1 or 2, but not those in other areas, id Level 3 options 3 or 4 -->
								</div>
							</fieldset>							<!-- Level 3 - option 1 (available from Level 2 option 1 only)  -->
							<div class="o-progressive-disclosure" id="...">
								<!-- Progressive disclosure message -->
								<div class="o-progressive-disclosure-container">
									<fieldset>
										<legend>...</legend>
										<div class="m-form-row m-form-row--full-width u-margin--top-none">
											<!-- Standard form control -->
										</div>
										<fieldset class="m-form-row m-form-row--disclosure m-form-row--full-width" data-module="o-progressive-disclosure">
											<legend class="a-label">...</legend>
											<div class="m-form-row__content">
												<!-- Progressive disclosure control -->
											</div>
										</fieldset>
										<div class="o-progressive-disclosure" id="...">
											<!-- Progressive disclosure end point -->
										</div>
									</fieldset>
								</div>
							</div>							<!-- Level 3 - option 2 (available from Level 2 option 1 only)  -->
							<div class="o-progressive-disclosure" id="...">
								<!-- Progressive disclosure message -->
								<div class="o-progressive-disclosure-container">
									<fieldset>
										<legend>...</legend>
										<div class="m-form-row m-form-row--full-width u-margin--top-none">
											<!-- Standard form control -->
										</div>
										<fieldset class="m-form-row m-form-row--disclosure m-form-row--full-width" data-module="o-progressive-disclosure">
											<legend class="a-label">...</legend>
											<div class="m-form-row__content">
												<!-- Progressive disclosure control -->
											</div>
										</fieldset>
										<div class="o-progressive-disclosure" id="...">
											<!-- Progressive disclosure end point -->
										</div>
									</fieldset>
								</div>
							</div>
						</fieldset>
					</div>
				</div>				<!-- Level 2 - option 2 -->
				<div class="o-progressive-disclosure" id="...">
					<!-- Progressive disclosure message -->
					<div class="o-progressive-disclosure-container">
						<fieldset>
							<legend>...</legend>
							<div class="m-form-row m-form-row--full-width u-margin--top-none">
								<!-- Standard form control -->
							</div>
							<fieldset class="m-form-row m-form-row--disclosure m-form-row--full-width" data-module="o-progressive-disclosure">
								<legend class="a-label">...</legend>
								<div class="m-form-row__content">
									<!-- Progressive disclosure control -->
								</div>
							</fieldset>							<!-- Level 3 - option 3 (available from Level 2 option 2 only) -->
							<div class="o-progressive-disclosure" id="...">
								<!-- Progressive disclosure message -->
								<div class="o-progressive-disclosure-container">
									<fieldset>
										<legend>...</legend>
										<div class="m-form-row m-form-row--full-width u-margin--top-none">
											<!-- Standard form control -->
										</div>
										<fieldset class="m-form-row m-form-row--disclosure m-form-row--full-width" data-module="o-progressive-disclosure">
											<legend class="a-label">...</legend>
											<div class="m-form-row__content">
												<!-- Progressive disclosure control -->
											</div>
										</fieldset>
										<div class="o-progressive-disclosure" id="...">
											<!-- Progressive disclosure end point -->
										</div>
									</fieldset>
								</div>
							</div>
						</fieldset>
					</div>
				</div>				<!-- Level 2 - option 3 -->
				<!-- etc -->
			</fieldset>
		</div>
	</div>
</fieldset>

Development and test

Notes for developers

To setup the client-side progressive disclosure, include data-module="o-progressive-disclosure" on the from row for the king question. On any inputs/options for that king question that are to open a progressive disclosure area, add the attribute data-disclosure with the value being the id for the progressive disclosure area, eg data-disclosure="level_1" to link to the progressive disclosure with id="level_1".

All inputs/options within a king question must have the value attribute set, even if it is left empty.

The first row or additional content area inside a top level progressive disclosure must have its top margin removed.

The last row or additional content area inside a top level progressive disclosure must have its bottom margin removed.

Any sub-disclosure areas can be set up in the same way but with no margin resets.

Careful nesting can be used to allow multiple answers to link to the same set of follow-up questions - see example below for more information.

Note: Responses in areas which become hidden if a user changes an answer are not cleared; this could potentially result in every question in the form being completed, so the submitted results will need to be parsed in order to follow the correct path.

Note: Currently the JavaScript for progressive disclosure does not support loading in questions via AJAX. This will be included in a future release.

Aria usage for errors and help text

To aid screen reader users in completing forms without error and also with understanding and fixing any errors that have occured, some additional aria attributes are now recommended to be applied across all form fields/rows.

All form row help, explanatory text, form row instructions and form row errors must have a unique id assigned to them and be appropriately linked to either the individual form input or the form row using aria-describedby. This ensures that the help text is read out immediately on focus of the form input/fieldset. When there is more that one item that requires referencing within the aria-describedby attribute the id values must be supplied in the form of a space separated list. For example if there is an error with id of 'err1' and help text with id of 'help1' the value for the aria-describedby attribute would be 'err1 help1'.

All inline form error messages should start with the hidden text 'Error: ' so that when read by out by a screen reader it is immediately clear that the text being read is an error message.

All erroring form fields (or fieldset if a group or radios/checkboxes) must have aria-invalid="true" set on them, ensuring that when a user focus' on an errored form field, the field will be announced as invalid, so the user is then aware that there in an issue that requires fixing.

When linking help or errors, or even adding aria-invalid to the markup careful consideration must be taken as to where it is most appropriate to apply the value. The following rules should be used to decide where the attribute(s) should be applied.

  • Single form field - All attributes should be applied directly to that form field
  • Group of form fields but only one in error or help text applies to only one - Apply the attribute(s) to the only field in error or where the help text applies
  • Group of form field where all in error or the help text applies to all - Apply the attribute(s) to the groups surrounding fieldset (form row)

There may situations where more than one of these rules applies to the overall form row for example a date range where the instruction text applies to both fields so should be linked to the fieldset for the date range but one of the two fields is in error so the error should be linked to just that field not to the fieldset.


Notes for testers

  • It must be clear to the user what questions they must answer. This is particularly important when using option 2 with JavaScript not available.
  • When using option 2 and JavaScript is available any notification messsage must be hidden.
  • When using option 2 and JavaScript is available, any notification messsage must be hidden. When the page loads, any progressive disclosure areas which should be open due to options already being selected must be opened immediately without animation. On any subsequent changes, the progressive disclosure areas must animate in and out.

Classes overview

The following table gives you a quick overview of the CSS classes that can be applied.

Class Outcome Required Applied to Comments
.m-form-row--disclosure Base modifier for the king question Yes .m-form-row To be applied to an question that had a progressive disclosure area associated to it
.o-progressive-disclosure Base style for the progressive disclosure area Yes .m-form-row--disclosure + div  
.o-progressive-disclosure--full-width Modifer to make the disclosure area display full width the same as the form rows   .o-progressive-disclosure  
.o-progressive-disclosure__message Modifier style for the notification message for the progressive disclosure area Yes .o-progressive-disclosure > .m-card-notification Must be at the top of the progressive disclosure area before the .o-progressive-disclosure-container
.o-progressive-disclosure-container Base style for the content container within the progressive disclosure area Yes .o-progressive-disclosure__message + div  
.o-progressive-disclosure-container__additional-content Base style container for general content that is not part of a form row   .o-progressive-disclosure-container > div  

Component releases

  • Added on: v4.8.0 (29/03/19)
  • Updated on: v5.5.0 (23/03/21)

Latest update:

  • updated: Additional aria attributes are now recommended to be applied across all form fields/rows so as to aid screen reader users in completing forms without error and also with understanding and fixing any errors that have occurred.

Full version history

A full history of changes and enhancements detailing all minor and major updates to the component.

View full version history

Want something new in Framework, or to chat about an issue you're having with it?

Contact the team