Year Range Picker
Alpha2020 | 2021 | 2022 | 2023 |
2024 | 2025 | 2026 | 2027 |
2028 | 2029 | 2030 | 2031 |
<script setup lang="ts">
import { CalendarDate } from '@internationalized/date';
import { YearRangePickerCell, YearRangePickerCellTrigger, YearRangePickerGrid, YearRangePickerGridBody, YearRangePickerGridRow, YearRangePickerHeader, YearRangePickerHeading, YearRangePickerNext, YearRangePickerPrev, YearRangePickerRoot } from 'akar';
import './styles.css';
const defaultValue = {
start: new CalendarDate(2020, 1, 1),
end: new CalendarDate(2024, 1, 1),
};
</script>
<template>
<div class="akar-demo akar-demo-YearRangePicker">
<YearRangePickerRoot
v-slot="{ grid }"
:default-value="defaultValue"
class="YearRangePicker"
>
<YearRangePickerHeader class="YearRangePickerHeader">
<YearRangePickerPrev class="YearRangePickerNavButton">
<PIcon
name="i-lucide-chevron-left"
class="Icon"
/>
</YearRangePickerPrev>
<YearRangePickerHeading class="YearRangePickerHeading" />
<YearRangePickerNext class="YearRangePickerNavButton">
<PIcon
name="i-lucide-chevron-right"
class="Icon"
/>
</YearRangePickerNext>
</YearRangePickerHeader>
<div class="YearRangePickerWrapper">
<YearRangePickerGrid class="YearRangePickerGrid">
<YearRangePickerGridBody class="YearRangePickerGridWrapper">
<YearRangePickerGridRow
v-for="(years, index) in grid.rows"
:key="`year-${index}`"
class="YearRangePickerGridRow"
>
<YearRangePickerCell
v-for="year in years"
:key="year.toString()"
:date="year"
class="YearRangePickerCell"
>
<YearRangePickerCellTrigger
:year="year"
class="YearRangePickerCellTrigger"
/>
</YearRangePickerCell>
</YearRangePickerGridRow>
</YearRangePickerGridBody>
</YearRangePickerGrid>
</div>
</YearRangePickerRoot>
</div>
</template>@import '../_colors/black-alpha.css';
@import '../_colors/grass.css';
@scope (.akar-demo-YearRangePicker) {
.Icon {
width: 1rem;
height: 1rem;
}
.YearRangePicker {
margin-top: 1.5rem;
border-radius: 0.75rem;
border: 1px solid #e5e5e5;
background-color: #ffffff;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
padding: 1rem;
}
.YearRangePickerHeader {
display: flex;
justify-content: space-between;
align-items: center;
}
.YearRangePickerNavButton {
display: inline-flex;
justify-content: center;
align-items: center;
width: 2rem;
height: 2rem;
color: #000000;
background-color: transparent;
cursor: pointer;
border-radius: 0.375rem;
}
.YearRangePickerNavButton:hover {
background-color: #fafaf9;
}
.YearRangePickerNavButton:focus {
box-shadow: 0 0 0 2px #000000;
}
.YearRangePickerHeading {
font-size: 0.875rem;
font-weight: 500;
color: #000000;
}
.YearRangePickerWrapper {
padding-top: 1rem;
}
.YearRangePickerGrid {
width: 100%;
user-select: none;
}
.YearRangePickerGridWrapper {
display: grid;
gap: 0.25rem 0;
}
.YearRangePickerGridRow {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 0.25rem;
}
.YearRangePickerCell {
position: relative;
text-align: center;
}
.YearRangePickerCellTrigger {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 3rem;
height: 3rem;
border-radius: 0.5rem;
font-size: 0.875rem;
font-weight: 400;
color: #000000;
background-color: transparent;
outline: none;
cursor: pointer;
border: none;
}
.YearRangePickerCellTrigger:hover {
background-color: var(--grass-5);
}
.YearRangePickerCellTrigger:focus {
box-shadow: 0 0 0 2px #000000;
}
.YearRangePickerCellTrigger[data-disabled] {
color: rgba(0, 0, 0, 0.3);
pointer-events: none;
}
.YearRangePickerCellTrigger[data-selected] {
background-color: var(--grass-10);
color: #ffffff;
font-weight: 500;
}
.YearRangePickerCellTrigger[data-highlighted] {
background-color: var(--grass-5);
}
.YearRangePickerCellTrigger[data-unavailable] {
color: rgba(0, 0, 0, 0.3);
text-decoration: line-through;
pointer-events: none;
}
.YearRangePickerCellTrigger::before {
content: '';
position: absolute;
top: 0.25rem;
width: 0.25rem;
height: 0.25rem;
border-radius: 9999px;
display: none;
}
.YearRangePickerCellTrigger[data-today]::before {
display: block;
background-color: var(--grass-9);
}
.YearRangePickerCellTrigger[data-selected]::before {
background-color: #ffffff;
}
}Features
- Full keyboard navigation
- Can be controlled or uncontrolled
- Focus is fully managed
- Localization support
- Highly composable
Preface
The component depends on the @internationalized/date package, which solves a lot of the problems that come with working with dates and times in JavaScript.
We highly recommend reading through the documentation for the package to get a solid feel for how it works, and you'll need to install it in your project to use the date-related components.
Installation
Install the date package.
pnpm add @internationalized/date
yarn add @internationalized/date
npm install @internationalized/date
bun add @internationalized/date
Install the component from your command line.
pnpm add akar
yarn add akar
npm install akar
bun add akar
Anatomy
Import all parts and piece them together.
<script setup>
import {
YearRangePickerCell,
YearRangePickerCellTrigger,
YearRangePickerGrid,
YearRangePickerGridBody,
YearRangePickerGridRow,
YearRangePickerHeader,
YearRangePickerHeading,
YearRangePickerNext,
YearRangePickerPrev,
YearRangePickerRoot,
} from 'akar';
</script>
<template>
<YearRangePickerRoot>
<YearRangePickerHeader>
<YearRangePickerPrev />
<YearRangePickerHeading />
<YearRangePickerNext />
</YearRangePickerHeader>
<YearRangePickerGrid>
<YearRangePickerGridBody>
<YearRangePickerGridRow>
<YearRangePickerCell>
<YearRangePickerCellTrigger />
</YearRangePickerCell>
</YearRangePickerGridRow>
</YearRangePickerGridBody>
</YearRangePickerGrid>
</YearRangePickerRoot>
</template>
API Reference
Root
Contains all the parts of a year range picker
| Prop | Default | Type |
|---|---|---|
allowNonContiguousRanges | false | booleanWhen combined with |
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
calendarLabel | stringThe accessible label for the calendar | |
defaultPlaceholder | DateValueThe default placeholder date | |
defaultValue | { start: undefined, end: undefined } | DateRangeThe default value for the calendar |
dir | "ltr" | "rtl"The reading direction of the calendar when applicable. | |
disabled | false | booleanWhether or not the calendar is disabled |
fixedDate | "start" | "end"Which part of the range should be fixed | |
initialFocus | false | booleanIf true, the calendar will focus the selected year on mount |
isYearDisabled | MatcherA function that returns whether or not a year is disabled | |
isYearUnavailable | MatcherA function that returns whether or not a year is unavailable | |
locale | stringThe locale to use for formatting dates | |
maximumYears | numberThe maximum number of years that can be selected in a range | |
maxValue | DateValueThe maximum date that can be selected | |
minValue | DateValueThe minimum date that can be selected | |
modelValue | DateRange | nullThe controlled selected year range of the year range picker. Can be bound as | |
nextPage | ((placeholder: DateValue) => DateValue)A function that returns the next page of the calendar. | |
placeholder | DateValueThe placeholder date, which is used to determine what year range to display when no date is selected. | |
preventDeselect | false | booleanWhether or not to prevent the user from deselecting a date without selecting another date first |
prevPage | ((placeholder: DateValue) => DateValue)A function that returns the previous page of the calendar. | |
readonly | false | booleanWhether or not the calendar is readonly |
yearsPerPage | 12 | numberNumber of years to display per page |
| Event | Payload |
|---|---|
update:modelValue | date: DateRangeEvent handler called whenever the model value changes |
update:placeholder | date: DateValueEvent handler called whenever the placeholder value changes |
update:startValue | date: DateValueEvent handler called whenever the start value changes |
| Slot (default) | Payload |
|---|---|
date | DateValueThe current date of the placeholder |
grid | Grid<DateValue>The grid of years |
locale | stringThe calendar locale |
modelValue | DateRangeThe current date range |
| Data attribute | Value |
|---|---|
[data-readonly] | Present when readonly |
[data-disabled] | Present when disabled |
[data-invalid] | Present when invalid |
Header
Contains the navigation buttons and the heading segments.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Prev Button
Calendar navigation button. It navigates the calendar one page (12 years by default) in the past.
| Prop | Default | Type |
|---|---|---|
as | "button" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
prevPage | ((placeholder: DateValue) => DateValue)The function to be used for the prev page. Overwrites the |
| Slot (default) | Payload |
|---|---|
disabled | booleanCurrent disable state |
| Data attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
Next Button
Calendar navigation button. It navigates the calendar one page (12 years by default) in the future.
| Prop | Default | Type |
|---|---|---|
as | "button" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
nextPage | ((placeholder: DateValue) => DateValue)The function to be used for the next page. Overwrites the |
| Slot (default) | Payload |
|---|---|
disabled | booleanCurrent disable state |
| Data attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
Heading
Heading for displaying the current year range.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
| Slot (default) | Payload |
|---|---|
headingValue | stringCurrent year range heading |
| Data attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
Grid
Container for wrapping the year range picker grid.
| Prop | Default | Type |
|---|---|---|
as | "table" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
| Data attribute | Value |
|---|---|
[data-readonly] | Present when readonly |
[data-disabled] | Present when disabled |
Grid Body
Container for wrapping the grid body.
| Prop | Default | Type |
|---|---|---|
as | "tbody" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Grid Row
Container for wrapping the grid row.
| Prop | Default | Type |
|---|---|---|
as | "tr" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Cell
Container for wrapping the year range picker cells.
| Prop | Default | Type |
|---|---|---|
as | "td" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
date* | DateValueThe date value for the cell |
| Data attribute | Value |
|---|---|
[data-disabled] | Present when disabled |
Cell Trigger
Interactable container for displaying the cell years. Clicking it selects the year.
| Prop | Default | Type |
|---|---|---|
as | "div" | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
year* | DateValueThe date value provided to the cell trigger |
| Slot (default) | Payload |
|---|---|
yearValue | stringCurrent year value |
disabled | booleanCurrent disable state |
selected | booleanCurrent selected state |
today | booleanCurrent year is today's year state |
unavailable | booleanCurrent unavailable state |
highlighted | booleanCurrent highlighted state |
highlightedStart | booleanCurrent highlighted start state |
highlightedEnd | booleanCurrent highlighted end state |
selectionStart | booleanCurrent selection start state |
selectionEnd | booleanCurrent selection end state |
| Data attribute | Value |
|---|---|
[data-selected] | Present when selected |
[data-value] | The ISO string value of the date. |
[data-disabled] | Present when disabled |
[data-unavailable] | Present when unavailable |
[data-today] | Present when the year is the current year |
[data-selection-start] | Present when the year is the start of the selection. |
[data-selection-end] | Present when the year is the end of the selection. |
[data-highlighted] | Present when the year is highlighted by the user as they select a range. |
[data-highlighted-start] | Present when the year is the start of the range that is highlighted by the user. |
[data-highlighted-end] | Present when the year is the end of the range that is highlighted by the user. |
[data-focused] | Present when focused |
Accessibility
Keyboard Interactions
| Key | Description |
|---|---|
| Tab | When focus moves onto the year range picker, focuses the first navigation button. |
| Space | When the focus is on either YearRangePickerNext or YearRangePickerPrev, it navigates the calendar. Otherwise, it selects the year. |
| Enter | When the focus is on either YearRangePickerNext or YearRangePickerPrev, it navigates the calendar. Otherwise, it selects the year. |
| ArrowLeft/ArrowRight/ArrowUp/ArrowDown | When the focus is on YearRangePickerCellTrigger, it navigates the years, changing the page if necessary. |
| PageUp | When the focus is on YearRangePickerCellTrigger, it navigates to the previous page of years. |
| PageDown | When the focus is on YearRangePickerCellTrigger, it navigates to the next page of years. |
| Escape | Cancels the current range selection and restores the previous valid range. |