npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@glook/date-primitives

v0.2.0

Published

Headless date/calendar primitives over react-aria hooks. Gregorian-only by default, locale-agnostic.

Readme

@glook/date-primitives

Headless date and calendar primitives built on react-aria hooks (@react-aria/datepicker, @react-aria/calendar).

No styles, no strings, no popup. You get structure, ARIA wiring and data-* state attributes; the markup and the appearance stay yours.

Live demo — a Storybook sandbox whose skin is attached entirely through the data-* attributes documented below.

npm install @glook/date-primitives

Peer deps: react ^17.0.2 || ^18 || ^19. Every major is covered by the CI test matrix.

Usage

import {Calendar, DateField} from '@glook/date-primitives';

<DateField.Root aria-label="Meeting date" className="field">
    <DateField.Segments>
        {(segment) => <DateField.Segment segment={segment} className="segment" />}
    </DateField.Segments>
</DateField.Root>;

<Calendar.Root aria-label="Event date" className="calendar">
    <Calendar.NavButton slot="previous">‹</Calendar.NavButton>
    <Calendar.Heading />
    <Calendar.NavButton slot="next">›</Calendar.NavButton>
    <Calendar.Grid renderCell={(date) => <Calendar.Cell date={date} className="cell" />} />
</Calendar.Root>;

RangeCalendar.Root swaps single-date selection for a range and reuses the very same parts — Calendar.Heading, Calendar.NavButton, Calendar.Grid, Calendar.Cell all read the context the root provides.

Roots

| Root | Selection | Renders DOM | | --- | --- | --- | | DateField.Root | one date, segmented input | yes | | Calendar.Root | one date, month grid | yes | | RangeCalendar.Root | a range, month grid | yes | | DatePicker.Root | one date, field + calendar | no | | DateRangePicker.Root | a range, two fields + calendar | no |

The two picker roots render nothing at all. They own the state and hand it back through context, so the group element, the trigger and the overlay are entirely yours:

import {Calendar, DateField, DatePicker, useDatePickerContext} from '@glook/date-primitives';
import {useButton} from '@react-aria/button';

const Body = () => {
    const {picker, state, groupRef} = useDatePickerContext();
    const triggerRef = React.useRef<HTMLButtonElement>(null);
    const {buttonProps} = useButton(picker.buttonProps, triggerRef);

    return (
        <div>
            {/* groupRef is required: the segment focus manager is built on this element */}
            <div {...picker.groupProps} ref={groupRef}>
                <DateField.Root {...picker.fieldProps}>
                    <DateField.Segments>
                        {(segment) => <DateField.Segment segment={segment} />}
                    </DateField.Segments>
                </DateField.Root>
                <button {...buttonProps} ref={triggerRef} type="button">▾</button>
            </div>
            {state.isOpen && (
                <div {...picker.dialogProps}>
                    <Calendar.Root {...picker.calendarProps}>{/* … */}</Calendar.Root>
                </div>
            )}
        </div>
    );
};

<DatePicker.Root aria-label="Delivery date"><Body /></DatePicker.Root>;

Bring your own positioning — floating-ui, Base UI, or a plain absolutely positioned div. The Storybook stories use the last one.

Validation and fill state

Validation already lives on the state react-stately hands you — isInvalid, plus a native ValidityState and localized messages:

const {state} = useDateFieldContext();
const {rangeUnderflow, rangeOverflow} = state.realtimeValidation.validationDetails;

What it does not tell you is whether the user has finished typing. state.value is not a substitute: in a controlled field the engine may hand back a best guess with the missing part defaulted while the segment is still a placeholder on screen. Hence one helper:

import {getDateFieldFillState, useDateFieldContext} from '@glook/date-primitives';

const {state} = useDateFieldContext();
const {isComplete, isEmpty} = getDateFieldFillState(state.segments);

isComplete means every editable segment is filled — treating the year as filled only at full width, since a half-typed year is still a valid number. Era-based calendars, where the year is relative and genuinely short, are exempt. Non-editable segments (literals, time zone) are ignored.

Dates in, dates out

The package speaks DateValue from @internationalized/date. Converting to and from a native Date is two one-liners, and whether "no date" is null or undefined stays your call:

import {fromDate, getLocalTimeZone, toCalendarDate} from '@internationalized/date';

const toCalendar = (date: Date) => toCalendarDate(fromDate(date, getLocalTimeZone()));
const toNative = (value: CalendarDate) => value.toDate(getLocalTimeZone());

Calendar systems

Every root takes a createCalendar prop, defaulting to a factory that knows Gregorian and nothing else. That default is what keeps the bundle small: pulling in the full @internationalized/date calendar registry would drag every calendar system into every consumer. Need Hebrew or Japanese? Pass your own factory — its weight lands in your bundle, not in the package.

Locale

The package never hardcodes a locale. It reads whatever I18nProvider from @react-aria/i18n provides, re-exported here for convenience:

import {I18nProvider} from '@glook/date-primitives';

<I18nProvider locale="en-US">{/* … */}</I18nProvider>;

Trim the aria string dictionaries you do not ship with @react-aria/optimize-locales-plugin.

Styling

There is no CSS. Hook onto the attributes:

| Element | Attributes | | --- | --- | | DateField.Segment | data-segment (the segment type), data-placeholder | | Calendar.Cell | data-selected, data-selection-start, data-selection-end, data-disabled, data-unavailable, data-outside-month | | hover / focus | native :hover, :focus, :focus-visible — nothing is emulated |

Disabled state is native, not data-*. Segments expose aria-disabled="true" (set by useSpinButton) and nav buttons expose the native :disabled (they are real <button> elements). Neither emits data-disabled — a skin ported from react-aria-components selectors will silently lose its disabled styling.

.cell[data-selected] { background: #dbeafe; }
.segment[data-placeholder] { color: #9aa5b1; }
.segment[aria-disabled='true'] { color: #cbd2d9; }
.nav:disabled { opacity: 0.4; }

Development

npm run storybook   # component catalogue, the only way to run things by hand
npm run build       # dist/index.{js,cjs,d.ts}
npm run typecheck
npm test

License

MIT © Andrey Polyakov