fulfillment tile
CdrFulfillmentTile - Selection variant of CdrSurfaceSelection with additional interactive states Fulfillment tiles present delivery and pickup options in a selectable format. Use these tiles to display shipping methods, store pickup availability, or other fulfillment choices with relevant details and pricing.
The fulfillment tile shows a checked state with descriptive content. It extends the surface selection component, providing a set of features intended for product, checkout, and search pages.
This component comes with styles for checked, disabled, loading, and active states. It also provides:
- A layout for inner elements
- A content component for styling text below the header
- An icon component that can be used in the header
- A header component
These parts allow Cedar users to build a custom version of the fulfillment tile.
All dos and don'ts from surface selection apply, but with some extra criteria:
When to use
- The ability to select a surface is needed, but with a more structured approach to the header and lower content
- When a status icon is helpful within a surface selection
When not to use
- Displaying mixed media, like a color swatch. The surface selection component may be more appropriate for these types of designs ::
Make sure fulfillment tiles:
- Convey the current state at all times (checked, disabled, active, loading)
- Serve a single purpose for a changing an option. It should not contain links or other interactive elements
- Are part of a radio group if used with a
radiorole. The group should be properly labled (see implementation)
Loading
When the loading prop is true, all content in the body and footer slots will have the opacity animated to 0, hiding the content. At this point, the loading slot will appear, while retaining the same size of the component. You can provide a loading slot, or use the default, which is a single CdrSkeletonBone line. The header slot will always display.
Disabled
When a selection is disabled, it will not emit any events.
Orientation
Inside of the component is CdrLayout, a grid that comes with a recommended gap.
Body
The body content is for displaying any lengthier text that needs to be part of the tile. It will attempt to stretch to use any extra space within the tile.
Footer
The footer content is for text that is slightly larger than the body content and has a bit more prominence.
What Cedar provides
- Adds the
aria-checkedattribute at all times - Adds the
disabledattribute
Development responsibilities
When using this component, here's what you are responsible for:
- Always group within a radio group if used with a
radiorole. Make sure the group is properly labled (see implementation) - Add a
tabindex="0"prop when appropriate (most of the time this is true)
Radio
Use the component within a radio group and include a label. Here is a simple example of how you can use the fulfillment tile on the product page:
<div id="radio-label">How would you like to receieve your order?</div>
<div
role="radiogroup"
aria-labelledby="radio-label"
>
<CdrFulfillmentTile
tabindex="0"
role="radio"
:checked="orderPreference === 'pickup'"
@click="orderPreference = 'pickup'"
>
<template #icon-right>
<CdrFulfillmentTileIcon
v-if="orderPreference === 'pickup'"
type="success"
>
<IconCheckFill />
</CdrFulfillmentTileIcon>
</template>
<template #header>Pick up</template>
<template #body>At San Diego</template>
</CdrFulfillmentTile>
<CdrFulfillmentTile
tabindex="0"
role="radio"
:checked="orderPreference === 'ship'"
@click="orderPreference = 'ship'"
>
<template #icon-right>
<CdrFulfillmentTileIcon
v-if="orderPreference === 'ship'"
type="success"
>
<IconCheckFill />
</CdrFulfillmentTileIcon>
</template>
<template #header>Ship it</template>
</CdrFulfillmentTile>
</div>
Checkbox
Use the component as a checkbox. If the content inside does not provide an adequate description, then include a label. Here is an example of how you can use the fulfillment tile on the search page:
<h2>Fulfillment methods available:</h2>
<div role="group">
<div>
<CdrFulfillmentTile
:checked="methods.includes('store')"
role="checkbox"
tabindex="0"
@click="toggleMethod('store')"
>
<template #icon-left>
<div
:class="{ 'example__icon': true, 'example__icon--checked': methods.includes('store') }"
/>
</template>
<template #header>Store Pickup (32)</template>
<template #footer>
In stock at
<strong>Encinitas</strong>
</template>
</CdrFulfillmentTile>
<CdrButton modifier="link">Change store</CdrButton>
</div>
<CdrFulfillmentTile
:checked="methods.includes('ship')"
role="checkbox"
tabindex="0"
@click="toggleMethod('ship')"
>
<template #icon-left>
<div
:class="{ 'example__icon': true, 'example__icon--checked': methods.includes('store') }"
/>
</template>
<template #header>Ship to address (32)</template>
</CdrFulfillmentTile>
</div>
<style
lang="scss"
scoped
>
@import '@rei/cdr-tokens/scss';
.example {
&__icon {
display: inline-block;
vertical-align: middle;
flex: 0 0 auto;
width: 1.8rem;
height: 1.8rem;
margin: $cdr-space-eighth-x;
fill: inherit;
border-radius: $cdr-radius-round;
border: $cdr-space-sixteenth-x solid #2e2e2b;
&--checked {
border-width: 0.7rem;
}
}
}
</style>
When using CdrFulfillmentTile in a TypeScript application, we recommend importing the surfaceSelection interface from the library if you want fine-grained control over the options passed to the component.
ParentComponent.vue
<script setup lang="ts">
import { CdrFulfillmentTile, type surfaceSelection } from '@rei/cedar';
const example: surfaceSelection = {
checked: false,
modifier: 'primary',
role: 'checkbox',
loading: false,
disabled: true,
};
</script>
<template>
<main>
<CdrFulfillmentTile v-bind="example">...</CdrFulfillmentTile>
</main>
</template>
CdrFulfillmentTile
Props
| Name | Type | Default |
|---|---|---|
tag
| div | |
role
| checkbox | |
borderColor
| primary | |
borderStyle
| solid | |
borderWidth
| sixteenth-x | |
borderRadius
| soft |
Slots
| Name |
|---|
icon-left Icon to display on the left of the header. |
header Header content that is still visible during loading. |
icon-right Icon to display on the right of the header. |
body Default font size is a step down. Placed just below the header. |
footer Footer content will be at the bottom of the component. |