Select (single)
<select>
elements are difficult to style while maintaining accessibility. The easiest (and possibly the best) way to create an accessible <Select />
component is to use the native <select>
element and simply style the button part while leaving the dropdown as is provided natively by the browser.
Example Usage
options
elements with “primitive” data type
Most <Select />
components will probably just use a simple array of string values as in this example:
<script>
import { Select, Checkbox } from "@fanny-pack-ui/svelte-kit";
const jobOptionsStrings = [
"UI/UX Designer",
"Frontend Engineer",
"Backend Engineer",
"QA Engineer",
"Unicorn",
];
let selectedJobOptionString = jobOptionsStrings[0];
let needVacation = false;
function handleChange(event) {
console.log("Selected Value:", event.target.value);
}
</script>
<div style="margin-bottom:20px;">
<Select
label="What Job Are You Interested In?"
options={jobOptionsStrings}
bind:value={selectedJobOptionString}
padding="var(--select-default-padding)"
fontSize="var(--select-default-font-size)"
disabled={needVacation}
on:change={handleChange}
/>
</div>
<Checkbox
bind:checked={needVacation}
label="I need a vacation instead of a job"
/>
Value of selectedJobOptionString
: UI/UX Designer
options
elements with “object” data type
What if you want each option in your <Select />
component to have values that are different from their labels? Take the following native select element as an example. The code for it is below.
<select>
<option value="">-- Select Role --</option>
<option value="ds">UI/UX Designer</option>
<option value="fe">Frontend Engineer</option>
<option value="be">Backend Engineer</option>
<option value="qa">QA Engineer</option>
<option value="un">Unicorn</option>
</select>
Notice how the values for each option are different from their labels? You would probably have to pass an array of objects that looked something like this to the select element:
const jobOptionsObjects = [
{ roleVal: "", roleLabel: "-- Select Role --" },
{ roleVal: "ds", roleLabel: "UI/UX Designer" },
{ roleVal: "fe", roleLabel: "Frontend Engineer" },
{ roleVal: "be", roleLabel: "Backend Engineer" },
{ roleVal: "qa", roleLabel: "QA Engineer" },
{ roleVal: "un", roleLabel: "Unicorn" },
];
The problem is that native select elements can only handle primitive data types, so you might have to pass an array of strings to your Svelte select element (as in the first example) and just live with that. No separate values or labels. Bummer. However, you can pass an object to a native select element written with Svelte and just make a few adjustments to your Svelte code, which would look like this:
Value of selectedValuePropertyFromJobOptionsObject
:
<script>
const jobOptionsObjects = [
{ roleVal: "", roleLabel: "-- Select Role --" },
{ roleVal: "ds", roleLabel: "UI/UX Designer" },
{ roleVal: "fe", roleLabel: "Frontend Engineer" },
{ roleVal: "be", roleLabel: "Backend Engineer" },
{ roleVal: "qa", roleLabel: "QA Engineer" },
{ roleVal: "un", roleLabel: "Unicorn" },
];
let selectedValuePropertyFromJobOptionsObject = jobOptionsObjects[0]["roleVal"];
</script>
<select bind:value={selectedValuePropertyFromJobOptionsObject}>
{#each jobOptionsObjects as option}
<option value={option["roleVal"]}>{option["roleLabel"]}</option>
{/each}
</select>
You can do the same with this Fanny Pack <Select />
component. You just have to specify which properties in your data objects should be used as the option values and which should be the option labels:
Value of selectedValuePropertyFromJobOptionsObject
:
<script>
const jobOptionsObjects = [
{ roleVal: "", roleLabel: "-- Select Role --" },
{ roleVal: "ds", roleLabel: "UI/UX Designer" },
{ roleVal: "fe", roleLabel: "Frontend Engineer" },
{ roleVal: "be", roleLabel: "Backend Engineer" },
{ roleVal: "qa", roleLabel: "QA Engineer" },
{ roleVal: "un", roleLabel: "Unicorn" },
];
let selectedValuePropertyFromJobOptionsObject = jobOptionsObjects[0]["roleVal"];
</script>
<Select
label="What Job Are You Interested In?"
options={jobOptionsObjects}
optionValue="roleVal"
optionLabel="roleLabel"
bind:value={selectedValuePropertyFromJobOptionsObject}
/>
Option Groups
You can group your options using the <optgroup>
tag by passing a property name to the optgroup
prop. The property name that you pass should be a property name that exists in the objects that are within your options
array. The optgroup
prop is only used with options
arrays that contain objects, not with options
arrays that contain primitive values.
Value of selectedDinoValue
: diplodocus
<script>
const dinoObjects = [
{ dinoGroup: "Theropods", dinoValue: "tyrannosaurus", dinoLabel: "Tyrannosaurus" },
{ dinoGroup: "Theropods", dinoValue: "velociraptor", dinoLabel: "Velociraptor" },
{ dinoGroup: "Sauropods", dinoValue: "diplodocus", dinoLabel: "Diplodocus" },
{ dinoGroup: "Sauropods", dinoValue: "saltasaurus", dinoLabel: "Saltasaurus" },
{ dinoGroup: "Theropods", dinoValue: "deinonychus", dinoLabel: "Deinonychus" },
{ dinoGroup: "Sauropods", dinoValue: "apatosaurus", dinoLabel: "Apatosaurus" },
];
let selectedDinoValue = dinoObjects.find(obj => obj.dinoValue === "diplodocus")["dinoValue"];
</script>
<Select
label="Select an option"
options={dinoObjects}
optgroup="dinoGroup"
optionValue="dinoValue"
optionLabel="dinoLabel"
bind:value={selectedDinoValue}
/>
Value of `selectedDinoValue`: <code>{selectedDinoValue}</code>
Note that the optgroup
s within the <Select />
component will be sorted based on the first appearance of the property that is passed to the optgroup
prop. For example, in the dinoObjects
array shown above, the dinoGroup
property is passed to the optgroup
prop, so the dinoObjects
array will be sorted based the dinoGroup
property. The first dinoGroup
property that appears in the dinoObjects
array has a value of Theropods
, so that will be the first optgroup
listed in the <Select />
component’s options list. The second dinoGroup
property that appears has a value of Sauropods
, so that will be the second optgroup
listed in the <Select />
component’s options list. And so on.
Note
- The
<Select />
component will fill the width of its parent element. So if you want a<Select />
component to be narrower, then you will have to set its parent element to be narrower.
Customize Select Style Props
The original intention for these custom styles was to set --custom-select-bg-color="transparent"
so the <Select />
component would blend into the background. A few extra custom style rules have been provided for even more customizability.
You can set the following custom variables:
--custom-select-bg-color
--custom-select-border-color
--custom-select-text-color
--custom-option-text-color
<div style="margin-bottom:20px" class="alt-background">
<Select
options={months}
bind:value={selectedMonth}
--custom-select-bg-color="transparent"
--custom-select-border-color="white"
--custom-select-text-color="white"
--custom-option-text-color="var(--secondary-color)"
/>
</div>
<style>
.alt-background {
padding: 25px;
border-radius: var(--border-radius);
background-color: var(--secondary-color);
}
</style>
Props
Prop name | Type | Possible values | Default value | Description |
---|---|---|---|---|
label (optional) | string | Any string | "" (an empty string) | The text for the <label> element that is displayed above the <select> element. If this prop is not provided, then no label will be displayed. |
options | Array | Any array | NA | This should be an array of strings, numbers, booleans, or objects. This array will be used to populate the <option> elements in the <Select /> component’s dropdown list. |
optionValue (only used with arrays of objects) | string | Any property name from the objects that are passed to the options array | null | When the optionValue prop is used with an array of objects (which are passed to the options prop), the optionValue prop will provide the property name that will be used as the value for each of the options in the <Select /> component. |
optionLabel (only used with arrays of objects) | string | Any property name from the objects that are passed to the options array | null | When the optionLabel prop is used with an array of objects (which are passed to the options prop), the optionLabel prop will provide the property name that will be used as the label text for each of the options in the <Select /> component. |
optgroup (only used with arrays of objects) | string | Any property name from the objects that are passed to the options array | null | You can group your options using the <optgroup> tag by passing a property name to the optgroup prop. The property name that you pass should be a property name that exists in the objects that are within your options array. The optgroup prop is only used with options arrays that contain objects, not with options arrays that contain primitive values. |
bind:value | string , number , boolean , object | Any element from the options array | NA There is no default value for this prop. However, you should set bind:value to equal a value from the array that you pass to the options prop. The value that bind:value is equal to will be the default value displayed in the select box. | When a user selects an option from the <Select /> component, that option will be bound to the variable that is passed to this prop. |
fontSize | string | Any CSS font size value or CSS font size variable from your theme.css file. | var(--select-default-font-size) | This prop will set the font size for the <Select /> component.The default value can be changed in the theme.css file. |
padding | string | Any CSS padding value or CSS size variable from your theme.css file. | var(--select-default-padding) | This prop will set the padding for the <Select /> component.The default value can be changed in the theme.css file. |
disabled | boolean | true , false | false | This prop will disable the <Select /> component. |
{...restProps} | NA | Any attribute that you can pass to a <select> element. | NA | This component does not specify every possible attribute that you can pass to a <select> element. However, restProps allows you to pass any attributes to this <select /> component that you could normally pass to a <select> element. For example, if you want to specify an id for this <Select> component, then you could pass the id prop, like this: id="some-id" . |
Event Forwarding
Event | Description |
---|---|
on:change | This component forwards the change event, so you can call an event handler when a user selects a value in the <Select /> component. |