react-hig-datepicker
v1.3.1
Published
An accessible date picker React component styled with the Autodesk HIG design language
Maintainers
Readme
react-hig-datepicker
An accessible React date picker styled with the Autodesk HIG design language.
- Built on react-datepicker 9
- Written in TypeScript; declarations are generated from source and ship with the package
- Works with React 18 and 19, in both ESM and CommonJS, and renders on the server
- No runtime dependency on the deprecated
@hig/*packages - Zero known vulnerabilities in runtime dependencies
Install
npm install react-hig-datepickerReact and React DOM are peer dependencies:
npm install react react-domUsage
import { useState } from "react";
import DatePicker from "react-hig-datepicker";
import "react-hig-datepicker/styles.css";
export default function Example() {
const [date, setDate] = useState(null);
return (
<DatePicker
label="Select a date"
onChange={setDate}
selected={date}
showClearButton
/>
);
}The stylesheet is a separate import so the package stays safe to load in
server and Node environments. react-hig-datepicker/lib/datePicker.css and
react-hig-datepicker/css/datePicker.css resolve to the same file.
What it looks like
| | |
| --- | --- |
| Empty — the floating label sits on the input until a date is chosen.<DatePicker label="Select a date" /> | |
| With a value — the label floats up and the clear button appears.showClearButton | |
| Keyboard focus — a visible ring, in addition to the blue underline.WCAG 2.4.7 / 2.4.11 | |
| Helper text — linked to the input with aria-describedby.showInstruction + instruction | |
| Disabled — dashed underline, muted label and icon.disabled | |
| Hover — days highlight in HIG blue; today stays bold blue.Squared cells, as in the HIG spec | |
| Open calendar — squared day cells, today in bold blue, the selection filled.Opens on click or focus | |
Screenshots are generated by npm run screenshots and the animation above by
npm run demo:gif. Both freeze the browser clock, so the images stay identical
between runs instead of drifting with the current month.
Styling from Sass
The Sass sources ship with the package, so you can override HIG tokens before the stylesheet is generated:
@use "react-hig-datepicker/src/styles/datePicker";Props
Every react-datepicker prop is forwarded, with the exceptions noted under Reserved below. The additions are:
| Prop | Type | Default | Description |
| ------------------ | ----------------------- | -------------- | -------------------------------------------------------------- |
| label | string | — | Floating label text. |
| showLabel | boolean | true | Render the label. |
| instruction | string | — | Helper text below the field, linked via aria-describedby. |
| showInstruction | boolean | false | Render the helper text. |
| errors | ReactNode | — | Validation message; also sets aria-invalid and error styling. |
| placeholder | string | — | Input placeholder. |
| showIcon | boolean | true | Show the HIG calendar icon. |
| showClearButton | boolean | false | Show a clear button once a date is selected. |
| clearButtonTitle | string | "Clear date" | Accessible name and tooltip for the clear button. |
| closeOnSelect | boolean | true | Close the calendar after a day is picked. |
| focused | boolean | — | Controlled override for the field's focused styling. |
| disabled | boolean | false | Disable the field. |
| selected | Date \| null | — | The selected date. |
| onChange | (date, event) => void | — | Called with a Date, or null when cleared. |
| id | string | auto | Input id. A unique one is generated when omitted. |
selected, minDate, maxDate, startDate, endDate, excludeDates,
includeDates and highlightDates all accept a Date, a date string, a
timestamp, or any object with a toDate() method (moment, dayjs).
Reserved. customInput, isClearable, popperModifiers,
popperPlacement and withPortal are controlled by this component to keep the
HIG presentation intact.
TypeScript declarations are generated from the source and bundled; no @types
package is needed. Range and multi-date selection are not supported, so
selectsRange and selectsMultiple are rejected at compile time.
Accessibility
The component is verified against WCAG 2.1 AA on every run, with axe in jsdom
(unit tests) and in a real browser (npm run test:e2e, which also checks
colour contrast).
- The field is a focusable, read-only combobox — typing is disabled, but it is reachable by Tab and opens with Enter/↓.
- Labels and helper text are programmatically associated with the input, and each instance gets a unique id.
- All interactive elements have a visible
:focus-visibleindicator. - The palette meets AA contrast minimums, and the calendar has a
forced-colors(high-contrast) treatment. - The label transition is disabled under
prefers-reduced-motion.
Behaviour
INVARIANTS.md specifies what the component is expected to
do — value coercion, calendar interaction, the clear button, labelling and
accessibility — with each statement tagged by the test that enforces it.
Development
npm install
npm start # demo at http://localhost:5173
npm test # unit tests (Vitest + Testing Library)
npm run test:e2e # browser tests (Playwright + axe)
npm run lint
npm run typecheck # type-checks the whole project
npm run build # -> es/, lib/, css/
npm run screenshots # regenerate docs/images/ used by this README
npm run demo:gif # re-record docs/images/demo.gif (needs ffmpeg)Upgrading from 0.1.x
0.1.x could not be installed alongside React 17+ — its @hig/* dependencies
were peer-locked to React 15/16 — so 1.0 replaces that chain. Migration is
usually two changes.
1. Dates are native Date objects, not moment
react-datepicker 9 dropped moment. onChange now hands you a Date:
-onChange={(date) => setDate(date)} // date was a moment
-<span>{date.format("YYYY-MM-DD")}</span>
+onChange={(date) => setDate(date)} // date is a Date, or null
+<span>{date?.toISOString().slice(0, 10)}</span>Values you pass in still accept moment objects, so selected={moment()}
keeps working — only values you receive changed.
Date format strings are now date-fns
patterns rather than moment ones. Most notably YYYY becomes yyyy and DD
becomes dd. locale now takes a date-fns locale object rather than a string.
2. Import the stylesheet explicitly
0.1.x pulled in @hig/text-field's CSS as a side effect of importing the
component, and the lib/datePicker.css the old README pointed at was never
actually published. All styles now live in one file that you import yourself:
import "react-hig-datepicker/styles.css";Also worth knowing
- Fixed: the input was previously rendered permanently
disabled, which made it unreachable by keyboard and — via HIG's--disabledrule — meantshowClearButtoncould never display anything. Both now work. - Fixed: every instance used the hardcoded id
hig__date-picker, so multiple pickers on a page produced duplicate ids and broken label associations. Ids are now unique per instance. - Fixed: the stylesheet contained unscoped
input { … !important }andbutton { … }rules that restyled unrelated elements in the host page. - Changed: several colours were darkened to reach AA contrast. The blues
and teal keep their hue; only luminance moved. Tokens live in
src/styles/_tokens.scss. - Removed:
propTypes. React 19 ignores them at runtime; TypeScript declarations replace them.
Sponsor
This component is in low-maintenance mode — it works, but it is not under active development. If it is load-bearing for you and you would like that to change, sponsorship is the signal that gets it back on the list.
License
MIT © Ziwei Wu
