The availability calendar on the property submission form did not look like a calendar. What showed up was a single run of text, SuMoTuWeThFrSa123..., weekday names and date numbers glued together with no spacing. That was the first defect, and it lived in the CSS layer.
The second defect had nothing to do with it and was only closed in the release after. On that same form, clicking a date cell did not toggle the date, and clicking a month-nav arrow did not change the month. Both of them submitted the whole form.
Two calendars, only one with CSS
The product has two calendars. One sits on the dashboard tab, the other on the property submission form. They read like twin components, but their class prefixes differ. The dashboard one uses .avail-calendar__* and has a full stylesheet behind it. The form one uses .cal__*, and classes with that prefix never had any CSS at all.
The result is exactly what the screen showed. Every element was present in the DOM, but with no grid and no sizing they collapsed into an ordinary text flow. Weekday names ran into the next weekday name, date numbers ran into the next number. The calendar was not broken, it had simply never been given a shape. That state had held since a far earlier release, long before the release that finally added type="button".
The fix added a .cal__* CSS block mirroring the dashboard calendar: a seven-column grid, day buttons, and states for blocked dates as well as past ones. Roughly this skeleton.
.cal__grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
}
.cal__day {
/* day button */
}
.cal__day--blocked,
.cal__day--past {
/* blocked dates and dates already in the past */
}The same fix swapped the weekday and month names, which had been hardcoded in English, for values read from the app's i18n object through appData.i18n.months and appData.i18n.weekdaysShort. After that the calendar follows the site's language instead of whatever language happened to be typed into the code.
Every click submits the form
The second defect lived in the button markup. The day cells and the month-nav arrows were <button> elements, and not one of them carried a type attribute.
<form action="/submit-listing" method="post">
<button data-calendar-day="2026-05-17">17</button>
</form>Inside a <form>, a <button> with no type defaults to type="submit". Not neutral, not button, but submit. So a control meant only to block one date was in fact a submit button, and the browser treated it as exactly that. The same went for the month arrows, which do not even touch any data.
Nothing here is syntactically wrong. A <button> with no type is valid HTML, so no validator complains. The bite is entirely in the default value, and the symptom is not an error message but a form that posts at a moment that makes no sense.
The fix is one attribute
The fix added type="button" to the calendar day buttons and the month-nav buttons. The notes for this case keep one snippet of the markup together with an explanatory comment.
<!-- button in a form with no type defaults to submit -->
<button type="button" data-calendar-day="2026-05-17">17</button>That is where the fix stops. No new logic, no handler rewritten, just one attribute turning a submit control back into an ordinary button.
What changed afterwards
With the attribute in place, clicking a day toggles the blocked state and the cell turns red, the state persists through the availability/toggle endpoint answering 200, and no form is submitted. The month nav works the way it should.
Verification ran directly on production using three different account roles: owner, guest, and admin.
A shape that repeats across this project
What made me pause was not the bug itself but how often a similar shape turned up in the same project.
The photo upload component once shipped with no CSS whatsoever. Its dropzone did not look clickable and the thumbnails rendered at full size, and that was a large part of the complaint that the feature simply would not work.
The amenity checkbox cards were the same story, no CSS at all. The complaint sounded like a logic problem, as if the multi-select were broken. The multi-select had worked from the start, because these were checkboxes. The real gap was cards that had never been styled.
The reverse case also exists, and that one is quieter. The symptom came in through a client report, the root cause was mine to chase. On the property detail page, below 1024px the booking widget is display:none and was meant to be replaced by a sticky booking bar. The CSS for that bar existed. Its markup was never in the template. So mobile and tablet had no Reserve button at all, and visitors could not book from there.
One caution, so that every report does not get blamed on missing CSS. The client reported that photo upload could not be clicked after re-uploading, and it turned out to be browser cache rather than a code error. The proof was simple: in an Incognito window the feature worked.
What I took away
- Inside a
<form>, a<button>with notypeis a submit button. If the control only performs an on-screen action, writetype="button"explicitly. - The symptom is not an error, it is a form posting at an implausible moment. When a small click inside a form triggers a submit, suspect the
typeattribute before chasing the JavaScript handler. - A widget rendering as run-on text is usually not broken markup but classes that never had CSS. Check the class prefix before blaming the template.
- Two components that look like twins can use different class prefixes, and only one of them may ever have been styled.
- Complaints that sound like logic problems are often presentation problems. The amenities multi-select worked from day one, what was missing was the card CSS.
- The inverse happens too and slips through more easily: complete CSS for markup that never renders, until one breakpoint loses its primary button.
- Before chasing code, make sure what the reporter sees is not a stale copy from cache. One report on this project was answered by opening it in Incognito.