component 27 / 28WORKING
SC Calendar Widget
- type
- Code Component
- platform
- Framer
- license
- SecretoCho Design
demo · runs with JavaScript
Form-ready calendar picker: month/year dropdowns, Sunday or Monday start, RTL, disable past dates, themed grid, hidden ISO-date input.
SC Calendar Widget is a self-contained date picker for Framer forms: a header with the selected date, month and year dropdowns, a tinted weekday row and a seven-column grid that fills the leading and trailing days from the neighbouring months. Week start, text direction, year range, past-date locking and every colour and size are controls, and a hidden input carries the ISO date to the form.
Built for the old site’s booking flow together with SC Time Slot Selector.
- Booking
- UI
- Data
Main
Overview
- Built With
- React (hooks), inline SVG icons, CSS grid, Framer Code Component API
- Layout Support
- Fills the frame (width / height any)
- Orientation
- Vertical: header, month | year row, weekday row, day grid; LTR or RTL
- Ideal Use Case
- Booking and contact forms, date fields inside Framer forms
Tags
Code comment
SC_Calendar.tsx opens with a signed note to curious developers (lines 4–9), left out of this draft until the owner confirms it.
Dependencies
- Technologies
- None (native Date; no date library)
- Notes
- Month names and weekday labels are English strings in the file.
Complexity
- Logic Level
- Medium (grid construction, two dropdowns with click-outside, past-date rules, form value)
- Runtime Cost
- Very Low (static DOM; one document mousedown listener)
- Component Size
- ~24 KB TSX (23,925 bytes, 671 lines)
- Notes
- Prints a styled console message on load (lines 664–671).
Guide
Instructions
- Drop SC Calendar Widget inside a Framer form.
- Set Data Type (date | text), Data Name (the form field name) and Required.
- Choose Week Start (Sunday | Monday), Text Direction (LTR | RTL), Min / Max Year and Disable Past Dates.
- Set the font and the three sizes (head, body, days), then the colours: fill, text, active text, accent, hover, icon, weekend, border.
- Adjust Padding, Gap Between Days (the weekday row’s column gap only; the day grid’s gap is fixed at 8px 0) and Radius.
Examples / Presets
Monday start
- weekStart
- monday
Disable past dates
- disablePast
- true (past days greyed and unclickable; past months and years hidden)
Right to left
- rtl
- rtl
The demo also carries an action “As in the booking flow” built from the owner’s instance on the old site’s earlier home page.
Mechanics
How it Works
- Grid: getCalendarGrid builds the month by hand — leading days from the previous month for the week-start offset, the month’s days, then trailing days to complete the last week; neighbouring-month days render transparent and unclickable.
- Header: shows MM/DD/YYYY of the selection with a calendar icon; the month and year cells open dropdown lists (max-height 200, scrollable) and a document mousedown listener closes them on an outside click.
- Disable past: days before today are greyed (#ccc) with a not-allowed cursor; past months of the current year and past years are hidden from the dropdowns.
- Styling: the weekday row sits on the accent at 10 % opacity; the selected day fills with the accent; weekends use weekendColor; fonts come from the Font control plus three sizes.
- Form: a hidden read-only <input type=date|text name=dataName required> holds selectedDate.toISOString().slice(0, 10) and updates on every change.
Performance & Tuning
- Static DOM of 28–42 day cells (the grid completes only the last week); nothing animates except CSS hover transitions.
- The year dropdown lists maxYear − minYear + 1 rows; keep the range tight.
Compatibility & Limits
- Evergreen browsers; native Date only, in the visitor’s local time.
- English month and weekday labels; the header date is always MM/DD/YYYY.
- The hidden value is a UTC date (toISOString): east of UTC, local midnight converts to the previous day, so the submitted date can be one day early.
- Required has no effect: a read-only input is exempt from form validation, and its value is never empty.
- Disable Past Dates can be bypassed: pick a later year, pick January, then switch back to this year; nothing rechecks the month.
- A shadows value is read but has no control; canSelectDate is unused.
- Changing the month does not clamp the selected day: a 31 in a 30-day month rolls the form value into the next month while no cell shows as selected.
Quality
Accessibility
- Days, month and year cells are clickable <div>s without button roles or keyboard handling; the hidden input is the only form control.
- Disabled days are shown by colour and cursor only.
- RTL flips the layout direction; the labels stay English.
Security & Privacy
- No network calls, no storage, no permissions, no third-party libraries.
- One document mousedown listener while mounted (removed on unmount).
- Logs a styled console message once when the module loads in a browser.
Reference
Props
| name | type | default | description |
|---|---|---|---|
dataType | enum | date | Data Type. date | text for the hidden input. |
dataName | string | Date | Data Name. The form field name. |
required | boolean | false | Required (no effect: the input is read-only and never empty). |
weekStart | enum | sunday | Week Start. Sunday | Monday. |
rtl | enum | ltr | Text Direction. LTR | RTL. |
minYear | number | the current year | Min Year 2000–2100. |
maxYear | number | the current year + 10 | Max Year 2000–2100. |
disablePast | boolean | false | Disable Past Dates. |
font | FontStyle | Framer font control | Font. Family, weight, letter spacing and line height. |
fontSizeHead | number | 15 | Font Size Head 8–36 px (header date, month, year). |
fontSizeBody | number | 13 | Font Size Body 8–36 px (dropdowns, weekday row). |
fontSizeDays | number | 13 | Font Size Days 8–36 px. |
fillColor | color | #ffffff | Fill Color (background). |
ActiveTextColor | color | #ffffff | Active Text Color (on the accent). |
textColor | color | #191919 | Text Color. |
accentColor | color | #3B82F6 | Accent Color. Weekname background row use %10 of this color. |
hoverColor | color | #f0f0f0 | Hover Color. |
iconColor | color | #000 | Icon Color. |
weekendColor | color | #999999 | Weekend Color. |
padding | string | 10px 8px | Padding (CSS string) for header, cells and grid. |
dayGap | number | 4 | Gap Between Days 2–12 px: sets only the weekday row’s column gap; the day grid’s gap is fixed at 8px 0. |
border | object | show=true, width=1, color=#d2d2d2 | Border. Show, Width 0–4, Color. |
radius | number | 8 | Radius 0–32 px (frame and day cells). |
ActiveTextColor is the only prop with a capital initial; keep the exact name when setting it from code.
Notes & Tips
- Pair it with SC Time Slot Selector in the same form: the two hidden inputs submit together.
- Set Min Year to the current year and Disable Past Dates on for booking forms.
- Keep Radius low when Border is on: day cells share the frame’s radius.
Troubleshooting / FAQ
The submitted date is one day early
The hidden value is toISOString() of local midnight, a UTC date; in zones ahead of UTC it is the previous day. Fix by formatting the local year, month and day instead.
No day looks selected after changing month
The selected day number does not exist in that month (for example 31); pick a day again.
A dropdown stays open
The outside-click listener is on document mousedown; a layer that stops propagation keeps it open.
Source
Header comment
SC_Calendar.tsx opens with a signed note to curious developers (lines 4–9), left out of this draft until the owner confirms it.
Key excerpt
The grid is built by hand: leading days from the previous month for the week-start offset, the month itself, then trailing days to complete the last week — no date library.
const getCalendarGrid = (year, month, weekStartsSunday) => { const days = [] const first = new Date(year, month, 1) const last = new Date(year, month + 1, 0) const firstIndex = first.getDay() const totalDays = last.getDate() const startOffset = weekStartsSunday ? firstIndex : firstIndex === 0 ? 6 : firstIndex - 1 for (let i = startOffset - 1; i >= 0; i--) { const date = new Date( year, month - 1, new Date(year, month, 0).getDate() - i ) days.push({ day: date.getDate(), current: false, date }) } for (let i = 1; i <= totalDays; i++) { const date = new Date(year, month, i) days.push({ day: i, current: true, date }) } const totalCells = days.length const remainder = totalCells % 7 if (remainder > 0) { for (let i = 1; i <= 7 - remainder; i++) { const date = new Date(year, month + 1, i) days.push({ day: i, current: false, date }) } } return days}Credits
Credits
© SecretoCho Design 2025 — stated in the file (line 96); the header note (lines 4–9) is signed “– SecretoCho Design”.
- license
- SecretoCho Design
SecretoCho Design 2025
Disclaimer
Provided “as is” for visual/UX purposes. Validate performance, accessibility, and licensing within your project.
Proves
Hiring a design engineer?
I’m open to senior design engineer roles: remote from Belgrade, or relocation with visa sponsorship. Write to me, or read the CV first. Client work stays selective, one project at a time.
sc@secretocho.design