diff --git a/date-picker/README.md b/date-picker/README.md
new file mode 100644
index 0000000..40f421a
--- /dev/null
+++ b/date-picker/README.md
@@ -0,0 +1,125 @@
+# DatePicker
+A fully-featured date picker component with a calendar dropdown popup.
+
+## Getting Started
+
+Install dependencies:
+```bash
+npm install
+```
+
+Share the component to your Webflow workspace:
+```bash
+npx webflow library share
+```
+
+For local development:
+```bash
+npm run dev
+```
+
+## Designer Properties
+
+| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+| ID | Id | — | HTML ID attribute for the date picker container |
+| Mode | Variant | single | Date selection mode: single date or date range |
+| Date Format | Variant | MM/DD/YYYY | Display format for selected date in input field |
+| Size | Variant | md | Size variant for the input field and calendar |
+| Label | Text | Select Date | Label text displayed above the input field |
+| Placeholder | Text | Choose a date | Placeholder text shown in empty input field |
+| Start Placeholder | Text | Start date | Placeholder for start date in range mode |
+| End Placeholder | Text | End date | Placeholder for end date in range mode |
+| Clear Button Text | Text | Clear date | Accessible label for the clear button |
+| Today Button Text | Text | Today | Text for the today quick-select button |
+| Previous Month Label | Text | Previous month | Accessible label for previous month button |
+| Next Month Label | Text | Next month | Accessible label for next month button |
+| Show Label | Visibility | — | Show or hide the label above the input |
+| Show Clear Button | Boolean | true | Show clear button to reset selection |
+| Show Today Button | Boolean | true | Show today button in calendar footer for quick selection |
+| Show Week Numbers | Boolean | false | Display week numbers in the calendar grid |
+| Highlight Today | Boolean | true | Visually highlight today's date in the calendar |
+| Is Disabled | Boolean | false | Disable the entire date picker input |
+| Is Required | Boolean | false | Mark the date picker as a required field |
+| Close On Select | Boolean | true | Automatically close calendar after date selection |
+| Min Date | Text | — | Minimum selectable date in YYYY-MM-DD format (dates before are disabled) |
+| Max Date | Text | — | Maximum selectable date in YYYY-MM-DD format (dates after are disabled) |
+| Default Date | Text | — | Default selected date in YYYY-MM-DD format |
+| Default Start Date | Text | — | Default start date for range mode in YYYY-MM-DD format |
+| Default End Date | Text | — | Default end date for range mode in YYYY-MM-DD format |
+| Disabled Days Of Week | Text | — | Comma-separated day numbers to disable (0=Sunday, 6=Saturday) |
+| First Day Of Week | Variant | sunday | First day of the week in calendar grid |
+| Month Year Format | Variant | MMMM YYYY | Format for month/year display in calendar header |
+| Helper Text | Text | — | Helper text displayed below the input field |
+| Error Text | Text | — | Error message text displayed when validation fails |
+| Show Helper Text | Visibility | — | Show or hide the helper text below input |
+| Show Error Text | Visibility | — | Show or hide the error message |
+| Name | Text | date | Form input name attribute for form submission |
+
+## Styling
+
+This component automatically adapts to your Webflow site's design system through site variables and inherited properties.
+
+### Site Variables
+
+To match your site's design system, define these CSS variables in your Webflow project settings. The component will use the fallback values shown below until you configure them.
+
+| Site Variable | What It Controls | Fallback |
+|---------------|------------------|----------|
+| --background-primary | Main background color for input and calendar | #ffffff |
+| --background-secondary | Hover states for buttons and days | #f5f5f5 |
+| --text-primary | Main text color for labels, input, and calendar | #1a1a1a |
+| --text-secondary | Muted text for weekday labels and helper text | #737373 |
+| --border-color | Input border, calendar border, and dividers | #e5e5e5 |
+| --accent-color | Selected day background, today highlight, focus states | #1a1a1a |
+| --accent-text-color | Text color on selected days | #ffffff |
+| --border-radius | Corner rounding for input, calendar, and buttons | 8px |
+
+### Inherited Properties
+
+The component inherits these CSS properties from its parent element:
+- `font-family` — Typography style
+- `color` — Text color
+- `line-height` — Text spacing
+
+## Extending in Code
+
+### Custom Date Validation
+
+Add custom validation logic by accessing the component's selected date value:
+
+```javascript
+// Listen for date selection changes
+const datePicker = document.querySelector('[data-component="date-picker"]');
+datePicker.addEventListener('change', (event) => {
+ const selectedDate = new Date(event.target.value);
+ const dayOfWeek = selectedDate.getDay();
+
+ // Example: Prevent weekend selections
+ if (dayOfWeek === 0 || dayOfWeek === 6) {
+ alert('Please select a weekday');
+ event.target.value = '';
+ }
+});
+```
+
+### Integration with Form Libraries
+
+The component works seamlessly with form validation libraries by using the `name` property:
+
+```javascript
+// Example with a form submission handler
+const form = document.querySelector('form');
+form.addEventListener('submit', (event) => {
+ event.preventDefault();
+ const formData = new FormData(form);
+ const selectedDate = formData.get('date'); // Uses the 'name' prop value
+
+ // Process the date value
+ console.log('Selected date:', selectedDate);
+});
+```
+
+## Dependencies
+
+- **react-day-picker** — Flexible date picker component library for React
\ No newline at end of file
diff --git a/date-picker/index.html b/date-picker/index.html
new file mode 100644
index 0000000..1b449da
--- /dev/null
+++ b/date-picker/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ DatePicker
+
+
+
+
+
+
+
diff --git a/date-picker/metadata.json b/date-picker/metadata.json
new file mode 100644
index 0000000..e30c7e8
--- /dev/null
+++ b/date-picker/metadata.json
@@ -0,0 +1,5 @@
+{
+ "name": "Date Picker",
+ "description": "Calendar-based date picker with range selection, min/max date constraints, and configurable date formats.",
+ "category": "Forms & Input"
+}
diff --git a/date-picker/package.json b/date-picker/package.json
new file mode 100644
index 0000000..95900ae
--- /dev/null
+++ b/date-picker/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "date-picker",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc -b && vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "react": "^19.1.1",
+ "react-dom": "^19.1.1",
+ "react-day-picker": "^9.0.0"
+ },
+ "devDependencies": {
+ "@types/react": "^19.1.13",
+ "@types/react-dom": "^19.1.9",
+ "@vitejs/plugin-react": "^5.0.3",
+ "@webflow/data-types": "^1.0.1",
+ "@webflow/react": "^1.0.1",
+ "@webflow/webflow-cli": "^1.8.44",
+ "typescript": "~5.8.3",
+ "vite": "^7.1.7"
+ }
+}
\ No newline at end of file
diff --git a/date-picker/screenshot-brand.png b/date-picker/screenshot-brand.png
new file mode 100644
index 0000000..21ab831
Binary files /dev/null and b/date-picker/screenshot-brand.png differ
diff --git a/date-picker/screenshot-dark.png b/date-picker/screenshot-dark.png
new file mode 100644
index 0000000..b967e2b
Binary files /dev/null and b/date-picker/screenshot-dark.png differ
diff --git a/date-picker/screenshot-light.png b/date-picker/screenshot-light.png
new file mode 100644
index 0000000..2b0dcb4
Binary files /dev/null and b/date-picker/screenshot-light.png differ
diff --git a/date-picker/src/components/DatePicker/DatePicker.css b/date-picker/src/components/DatePicker/DatePicker.css
new file mode 100644
index 0000000..a3701f4
--- /dev/null
+++ b/date-picker/src/components/DatePicker/DatePicker.css
@@ -0,0 +1,490 @@
+/*
+ * Webflow Site Variables Used:
+ * - --background-primary: Main background color for input and calendar
+ * - --background-secondary: Hover states for buttons and days
+ * - --text-primary: Main text color for labels, input, and calendar
+ * - --text-secondary: Muted text for weekday labels and helper text
+ * - --border-color: Input border, calendar border, and dividers
+ * - --accent-color: Selected day background, today highlight, focus states
+ * - --accent-text-color: Text color on selected days
+ * - --border-radius: Corner rounding for input, calendar, and buttons
+ */
+
+/* Box sizing reset */
+.wf-datepicker *,
+.wf-datepicker *::before,
+.wf-datepicker *::after {
+ box-sizing: border-box;
+}
+
+/* Root element - inherit Webflow typography + default padding */
+.wf-datepicker {
+ font-family: inherit;
+ color: inherit;
+ line-height: inherit;
+ padding: 24px;
+ position: relative;
+ --wf-datepicker-day-size: 36px;
+ --wf-datepicker-spacing: 4px;
+}
+
+/* Size variants */
+.wf-datepicker-size-sm {
+ --wf-datepicker-day-size: 32px;
+ --wf-datepicker-spacing: 2px;
+ font-size: 14px;
+}
+
+.wf-datepicker-size-md {
+ --wf-datepicker-day-size: 36px;
+ --wf-datepicker-spacing: 4px;
+ font-size: 16px;
+}
+
+.wf-datepicker-size-lg {
+ --wf-datepicker-day-size: 40px;
+ --wf-datepicker-spacing: 6px;
+ font-size: 18px;
+}
+
+/* Label */
+.wf-datepicker-label {
+ display: block;
+ margin-bottom: 8px;
+ font-weight: 500;
+ color: var(--text-primary, #1a1a1a);
+}
+
+.wf-datepicker-required {
+ color: #dc2626;
+ margin-left: 4px;
+}
+
+/* Input wrapper */
+.wf-datepicker-input-wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+
+/* Input field */
+.wf-datepicker-input {
+ width: 100%;
+ padding: 12px 80px 12px 16px;
+ background: var(--background-primary, #ffffff);
+ border: 1px solid var(--border-color, #e5e5e5);
+ border-radius: var(--border-radius, 8px);
+ color: var(--text-primary, #1a1a1a);
+ font-size: inherit;
+ font-family: inherit;
+ line-height: 1.5;
+ cursor: pointer;
+ transition: border-color 0.2s, box-shadow 0.2s;
+}
+
+.wf-datepicker-input::placeholder {
+ color: var(--text-secondary, #737373);
+}
+
+.wf-datepicker-input:hover:not(:disabled) {
+ border-color: var(--accent-color, #1a1a1a);
+}
+
+.wf-datepicker-input:focus {
+ outline: none;
+ border-color: var(--accent-color, #1a1a1a);
+ box-shadow: 0 0 0 3px rgba(26, 26, 26, 0.1);
+}
+
+.wf-datepicker-input:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ background: var(--background-secondary, #f5f5f5);
+}
+
+.wf-datepicker-size-sm .wf-datepicker-input {
+ padding: 8px 72px 8px 12px;
+ font-size: 14px;
+}
+
+.wf-datepicker-size-lg .wf-datepicker-input {
+ padding: 16px 88px 16px 20px;
+ font-size: 18px;
+}
+
+/* Input icons container */
+.wf-datepicker-input-icons {
+ position: absolute;
+ right: 8px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+
+/* Icon base styles */
+.wf-datepicker-icon {
+ width: 20px;
+ height: 20px;
+ color: var(--text-secondary, #737373);
+}
+
+.wf-datepicker-size-sm .wf-datepicker-icon {
+ width: 16px;
+ height: 16px;
+}
+
+.wf-datepicker-size-lg .wf-datepicker-icon {
+ width: 24px;
+ height: 24px;
+}
+
+/* Clear button */
+.wf-datepicker-clear-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 6px;
+ background: transparent;
+ border: none;
+ border-radius: var(--border-radius, 8px);
+ cursor: pointer;
+ color: var(--text-secondary, #737373);
+ transition: background-color 0.2s, color 0.2s;
+}
+
+.wf-datepicker-clear-button:hover {
+ background: var(--background-secondary, #f5f5f5);
+ color: var(--text-primary, #1a1a1a);
+}
+
+.wf-datepicker-clear-button:focus-visible {
+ outline: 2px solid var(--accent-color, #1a1a1a);
+ outline-offset: 2px;
+}
+
+/* Calendar button */
+.wf-datepicker-calendar-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 6px;
+ background: transparent;
+ border: none;
+ border-radius: var(--border-radius, 8px);
+ cursor: pointer;
+ color: var(--text-secondary, #737373);
+ transition: background-color 0.2s, color 0.2s;
+}
+
+.wf-datepicker-calendar-button:hover:not(:disabled) {
+ background: var(--background-secondary, #f5f5f5);
+ color: var(--text-primary, #1a1a1a);
+}
+
+.wf-datepicker-calendar-button:focus-visible {
+ outline: 2px solid var(--accent-color, #1a1a1a);
+ outline-offset: 2px;
+}
+
+.wf-datepicker-calendar-button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+}
+
+/* Helper text */
+.wf-datepicker-helper-text {
+ margin-top: 6px;
+ font-size: 14px;
+ color: var(--text-secondary, #737373);
+}
+
+/* Error text */
+.wf-datepicker-error-text {
+ margin-top: 6px;
+ font-size: 14px;
+ color: #dc2626;
+}
+
+/* Dropdown calendar */
+.wf-datepicker-dropdown {
+ position: absolute;
+ top: calc(100% + 8px);
+ left: 0;
+ z-index: 1000;
+ padding: 16px;
+ background: var(--background-primary, #ffffff);
+ border: 1px solid var(--border-color, #e5e5e5);
+ border-radius: var(--border-radius, 8px);
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
+ min-width: 320px;
+}
+
+.wf-datepicker-size-sm .wf-datepicker-dropdown {
+ padding: 12px;
+ min-width: 280px;
+}
+
+.wf-datepicker-size-lg .wf-datepicker-dropdown {
+ padding: 20px;
+ min-width: 360px;
+}
+
+/* Calendar header */
+.wf-datepicker-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16px;
+ gap: 12px;
+}
+
+/* Month/year display */
+.wf-datepicker-month-year {
+ font-weight: 600;
+ color: var(--text-primary, #1a1a1a);
+ font-size: 16px;
+ text-align: center;
+ flex: 1;
+}
+
+.wf-datepicker-size-sm .wf-datepicker-month-year {
+ font-size: 14px;
+}
+
+.wf-datepicker-size-lg .wf-datepicker-month-year {
+ font-size: 18px;
+}
+
+/* Navigation buttons */
+.wf-datepicker-nav-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 8px;
+ background: transparent;
+ border: none;
+ border-radius: var(--border-radius, 8px);
+ cursor: pointer;
+ color: var(--text-primary, #1a1a1a);
+ transition: background-color 0.2s;
+}
+
+.wf-datepicker-nav-button:hover {
+ background: var(--background-secondary, #f5f5f5);
+}
+
+.wf-datepicker-nav-button:focus-visible {
+ outline: 2px solid var(--accent-color, #1a1a1a);
+ outline-offset: 2px;
+}
+
+.wf-datepicker-nav-button:disabled {
+ opacity: 0.3;
+ cursor: not-allowed;
+}
+
+/* Calendar grid */
+.wf-datepicker-calendar {
+ margin-bottom: 12px;
+}
+
+/* Weekdays header */
+.wf-datepicker-weekdays {
+ display: grid;
+ grid-template-columns: repeat(7, var(--wf-datepicker-day-size));
+ gap: var(--wf-datepicker-spacing);
+ margin-bottom: 8px;
+}
+
+.wf-datepicker-weekdays.wf-datepicker-with-week-numbers {
+ grid-template-columns: 32px repeat(7, var(--wf-datepicker-day-size));
+}
+
+/* Week number header */
+.wf-datepicker-week-number-header {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--text-secondary, #737373);
+}
+
+/* Weekday labels */
+.wf-datepicker-weekday {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--text-secondary, #737373);
+ text-transform: uppercase;
+ height: 32px;
+}
+
+.wf-datepicker-size-sm .wf-datepicker-weekday {
+ font-size: 11px;
+ height: 28px;
+}
+
+.wf-datepicker-size-lg .wf-datepicker-weekday {
+ font-size: 13px;
+ height: 36px;
+}
+
+/* Days container */
+.wf-datepicker-days {
+ display: flex;
+ flex-direction: column;
+ gap: var(--wf-datepicker-spacing);
+}
+
+/* Week row */
+.wf-datepicker-week {
+ display: grid;
+ grid-template-columns: repeat(7, var(--wf-datepicker-day-size));
+ gap: var(--wf-datepicker-spacing);
+}
+
+/* Week number in row */
+.wf-datepicker-week-number {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ font-weight: 500;
+ color: var(--text-secondary, #737373);
+ width: 32px;
+}
+
+/* Day cell */
+.wf-datepicker-day {
+ width: var(--wf-datepicker-day-size);
+ height: var(--wf-datepicker-day-size);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: none;
+ background: transparent;
+ cursor: pointer;
+ border-radius: var(--border-radius, 8px);
+ color: var(--text-primary, #1a1a1a);
+ font-size: 14px;
+ font-weight: 400;
+ transition: background-color 0.2s, color 0.2s;
+ position: relative;
+}
+
+.wf-datepicker-size-sm .wf-datepicker-day {
+ font-size: 13px;
+}
+
+.wf-datepicker-size-lg .wf-datepicker-day {
+ font-size: 16px;
+}
+
+.wf-datepicker-day:hover:not(.wf-datepicker-day-disabled):not(.wf-datepicker-day-empty) {
+ background: var(--background-secondary, #f5f5f5);
+}
+
+.wf-datepicker-day:focus-visible {
+ outline: 2px solid var(--accent-color, #1a1a1a);
+ outline-offset: 2px;
+ z-index: 1;
+}
+
+/* Empty day cells */
+.wf-datepicker-day-empty {
+ cursor: default;
+ pointer-events: none;
+}
+
+/* Today */
+.wf-datepicker-day-today {
+ font-weight: 600;
+}
+
+.wf-datepicker-day-today::after {
+ content: '';
+ position: absolute;
+ bottom: 4px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 4px;
+ height: 4px;
+ border-radius: 50%;
+ background: var(--accent-color, #1a1a1a);
+}
+
+/* Selected day */
+.wf-datepicker-day-selected {
+ background: var(--accent-color, #1a1a1a);
+ color: var(--accent-text-color, #ffffff);
+ font-weight: 600;
+}
+
+.wf-datepicker-day-selected:hover {
+ background: var(--accent-color, #1a1a1a);
+ opacity: 0.9;
+}
+
+.wf-datepicker-day-selected::after {
+ background: var(--accent-text-color, #ffffff);
+}
+
+/* In range (for range mode) */
+.wf-datepicker-day-in-range {
+ background: rgba(26, 26, 26, 0.1);
+}
+
+.wf-datepicker-day-in-range:hover {
+ background: rgba(26, 26, 26, 0.15);
+}
+
+/* Disabled day */
+.wf-datepicker-day-disabled {
+ opacity: 0.3;
+ cursor: not-allowed;
+ pointer-events: none;
+}
+
+/* Footer */
+.wf-datepicker-footer {
+ padding-top: 12px;
+ border-top: 1px solid var(--border-color, #e5e5e5);
+ display: flex;
+ justify-content: center;
+}
+
+/* Today button */
+.wf-datepicker-today-button {
+ padding: 8px 16px;
+ background: transparent;
+ border: 1px solid var(--border-color, #e5e5e5);
+ border-radius: var(--border-radius, 8px);
+ cursor: pointer;
+ color: var(--text-primary, #1a1a1a);
+ font-size: 14px;
+ font-weight: 500;
+ transition: background-color 0.2s, border-color 0.2s;
+}
+
+.wf-datepicker-today-button:hover {
+ background: var(--background-secondary, #f5f5f5);
+ border-color: var(--accent-color, #1a1a1a);
+}
+
+.wf-datepicker-today-button:focus-visible {
+ outline: 2px solid var(--accent-color, #1a1a1a);
+ outline-offset: 2px;
+}
+
+.wf-datepicker-size-sm .wf-datepicker-today-button {
+ padding: 6px 12px;
+ font-size: 13px;
+}
+
+.wf-datepicker-size-lg .wf-datepicker-today-button {
+ padding: 10px 20px;
+ font-size: 16px;
+}
\ No newline at end of file
diff --git a/date-picker/src/components/DatePicker/DatePicker.tsx b/date-picker/src/components/DatePicker/DatePicker.tsx
new file mode 100644
index 0000000..29ed56c
--- /dev/null
+++ b/date-picker/src/components/DatePicker/DatePicker.tsx
@@ -0,0 +1,600 @@
+import { useState, useRef, useEffect } from "react";
+
+export interface DatePickerProps {
+ id?: string;
+ mode?: "single" | "range";
+ dateFormat?: "MM/DD/YYYY" | "DD/MM/YYYY" | "YYYY-MM-DD";
+ size?: "sm" | "md" | "lg";
+ label?: string;
+ placeholder?: string;
+ startPlaceholder?: string;
+ endPlaceholder?: string;
+ clearButtonText?: string;
+ todayButtonText?: string;
+ previousMonthLabel?: string;
+ nextMonthLabel?: string;
+ showLabel?: boolean;
+ showClearButton?: boolean;
+ showTodayButton?: boolean;
+ showWeekNumbers?: boolean;
+ highlightToday?: boolean;
+ isDisabled?: boolean;
+ isRequired?: boolean;
+ closeOnSelect?: boolean;
+ minDate?: string;
+ maxDate?: string;
+ defaultDate?: string;
+ defaultStartDate?: string;
+ defaultEndDate?: string;
+ disabledDaysOfWeek?: string;
+ firstDayOfWeek?: "sunday" | "monday";
+ monthYearFormat?: "MMMM YYYY" | "MMM YYYY" | "MM/YYYY";
+ helperText?: string;
+ errorText?: string;
+ showHelperText?: boolean;
+ showErrorText?: boolean;
+ name?: string;
+}
+
+const MONTH_NAMES = [
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+];
+
+const MONTH_NAMES_SHORT = [
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+];
+
+const DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
+const DAY_NAMES_MONDAY_FIRST = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
+
+export default function DatePicker({
+ id,
+ mode = "single",
+ dateFormat = "MM/DD/YYYY",
+ size = "md",
+ label = "Select Date",
+ placeholder = "Choose a date",
+ startPlaceholder = "Start date",
+ endPlaceholder = "End date",
+ clearButtonText = "Clear date",
+ todayButtonText = "Today",
+ previousMonthLabel = "Previous month",
+ nextMonthLabel = "Next month",
+ showLabel = true,
+ showClearButton = true,
+ showTodayButton = true,
+ showWeekNumbers = false,
+ highlightToday = true,
+ isDisabled = false,
+ isRequired = false,
+ closeOnSelect = true,
+ minDate = "",
+ maxDate = "",
+ defaultDate = "",
+ defaultStartDate = "",
+ defaultEndDate = "",
+ disabledDaysOfWeek = "",
+ firstDayOfWeek = "sunday",
+ monthYearFormat = "MMMM YYYY",
+ helperText = "",
+ errorText = "",
+ showHelperText = true,
+ showErrorText = false,
+ name = "date",
+}: DatePickerProps) {
+ const [isOpen, setIsOpen] = useState(false);
+ const [selectedDate, setSelectedDate] = useState(
+ defaultDate ? parseDate(defaultDate) : null
+ );
+ const [rangeStart, setRangeStart] = useState(
+ defaultStartDate ? parseDate(defaultStartDate) : null
+ );
+ const [rangeEnd, setRangeEnd] = useState(
+ defaultEndDate ? parseDate(defaultEndDate) : null
+ );
+ const [currentMonth, setCurrentMonth] = useState(new Date().getMonth());
+ const [currentYear, setCurrentYear] = useState(new Date().getFullYear());
+ const [focusedDay, setFocusedDay] = useState(null);
+
+ const containerRef = useRef(null);
+ const inputRef = useRef(null);
+ const calendarRef = useRef(null);
+
+ const minDateObj = minDate ? parseDate(minDate) : null;
+ const maxDateObj = maxDate ? parseDate(maxDate) : null;
+ const disabledDays = disabledDaysOfWeek
+ ? disabledDaysOfWeek.split(",").map((d) => parseInt(d.trim(), 10))
+ : [];
+
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (
+ containerRef.current &&
+ !containerRef.current.contains(event.target as Node)
+ ) {
+ setIsOpen(false);
+ }
+ };
+
+ if (isOpen) {
+ document.addEventListener("mousedown", handleClickOutside);
+ }
+
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, [isOpen]);
+
+ useEffect(() => {
+ if (isOpen && calendarRef.current && focusedDay !== null) {
+ const dayButton = calendarRef.current.querySelector(
+ `[data-day="${focusedDay}"]`
+ ) as HTMLButtonElement;
+ if (dayButton) {
+ dayButton.focus();
+ }
+ }
+ }, [focusedDay, isOpen]);
+
+ function parseDate(dateStr: string): Date | null {
+ if (!dateStr) return null;
+ const parts = dateStr.split("-");
+ if (parts.length === 3) {
+ return new Date(
+ parseInt(parts[0], 10),
+ parseInt(parts[1], 10) - 1,
+ parseInt(parts[2], 10)
+ );
+ }
+ return null;
+ }
+
+ function formatDate(date: Date | null): string {
+ if (!date) return "";
+ const day = date.getDate().toString().padStart(2, "0");
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
+ const year = date.getFullYear();
+
+ switch (dateFormat) {
+ case "DD/MM/YYYY":
+ return `${day}/${month}/${year}`;
+ case "YYYY-MM-DD":
+ return `${year}-${month}-${day}`;
+ case "MM/DD/YYYY":
+ default:
+ return `${month}/${day}/${year}`;
+ }
+ }
+
+ function getDisplayValue(): string {
+ if (mode === "range") {
+ const start = rangeStart ? formatDate(rangeStart) : "";
+ const end = rangeEnd ? formatDate(rangeEnd) : "";
+ if (start && end) return `${start} - ${end}`;
+ if (start) return start;
+ return "";
+ }
+ return selectedDate ? formatDate(selectedDate) : "";
+ }
+
+ function getDaysInMonth(month: number, year: number): number {
+ return new Date(year, month + 1, 0).getDate();
+ }
+
+ function getFirstDayOfMonth(month: number, year: number): number {
+ const day = new Date(year, month, 1).getDay();
+ return firstDayOfWeek === "monday" ? (day === 0 ? 6 : day - 1) : day;
+ }
+
+ function getWeekNumber(date: Date): number {
+ const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
+ const dayNum = d.getUTCDay() || 7;
+ d.setUTCDate(d.getUTCDate() + 4 - dayNum);
+ const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
+ return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7);
+ }
+
+ function isDateDisabled(date: Date): boolean {
+ if (minDateObj && date < minDateObj) return true;
+ if (maxDateObj && date > maxDateObj) return true;
+ if (disabledDays.includes(date.getDay())) return true;
+ return false;
+ }
+
+ function isSameDay(date1: Date | null, date2: Date | null): boolean {
+ if (!date1 || !date2) return false;
+ return (
+ date1.getDate() === date2.getDate() &&
+ date1.getMonth() === date2.getMonth() &&
+ date1.getFullYear() === date2.getFullYear()
+ );
+ }
+
+ function isInRange(date: Date): boolean {
+ if (mode !== "range" || !rangeStart || !rangeEnd) return false;
+ return date >= rangeStart && date <= rangeEnd;
+ }
+
+ function handleDayClick(day: number) {
+ const clickedDate = new Date(currentYear, currentMonth, day);
+ if (isDateDisabled(clickedDate)) return;
+
+ if (mode === "range") {
+ if (!rangeStart || (rangeStart && rangeEnd)) {
+ setRangeStart(clickedDate);
+ setRangeEnd(null);
+ } else {
+ if (clickedDate < rangeStart) {
+ setRangeEnd(rangeStart);
+ setRangeStart(clickedDate);
+ } else {
+ setRangeEnd(clickedDate);
+ }
+ if (closeOnSelect) {
+ setIsOpen(false);
+ }
+ }
+ } else {
+ setSelectedDate(clickedDate);
+ if (closeOnSelect) {
+ setIsOpen(false);
+ }
+ }
+ }
+
+ function handleTodayClick() {
+ const today = new Date();
+ setCurrentMonth(today.getMonth());
+ setCurrentYear(today.getFullYear());
+ if (mode === "range") {
+ setRangeStart(today);
+ setRangeEnd(null);
+ } else {
+ setSelectedDate(today);
+ if (closeOnSelect) {
+ setIsOpen(false);
+ }
+ }
+ }
+
+ function handleClear() {
+ if (mode === "range") {
+ setRangeStart(null);
+ setRangeEnd(null);
+ } else {
+ setSelectedDate(null);
+ }
+ }
+
+ function handlePreviousMonth() {
+ if (currentMonth === 0) {
+ setCurrentMonth(11);
+ setCurrentYear(currentYear - 1);
+ } else {
+ setCurrentMonth(currentMonth - 1);
+ }
+ }
+
+ function handleNextMonth() {
+ if (currentMonth === 11) {
+ setCurrentMonth(0);
+ setCurrentYear(currentYear + 1);
+ } else {
+ setCurrentMonth(currentMonth + 1);
+ }
+ }
+
+ function handleKeyDown(e: React.KeyboardEvent, day: number) {
+ const daysInMonth = getDaysInMonth(currentMonth, currentYear);
+ let newDay = day;
+
+ switch (e.key) {
+ case "ArrowLeft":
+ e.preventDefault();
+ newDay = day > 1 ? day - 1 : day;
+ break;
+ case "ArrowRight":
+ e.preventDefault();
+ newDay = day < daysInMonth ? day + 1 : day;
+ break;
+ case "ArrowUp":
+ e.preventDefault();
+ newDay = day > 7 ? day - 7 : day;
+ break;
+ case "ArrowDown":
+ e.preventDefault();
+ newDay = day + 7 <= daysInMonth ? day + 7 : day;
+ break;
+ case "Enter":
+ case " ":
+ e.preventDefault();
+ handleDayClick(day);
+ return;
+ case "Escape":
+ e.preventDefault();
+ setIsOpen(false);
+ inputRef.current?.focus();
+ return;
+ default:
+ return;
+ }
+
+ setFocusedDay(newDay);
+ }
+
+ function getMonthYearDisplay(): string {
+ const monthName =
+ monthYearFormat === "MMM YYYY"
+ ? MONTH_NAMES_SHORT[currentMonth]
+ : MONTH_NAMES[currentMonth];
+ const yearStr = currentYear.toString();
+
+ switch (monthYearFormat) {
+ case "MM/YYYY":
+ return `${(currentMonth + 1).toString().padStart(2, "0")}/${yearStr}`;
+ case "MMM YYYY":
+ return `${monthName} ${yearStr}`;
+ case "MMMM YYYY":
+ default:
+ return `${monthName} ${yearStr}`;
+ }
+ }
+
+ function renderCalendar() {
+ const daysInMonth = getDaysInMonth(currentMonth, currentYear);
+ const firstDay = getFirstDayOfMonth(currentMonth, currentYear);
+ const today = new Date();
+ const days: JSX.Element[] = [];
+ const weeks: JSX.Element[][] = [];
+ let currentWeek: JSX.Element[] = [];
+
+ for (let i = 0; i < firstDay; i++) {
+ currentWeek.push(
+
+ );
+ }
+
+ for (let day = 1; day <= daysInMonth; day++) {
+ const date = new Date(currentYear, currentMonth, day);
+ const isToday = highlightToday && isSameDay(date, today);
+ const isSelected =
+ mode === "range"
+ ? isSameDay(date, rangeStart) || isSameDay(date, rangeEnd)
+ : isSameDay(date, selectedDate);
+ const inRange = mode === "range" && isInRange(date);
+ const disabled = isDateDisabled(date);
+
+ const dayClasses = [
+ "wf-datepicker-day",
+ isToday && "wf-datepicker-day-today",
+ isSelected && "wf-datepicker-day-selected",
+ inRange && "wf-datepicker-day-in-range",
+ disabled && "wf-datepicker-day-disabled",
+ ]
+ .filter(Boolean)
+ .join(" ");
+
+ currentWeek.push(
+ handleDayClick(day)}
+ onKeyDown={(e) => handleKeyDown(e, day)}
+ disabled={disabled}
+ data-day={day}
+ aria-label={`${MONTH_NAMES[currentMonth]} ${day}, ${currentYear}`}
+ aria-selected={isSelected}
+ >
+ {day}
+
+ );
+
+ if ((firstDay + day) % 7 === 0 || day === daysInMonth) {
+ while (currentWeek.length < 7) {
+ currentWeek.push(
+
+ );
+ }
+ weeks.push([...currentWeek]);
+ currentWeek = [];
+ }
+ }
+
+ return weeks.map((week, weekIndex) => {
+ const firstDayOfWeek = new Date(currentYear, currentMonth, weekIndex * 7 - firstDay + 1);
+ const weekNumber = getWeekNumber(firstDayOfWeek);
+
+ return (
+
+ {showWeekNumbers && (
+
{weekNumber}
+ )}
+ {week}
+
+ );
+ });
+ }
+
+ const dayNames = firstDayOfWeek === "monday" ? DAY_NAMES_MONDAY_FIRST : DAY_NAMES;
+ const sizeClass = `wf-datepicker-size-${size}`;
+ const displayValue = getDisplayValue();
+ const showClear = showClearButton && displayValue && !isDisabled;
+
+ return (
+
+ {showLabel && (
+
+ {label}
+ {isRequired && * }
+
+ )}
+
+
+
!isDisabled && setIsOpen(!isOpen)}
+ onFocus={() => !isDisabled && setIsOpen(true)}
+ readOnly
+ disabled={isDisabled}
+ required={isRequired}
+ aria-expanded={isOpen}
+ aria-haspopup="dialog"
+ />
+
+
+ {showClear && (
+
+
+
+
+
+ )}
+
+
!isDisabled && setIsOpen(!isOpen)}
+ aria-label="Open calendar"
+ disabled={isDisabled}
+ tabIndex={-1}
+ >
+
+
+
+
+
+
+
+ {showHelperText && helperText && (
+
{helperText}
+ )}
+
+ {showErrorText && errorText && (
+
{errorText}
+ )}
+
+ {isOpen && (
+
+
+
+
+
+
+
+
+
{getMonthYearDisplay()}
+
+
+
+
+
+
+
+
+
+
+ {showWeekNumbers &&
Wk
}
+ {dayNames.map((day) => (
+
+ {day}
+
+ ))}
+
+
+
{renderCalendar()}
+
+
+ {showTodayButton && (
+
+
+ {todayButtonText}
+
+
+ )}
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/date-picker/src/components/DatePicker/DatePicker.webflow.tsx b/date-picker/src/components/DatePicker/DatePicker.webflow.tsx
new file mode 100644
index 0000000..678e0a3
--- /dev/null
+++ b/date-picker/src/components/DatePicker/DatePicker.webflow.tsx
@@ -0,0 +1,215 @@
+import DatePicker from "./DatePicker";
+import { props } from "@webflow/data-types";
+import { declareComponent } from "@webflow/react";
+import "./DatePicker.css";
+
+export default declareComponent(DatePicker, {
+ name: "DatePicker",
+ description: "A fully-featured date picker component with a calendar dropdown popup. Clicking the input field opens a calendar showing the current month in a grid layout with day names. Users can navigate between months using previous/next arrow buttons and select dates by clicking. The selected date displays in the input field in a configurable format. Supports single date selection or date range mode where users select start and end dates with visual highlighting of the range. Today's date is highlighted with a distinct visual style. Dates outside configurable min/max boundaries are disabled and grayed out. The input includes a calendar icon and a clear button to reset the selection. Fully keyboard navigable for accessibility with arrow keys for date navigation and Enter to select.",
+ group: "Forms",
+ options: {
+ ssr: false,
+ applyTagSelectors: true
+ },
+ props: {
+ id: props.Id({
+ name: "Element ID",
+ group: "Settings",
+ tooltip: "HTML ID attribute for the date picker container"
+ }),
+ mode: props.Variant({
+ name: "Selection Mode",
+ options: ["single", "range"],
+ defaultValue: "single",
+ group: "Behavior",
+ tooltip: "Date selection mode: single date or date range"
+ }),
+ dateFormat: props.Variant({
+ name: "Date Format",
+ options: ["MM/DD/YYYY", "DD/MM/YYYY", "YYYY-MM-DD"],
+ defaultValue: "MM/DD/YYYY",
+ group: "Style",
+ tooltip: "Display format for selected date in input field"
+ }),
+ size: props.Variant({
+ name: "Size",
+ options: ["sm", "md", "lg"],
+ defaultValue: "md",
+ group: "Style",
+ tooltip: "Size variant for the input field and calendar"
+ }),
+ label: props.Text({
+ name: "Label",
+ defaultValue: "Select Date",
+ group: "Content",
+ tooltip: "Label text displayed above the input field"
+ }),
+ placeholder: props.Text({
+ name: "Placeholder",
+ defaultValue: "Choose a date",
+ group: "Content",
+ tooltip: "Placeholder text shown in empty input field"
+ }),
+ startPlaceholder: props.Text({
+ name: "Start Placeholder",
+ defaultValue: "Start date",
+ group: "Content",
+ tooltip: "Placeholder for start date in range mode"
+ }),
+ endPlaceholder: props.Text({
+ name: "End Placeholder",
+ defaultValue: "End date",
+ group: "Content",
+ tooltip: "Placeholder for end date in range mode"
+ }),
+ clearButtonText: props.Text({
+ name: "Clear Button Text",
+ defaultValue: "Clear date",
+ group: "Content",
+ tooltip: "Accessible label for the clear button"
+ }),
+ todayButtonText: props.Text({
+ name: "Today Button Text",
+ defaultValue: "Today",
+ group: "Content",
+ tooltip: "Text for the today quick-select button"
+ }),
+ previousMonthLabel: props.Text({
+ name: "Previous Month Label",
+ defaultValue: "Previous month",
+ group: "Content",
+ tooltip: "Accessible label for previous month button"
+ }),
+ nextMonthLabel: props.Text({
+ name: "Next Month Label",
+ defaultValue: "Next month",
+ group: "Content",
+ tooltip: "Accessible label for next month button"
+ }),
+ showLabel: props.Visibility({
+ name: "Show Label",
+ group: "Display",
+ tooltip: "Show or hide the label above the input"
+ }),
+ showClearButton: props.Boolean({
+ name: "Show Clear Button",
+ defaultValue: true,
+ group: "Display",
+ tooltip: "Show clear button to reset selection"
+ }),
+ showTodayButton: props.Boolean({
+ name: "Show Today Button",
+ defaultValue: true,
+ group: "Display",
+ tooltip: "Show today button in calendar footer for quick selection"
+ }),
+ showWeekNumbers: props.Boolean({
+ name: "Show Week Numbers",
+ defaultValue: false,
+ group: "Display",
+ tooltip: "Display week numbers in the calendar grid"
+ }),
+ highlightToday: props.Boolean({
+ name: "Highlight Today",
+ defaultValue: true,
+ group: "Display",
+ tooltip: "Visually highlight today's date in the calendar"
+ }),
+ isDisabled: props.Boolean({
+ name: "Disabled",
+ defaultValue: false,
+ group: "Behavior",
+ tooltip: "Disable the entire date picker input"
+ }),
+ isRequired: props.Boolean({
+ name: "Required",
+ defaultValue: false,
+ group: "Behavior",
+ tooltip: "Mark the date picker as a required field"
+ }),
+ closeOnSelect: props.Boolean({
+ name: "Close On Select",
+ defaultValue: true,
+ group: "Behavior",
+ tooltip: "Automatically close calendar after date selection"
+ }),
+ minDate: props.Text({
+ name: "Minimum Date",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Minimum selectable date in YYYY-MM-DD format (dates before are disabled)"
+ }),
+ maxDate: props.Text({
+ name: "Maximum Date",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Maximum selectable date in YYYY-MM-DD format (dates after are disabled)"
+ }),
+ defaultDate: props.Text({
+ name: "Default Date",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Default selected date in YYYY-MM-DD format"
+ }),
+ defaultStartDate: props.Text({
+ name: "Default Start Date",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Default start date for range mode in YYYY-MM-DD format"
+ }),
+ defaultEndDate: props.Text({
+ name: "Default End Date",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Default end date for range mode in YYYY-MM-DD format"
+ }),
+ disabledDaysOfWeek: props.Text({
+ name: "Disabled Days of Week",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Comma-separated day numbers to disable (0=Sunday, 6=Saturday)"
+ }),
+ firstDayOfWeek: props.Variant({
+ name: "First Day of Week",
+ options: ["sunday", "monday"],
+ defaultValue: "sunday",
+ group: "Style",
+ tooltip: "First day of the week in calendar grid"
+ }),
+ monthYearFormat: props.Variant({
+ name: "Month Year Format",
+ options: ["MMMM YYYY", "MMM YYYY", "MM/YYYY"],
+ defaultValue: "MMMM YYYY",
+ group: "Style",
+ tooltip: "Format for month/year display in calendar header"
+ }),
+ helperText: props.Text({
+ name: "Helper Text",
+ defaultValue: "",
+ group: "Content",
+ tooltip: "Helper text displayed below the input field"
+ }),
+ errorText: props.Text({
+ name: "Error Text",
+ defaultValue: "",
+ group: "Content",
+ tooltip: "Error message text displayed when validation fails"
+ }),
+ showHelperText: props.Visibility({
+ name: "Show Helper Text",
+ group: "Display",
+ tooltip: "Show or hide the helper text below input"
+ }),
+ showErrorText: props.Visibility({
+ name: "Show Error Text",
+ group: "Display",
+ tooltip: "Show or hide the error message"
+ }),
+ name: props.Text({
+ name: "Input Name",
+ defaultValue: "date",
+ group: "Settings",
+ tooltip: "Form input name attribute for form submission"
+ })
+ }
+});
\ No newline at end of file
diff --git a/date-picker/src/components/DatePicker/DatePickerSimple.webflow.tsx b/date-picker/src/components/DatePicker/DatePickerSimple.webflow.tsx
new file mode 100644
index 0000000..89fb860
--- /dev/null
+++ b/date-picker/src/components/DatePicker/DatePickerSimple.webflow.tsx
@@ -0,0 +1,96 @@
+import DatePicker from "./DatePicker";
+import { props } from "@webflow/data-types";
+import { declareComponent } from "@webflow/react";
+import "./DatePicker.css";
+
+export default declareComponent(DatePicker, {
+ name: "DatePicker (Simple)",
+ description: "A fully-featured date picker component with a calendar dropdown popup. Clicking the input field opens a calendar showing the current month in a grid layout with day names. Users can navigate between months using previous/next arrow buttons and select dates by clicking. The selected date displays in the input field in a configurable format. Supports single date selection or date range mode where users select start and end dates with visual highlighting of the range. Today's date is highlighted with a distinct visual style. Dates outside configurable min/max boundaries are disabled and grayed out. The input includes a calendar icon and a clear button to reset the selection. Fully keyboard navigable for accessibility with arrow keys for date navigation and Enter to select.",
+ group: "Forms",
+ options: {
+ ssr: false,
+ applyTagSelectors: true
+ },
+ props: {
+ id: props.Id({
+ name: "Element ID",
+ group: "Settings",
+ tooltip: "HTML ID attribute for the date picker container"
+ }),
+ label: props.Text({
+ name: "Label",
+ defaultValue: "Select Date",
+ group: "Content",
+ tooltip: "Label text displayed above the input field"
+ }),
+ placeholder: props.Text({
+ name: "Placeholder",
+ defaultValue: "Choose a date",
+ group: "Content",
+ tooltip: "Placeholder text shown in empty input field"
+ }),
+ showLabel: props.Visibility({
+ name: "Show Label",
+ group: "Display",
+ tooltip: "Show or hide the label above the input"
+ }),
+ isDisabled: props.Boolean({
+ name: "Disabled",
+ defaultValue: false,
+ group: "Behavior",
+ tooltip: "Disable the entire date picker input"
+ }),
+ isRequired: props.Boolean({
+ name: "Required",
+ defaultValue: false,
+ group: "Behavior",
+ tooltip: "Mark the date picker as a required field"
+ }),
+ minDate: props.Text({
+ name: "Minimum Date",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Minimum selectable date in YYYY-MM-DD format (dates before are disabled)"
+ }),
+ maxDate: props.Text({
+ name: "Maximum Date",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Maximum selectable date in YYYY-MM-DD format (dates after are disabled)"
+ }),
+ defaultDate: props.Text({
+ name: "Default Date",
+ defaultValue: "",
+ group: "Date Constraints",
+ tooltip: "Default selected date in YYYY-MM-DD format"
+ }),
+ helperText: props.Text({
+ name: "Helper Text",
+ defaultValue: "",
+ group: "Content",
+ tooltip: "Helper text displayed below the input field"
+ }),
+ errorText: props.Text({
+ name: "Error Text",
+ defaultValue: "",
+ group: "Content",
+ tooltip: "Error message text displayed when validation fails"
+ }),
+ showHelperText: props.Visibility({
+ name: "Show Helper Text",
+ group: "Display",
+ tooltip: "Show or hide the helper text below input"
+ }),
+ showErrorText: props.Visibility({
+ name: "Show Error Text",
+ group: "Display",
+ tooltip: "Show or hide the error message"
+ }),
+ name: props.Text({
+ name: "Input Name",
+ defaultValue: "date",
+ group: "Settings",
+ tooltip: "Form input name attribute for form submission"
+ })
+ }
+});
\ No newline at end of file
diff --git a/date-picker/src/main.tsx b/date-picker/src/main.tsx
new file mode 100644
index 0000000..6d5074e
--- /dev/null
+++ b/date-picker/src/main.tsx
@@ -0,0 +1,370 @@
+import { StrictMode, useState } from "react"
+import { createRoot } from "react-dom/client"
+import DatePicker from "./components/DatePicker/DatePicker"
+import "./components/DatePicker/DatePicker.css"
+
+type ThemeVars = {
+ "--background-primary": string
+ "--background-secondary": string
+ "--text-primary": string
+ "--text-secondary": string
+ "--border-color": string
+ "--accent-color": string
+ "--accent-text-color": string
+ "--border-radius": string
+}
+
+const themes: Record = {
+ light: {
+ "--background-primary": "#ffffff",
+ "--background-secondary": "#f5f5f5",
+ "--text-primary": "#1a1a1a",
+ "--text-secondary": "#737373",
+ "--border-color": "#e5e5e5",
+ "--accent-color": "#2563eb",
+ "--accent-text-color": "#ffffff",
+ "--border-radius": "8px",
+ },
+ dark: {
+ "--background-primary": "#0a0a0a",
+ "--background-secondary": "#1a1a1a",
+ "--text-primary": "#fafafa",
+ "--text-secondary": "#a3a3a3",
+ "--border-color": "#2a2a2a",
+ "--accent-color": "#3b82f6",
+ "--accent-text-color": "#ffffff",
+ "--border-radius": "8px",
+ },
+ brand: {
+ "--background-primary": "#fef7f0",
+ "--background-secondary": "#fde8d0",
+ "--text-primary": "#1c1917",
+ "--text-secondary": "#78716c",
+ "--border-color": "#e7e5e4",
+ "--accent-color": "#ea580c",
+ "--accent-text-color": "#ffffff",
+ "--border-radius": "12px",
+ },
+}
+
+function App() {
+ const [activeTheme, setActiveTheme] = useState<"light" | "dark" | "brand" | "custom">("light")
+ const [customVars, setCustomVars] = useState(themes.light)
+
+ const currentVars = activeTheme === "custom" ? customVars : themes[activeTheme]
+
+ const handleThemeChange = (theme: "light" | "dark" | "brand" | "custom") => {
+ setActiveTheme(theme)
+ if (theme !== "custom") {
+ setCustomVars(themes[theme])
+ }
+ }
+
+ const handleCustomVarChange = (key: keyof ThemeVars, value: string) => {
+ setCustomVars((prev) => ({ ...prev, [key]: value }))
+ }
+
+ const pageBackground = activeTheme === "dark" ? "#000000" : activeTheme === "brand" ? "#fef2e8" : "#f9fafb"
+
+ return (
+
+
+
+
+ Theme Preview
+
+
+
+ {(["light", "dark", "brand", "custom"] as const).map((theme) => (
+ handleThemeChange(theme)}
+ style={{
+ padding: "8px 16px",
+ border: `2px solid ${activeTheme === theme ? currentVars["--accent-color"] : currentVars["--border-color"]}`,
+ borderRadius: currentVars["--border-radius"],
+ background: activeTheme === theme ? currentVars["--accent-color"] : "transparent",
+ color: activeTheme === theme ? currentVars["--accent-text-color"] : currentVars["--text-primary"],
+ cursor: "pointer",
+ fontSize: "14px",
+ fontWeight: "500",
+ textTransform: "capitalize",
+ transition: "all 0.2s ease"
+ }}
+ >
+ {theme}
+
+ ))}
+
+
+ {activeTheme === "custom" && (
+
+ )}
+
+
+
+
+ DatePicker Component Preview
+
+
+
+
+ Default Configuration
+
+
+
+
+
+
+ Date Range Mode
+
+
+
+
+
+
+ With Date Constraints
+
+
+
+
+
+
+ Compact Size with Error State
+
+
+
+
+
+
+ )
+}
+
+createRoot(document.getElementById("root")!).render(
+
+
+
+)
\ No newline at end of file
diff --git a/date-picker/src/vite-env.d.ts b/date-picker/src/vite-env.d.ts
new file mode 100644
index 0000000..151aa68
--- /dev/null
+++ b/date-picker/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
\ No newline at end of file
diff --git a/date-picker/tsconfig.app.json b/date-picker/tsconfig.app.json
new file mode 100644
index 0000000..d775f2a
--- /dev/null
+++ b/date-picker/tsconfig.app.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsBuildInfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": [
+ "ES2022",
+ "DOM",
+ "DOM.Iterable"
+ ],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": [
+ "src"
+ ]
+}
\ No newline at end of file
diff --git a/date-picker/tsconfig.json b/date-picker/tsconfig.json
new file mode 100644
index 0000000..65f670c
--- /dev/null
+++ b/date-picker/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.app.json"
+ },
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/date-picker/tsconfig.node.json b/date-picker/tsconfig.node.json
new file mode 100644
index 0000000..c4a9a48
--- /dev/null
+++ b/date-picker/tsconfig.node.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsBuildInfo",
+ "target": "ES2023",
+ "lib": [
+ "ES2023"
+ ],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "noEmit": true,
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": [
+ "vite.config.ts"
+ ]
+}
\ No newline at end of file
diff --git a/date-picker/vite.config.ts b/date-picker/vite.config.ts
new file mode 100644
index 0000000..c7a4f78
--- /dev/null
+++ b/date-picker/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+
+export default defineConfig({
+ plugins: [react()],
+});
\ No newline at end of file
diff --git a/date-picker/webflow.json b/date-picker/webflow.json
new file mode 100644
index 0000000..e17ee1a
--- /dev/null
+++ b/date-picker/webflow.json
@@ -0,0 +1,10 @@
+{
+ "library": {
+ "name": "DatePicker",
+ "components": [
+ "./src/**/*.webflow.@(js|jsx|mjs|ts|tsx)"
+ ],
+ "description": "A fully-featured date picker component with a calendar dropdown popup. Clicking the input field opens a calendar showing the current month in a grid layout with day names. Users can navigate between months using previous/next arrow buttons and select dates by clicking. The selected date displays in the input field in a configurable format. Supports single date selection or date range mode where users select start and end dates with visual highlighting of the range. Today's date is highlighted with a distinct visual style. Dates outside configurable min/max boundaries are disabled and grayed out. The input includes a calendar icon and a clear button to reset the selection. Fully keyboard navigable for accessibility with arrow keys for date navigation and Enter to select.",
+ "id": "date-picker"
+ }
+}
\ No newline at end of file