bjork-ui
componentprimitive-date-pickersource: ok · hit
Date Picker
Date Picker is a production-ready ui component from BJORK UI, packaged for shadcn registries.
npx shadcn add "https://ui.isaiahbjork.com/primitive-date-picker.json"free
Where it goes
calendar
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · mediumui-primitives@radix-ui/react-popover@radix-ui/react-slot
animationframer-motion
iconslucide-react
Files8 · source cached 10/15/2026, 4:38:58 AM
components/bjork-ui/primitives/date-picker.tsx
"use client";
import * as React from "react";
import { format } from "date-fns";
import { CalendarIcon } from "lucide-react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { BjorkButton } from "./button";
import { BjorkCalendar } from "./calendar";
import { bjorkMenu } from "./_shared";
export function BjorkDatePicker() {
const [date, setDate] = React.useState<Date | undefined>(new Date(2026, 4, 15));
return (
<Popover>
<PopoverTrigger asChild>
<BjorkButton className="w-[230px] justify-start">
<CalendarIcon aria-hidden="true" />
{date ? format(date, "MMM d, yyyy") : "Pick a date"}
</BjorkButton>
</PopoverTrigger>
<PopoverContent
className={cn(bjorkMenu, "w-auto border-0 bg-transparent p-0 shadow-none")}
align="center"
>
<BjorkCalendar mode="single" selected={date} onSelect={setDate} initialFocus />
</PopoverContent>
</Popover>
);
}
components/ui/popover.tsx
"use client"
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
function Popover({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
return <PopoverPrimitive.Root data-slot="popover" {...props} />
}
function PopoverTrigger({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
}
function PopoverContent({
className,
align = "center",
sideOffset = 4,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
data-slot="popover-content"
align={align}
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
className
)}
{...props}
/>
</PopoverPrimitive.Portal>
)
}
function PopoverAnchor({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
}
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
lib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
components/bjork-ui/primitives/button.tsx
"use client";
import * as React from "react";
import { motion, useReducedMotion, type HTMLMotionProps } from "framer-motion";
import { cva, type VariantProps } from "class-variance-authority";
import { useWebHaptics } from "web-haptics/react";
import type { HapticInput, TriggerOptions } from "web-haptics";
import { cn } from "@/lib/utils";
const bjorkButtonVariants = cva(
"inline-flex shrink-0 transform-gpu items-center justify-center gap-2 whitespace-nowrap rounded-[13px] text-sm font-medium tracking-[-0.01em] outline-none backdrop-blur-md transition-[background-color,border-color,color,box-shadow,opacity] duration-150 ease-out focus-visible:ring-2 focus-visible:ring-[color:var(--bjork-accent)]/45 focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--bjork-ring-offset)] disabled:pointer-events-none disabled:opacity-35 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"border border-transparent bg-[var(--bjork-field)] text-[color:var(--bjork-text)] shadow-[var(--bjork-shadow-surface)] hover:bg-[var(--bjork-surface-hover)]",
secondary:
"border border-[color:var(--bjork-border)] bg-[var(--bjork-surface-active)] text-[color:var(--bjork-text)] shadow-[var(--bjork-shadow-soft)] hover:bg-[var(--bjork-surface-hover)]",
outline:
"border border-[color:var(--bjork-border)] bg-[var(--bjork-field-inset)] text-[color:var(--bjork-text-strong)] shadow-[var(--bjork-shadow-soft)] hover:border-[color:var(--bjork-border-strong)] hover:bg-[var(--bjork-surface)] hover:text-[color:var(--bjork-text)]",
ghost:
"border border-transparent bg-transparent text-[color:var(--bjork-text-muted)] hover:bg-[var(--bjork-surface-hover)] hover:text-[color:var(--bjork-text)]",
quiet:
"border border-transparent bg-[var(--bjork-field-muted)] text-[color:var(--bjork-text-soft)] hover:bg-[var(--bjork-surface-hover)] hover:text-[color:var(--bjork-text-medium)]",
accent:
"bjork-layered-button-accent tracking-tight text-white",
raised:
"bjork-layered-button-raised tracking-tight",
},
size: {
sm: "h-8 px-3 text-xs has-[>svg:first-child]:pl-2.5 has-[>svg:last-child]:pr-2.5",
md: "h-[45px] px-4 has-[>svg:first-child]:pl-3.5 has-[>svg:last-child]:pr-3.5",
lg: "h-12 px-5 text-[15px] has-[>svg:first-child]:pl-4.5 has-[>svg:last-child]:pr-4.5",
icon: "size-[45px] p-0",
},
},
defaultVariants: {
variant: "default",
size: "md",
},
},
);
export interface BjorkButtonProps
extends Omit<HTMLMotionProps<"button">, "ref" | "size">,
VariantProps<typeof bjorkButtonVariants> {
haptics?: boolean;
hapticPattern?: HapticInput;
hapticOptions?: TriggerOptions;
}
export function BjorkButton({
className,
variant,
size,
disabled,
haptics = true,
hapticPattern = "light",
hapticOptions,
onPointerDown,
tabIndex,
...props
}: BjorkButtonProps) {
const shouldReduceMotion = useReducedMotion();
const { trigger } = useWebHaptics();
const handlePointerDown = React.useCallback(
(event: React.PointerEvent<HTMLButtonElement>) => {
if (!disabled && haptics) {
void trigger(hapticPattern, hapticOptions);
}
onPointerDown?.(event);
},
[disabled, hapticOptions, hapticPattern, haptics, onPointerDown, trigger],
);
return (
<motion.button
data-slot="bjork-button"
className={cn(bjorkButtonVariants({ variant, size, className }))}
disabled={disabled}
onPointerDown={handlePointerDown}
whileHover={disabled || shouldReduceMotion ? undefined : { scale: 1.05 }}
whileTap={disabled || shouldReduceMotion ? undefined : { scale: 0.95 }}
transition={{ type: "spring", stiffness: 400, damping: 25 }}
tabIndex={tabIndex ?? 0}
{...props}
/>
);
}
export { bjorkButtonVariants };
components/bjork-ui/primitives/calendar.tsx
"use client";
import * as React from "react";
import { Calendar } from "@/components/ui/calendar";
import { cn } from "@/lib/utils";
type BjorkCalendarVariant = "outline" | "base";
type BjorkCalendarProps = React.ComponentProps<typeof Calendar> & {
variant?: BjorkCalendarVariant;
};
const calendarVariants: Record<BjorkCalendarVariant, string> = {
outline:
"border-[color:var(--bjork-border)] bg-[var(--bjork-bg)] shadow-[var(--bjork-shadow-soft)]",
base:
"border-[color:var(--bjork-border-muted)] bg-[var(--bjork-surface)] shadow-[var(--bjork-shadow-surface)]",
};
export function BjorkCalendar({
className,
classNames,
variant = "base",
...props
}: BjorkCalendarProps) {
return (
<Calendar
className={cn(
"rounded-[18px] border p-3 text-[color:var(--bjork-text)]",
calendarVariants[variant],
className,
)}
classNames={{
caption_label: "text-sm font-medium text-[color:var(--bjork-text-strong)]",
head_cell: "w-8 rounded-md text-[0.72rem] font-normal text-[color:var(--bjork-text-soft)]",
cell: "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:rounded-[10px]",
day: "size-8 rounded-[10px] p-0 text-sm font-normal text-[color:var(--bjork-text-medium)] hover:bg-[var(--bjork-accent-soft)] hover:text-[color:var(--bjork-text)] focus:bg-[var(--bjork-accent-soft)]",
day_selected:
"bg-[var(--bjork-accent-fill)] text-[color:var(--bjork-accent-foreground)] hover:bg-[var(--bjork-accent-fill-hover)] hover:text-[color:var(--bjork-accent-foreground)] focus:bg-[var(--bjork-accent-fill-hover)] focus:text-[color:var(--bjork-accent-foreground)]",
day_today: "bg-[var(--bjork-surface-active)] text-[color:var(--bjork-text)]",
day_outside: "text-[color:var(--bjork-text-faint)] opacity-40",
day_disabled: "text-[color:var(--bjork-text-faint)] opacity-70",
nav_button:
"inline-flex size-8 items-center justify-center rounded-full border-0 bg-transparent p-0 text-[color:var(--bjork-text-muted)] opacity-100 shadow-none hover:bg-transparent hover:text-[color:var(--bjork-text)] focus:bg-transparent",
nav_button_previous: "absolute left-0",
nav_button_next: "absolute right-0",
...classNames,
}}
{...props}
/>
);
}
components/ui/calendar.tsx
"use client"
import * as React from "react"
import { AnimatePresence, motion, useReducedMotion } from "framer-motion"
import { ChevronLeft, ChevronRight } from "lucide-react"
import {
CaptionLabel,
DayContent,
DayPicker,
type CaptionLabelProps,
type DayContentProps,
} from "react-day-picker"
import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
function Calendar({
className,
classNames,
components,
month,
defaultMonth,
onMonthChange,
showOutsideDays = true,
...props
}: React.ComponentProps<typeof DayPicker>) {
const shouldReduceMotion = useReducedMotion()
const [visibleMonth, setVisibleMonth] = React.useState(
() => month ?? defaultMonth ?? new Date()
)
React.useEffect(() => {
if (month) {
setVisibleMonth(month)
}
}, [month])
const handleMonthChange = React.useCallback(
(nextMonth: Date) => {
if (!month) {
setVisibleMonth(nextMonth)
}
onMonthChange?.(nextMonth)
},
[month, onMonthChange]
)
const transition = React.useMemo(
() =>
shouldReduceMotion
? { duration: 0 }
: { type: "spring" as const, stiffness: 360, damping: 34, mass: 0.7 },
[shouldReduceMotion]
)
const contentTransition = React.useMemo(
() =>
shouldReduceMotion
? { duration: 0 }
: { type: "spring" as const, stiffness: 420, damping: 30, mass: 0.55 },
[shouldReduceMotion]
)
const CaptionLabelComponent = components?.CaptionLabel ?? CaptionLabel
const DayContentComponent = components?.DayContent ?? DayContent
const AnimatedCaptionLabel = React.useCallback(
(captionProps: CaptionLabelProps) => (
<div className="relative inline-grid min-w-24 place-items-center overflow-hidden">
<AnimatePresence mode="popLayout" initial={false}>
<motion.span
key={`${captionProps.displayMonth.getFullYear()}-${captionProps.displayMonth.getMonth()}`}
initial={
shouldReduceMotion
? false
: { opacity: 0, y: 5, filter: "blur(5px)" }
}
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
exit={
shouldReduceMotion
? { opacity: 0 }
: { opacity: 0, y: -5, filter: "blur(5px)" }
}
transition={contentTransition}
className="col-start-1 row-start-1 transform-gpu"
>
<CaptionLabelComponent {...captionProps} />
</motion.span>
</AnimatePresence>
</div>
),
[CaptionLabelComponent, contentTransition, shouldReduceMotion]
)
const AnimatedDayContent = React.useCallback(
(dayContentProps: DayContentProps) => (
<span className="relative inline-grid size-full place-items-center overflow-hidden">
<AnimatePresence mode="popLayout" initial={false}>
<motion.span
key={dayContentProps.date.getTime()}
initial={
shouldReduceMotion
? false
: { opacity: 0, y: 6, scale: 0.96, filter: "blur(5px)" }
}
animate={{ opacity: 1, y: 0, scale: 1, filter: "blur(0px)" }}
exit={
shouldReduceMotion
? { opacity: 0 }
: { opacity: 0, y: -6, scale: 0.96, filter: "blur(5px)" }
}
transition={contentTransition}
className="col-start-1 row-start-1 transform-gpu"
>
<DayContentComponent {...dayContentProps} />
</motion.span>
</AnimatePresence>
</span>
),
[DayContentComponent, contentTransition, shouldReduceMotion]
)
return (
<motion.div
layout
transition={transition}
className="origin-top overflow-visible"
>
<DayPicker
month={visibleMonth}
defaultMonth={defaultMonth}
onMonthChange={handleMonthChange}
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row gap-2",
month: "flex flex-col gap-4",
caption: "flex justify-center pt-1 relative items-center w-full",
caption_label: "text-sm font-medium",
nav: "flex items-center gap-1",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-x-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md"
),
day: cn(
buttonVariants({ variant: "ghost" }),
"size-8 p-0 font-normal aria-selected:opacity-100"
),
day_range_start:
"day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
day_range_end:
"day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground opacity-35 aria-selected:text-muted-foreground",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
...components,
CaptionLabel: AnimatedCaptionLabel,
DayContent: AnimatedDayContent,
IconLeft: ({ className, ...props }) => (
<ChevronLeft className={cn("size-4", className)} {...props} />
),
IconRight: ({ className, ...props }) => (
<ChevronRight className={cn("size-4", className)} {...props} />
),
}}
{...props}
/>
</motion.div>
)
}
export { Calendar }
components/ui/button.tsx
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
destructive:
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
outline:
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
ghost:
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2 has-[>svg:first-child]:pl-3.5 has-[>svg:last-child]:pr-3.5",
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg:first-child]:pl-2.5 has-[>svg:last-child]:pr-2.5",
lg: "h-10 rounded-md px-6 has-[>svg:first-child]:pl-5 has-[>svg:last-child]:pr-5",
icon: "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function Button({
className,
variant,
size,
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
export { Button, buttonVariants }
components/bjork-ui/primitives/_shared.ts
export const bjorkSurface =
"border border-[color:var(--bjork-border)] bg-[var(--bjork-field)] text-[color:var(--bjork-text)] shadow-[var(--bjork-shadow-surface)]";
export const bjorkSoftSurface =
"border border-[color:var(--bjork-border)] bg-[var(--bjork-surface-muted)] text-[color:var(--bjork-text)] shadow-[var(--bjork-shadow-soft)]";
export const bjorkPanel =
"rounded-[18px] border border-[color:var(--bjork-border)] bg-[var(--bjork-panel)] shadow-[var(--bjork-shadow-panel)]";
export const bjorkMenu =
"z-50 rounded-[16px] border border-[color:var(--bjork-border)] bg-[var(--bjork-menu)] p-1 text-[color:var(--bjork-text)] shadow-[var(--bjork-shadow-menu)] backdrop-blur-xl";
export const bjorkMenuItem =
"rounded-[12px] px-3 py-2 text-sm text-[color:var(--bjork-text-medium)] outline-none transition hover:bg-[var(--bjork-surface-hover)] hover:text-[color:var(--bjork-text)] focus:bg-[var(--bjork-surface-hover)] focus:text-[color:var(--bjork-text)] data-[highlighted]:bg-[var(--bjork-surface-hover)] data-[highlighted]:text-[color:var(--bjork-text)] data-[selected=true]:!bg-[var(--bjork-surface-hover)] data-[selected=true]:!text-[color:var(--bjork-text)] aria-selected:!bg-[var(--bjork-surface-hover)] aria-selected:!text-[color:var(--bjork-text)]";