I love the looks and UX of the Google calendar. But I wanted to have a version I could use more freely in my own projects, still looking similar to the one from Google.
On the screenshot on the homepage https://schedule-x.dev/ the events are touching the left border of the box for the day, but there's a gap between the event and the right border of the box.
Google Calendar does that as well, and I found it quite ugly. I didn't know whether they did it deliberately (maybe I'm the only one who doesn't like it? or maybe there's some functionality associated with that that I haven't worked out?)
So I can't ask the developers of Google Calendar but I can ask you :) Is there a reason for laying out the calendar that way? Or is that just the convention now that Google Calendar does that?
I see you've obviously put thought into the design so I don't think it's an oversight on your part (although it may be on Google Calendar's part).
This is a good question, which baffled me a bit too when studying the Google calendar.
Took me a while to figure it out, but when I first built the month grid without subtracting a few pixels per event, it got a bit hard to optically make sense of where an event ended and a new one started. This especially since events are also allowed to stretch over multiple days.
Subtracting a few pixels from each event, helps me to quicker grasp that an event ends on a given date, and isn't to be considered "one" with an event in the next day.
On Google Calendar clicking on an event you see the information of that event, clicking on a white space let's you create a new event at that time.
Having some white space that pertains to the day, but not to an event, let's you create a new event at the same time of an existing one without having to modify the start hour.
I think it's to easily see if the event spans only one day or multiple days.
Multi-days events don't have the gap on the right so it shows a continuous line. You need the gap to have a difference for single-day events.
That makes sense, but they could instead have put an equal gap on the left and right side, as opposed to having all the gap on the right side, making the item look like it's not centered within the box?
It's to allow the user to click on that time to make another event manually. If the user has an event at some time and the event goes all the way to the right, there is nowhere to click to add the new event. With the gap, the user can click there and have the add event modal show up.
This is a good point. The Google calendar actually has a gap in the week view too. I just didn't understand why. Do you think it would look better if the gap exists in the week view as well?
not the op, but one reason might be to visually distinguish between two events at the same time on adjacent days versus a 2-day event. Without the gap, events blend together. I don’t think the border provides enough contrast to rely on either.
Very cool! Having written a couple of web calendars myself, I found that most of the hair-tearing logical complexity was due to recurring events. I didn't see any reference to recurrence when I skimmed the docs or the demo.
Have you decided how (or if) you're going to handle recurring events? Because that has huge implications for how otherwise "simple" changes to events behave, both from whatever backend you use as well as the UI.
I intend to implement event recurrence, but this is definitely one of those things which will require changes across most packages of the project code. The details of how it will be implemented is still open :) Will surely look to other open source calendars for inspiration. Do you have any tips on solutions/APIs you like?
> this is definitely one of those things which will require changes across most packages of the project code
While iterative development is the best way, there's something to be said for staring at a whiteboard full of the most common calendaring use cases, and being sure before starting the basics that the data model and code factoring "plan" support foreseeable uses, particularly "must have" ones.
This gets lost sight of in most Agile™ teams, maybe even more so than with solo-dev.
One way to do this in a reasonably light and OSS-friendly way is to pre-write the essential features README for the tool (event recurrence is an essential calendaring feature), then go through and mark them as TODO or blank checkboxes or etc., letting people contribute.
If you've architected the code structure a bit towards those things, the contributions / PRs are easier too, as the third party contributor isn't trying to refactor out from under everyone.
"Remind" calendar has been a daily driver for me for the better part of a decade, you might find some inspiration there? I could see these two things working well together?
Long answer incoming, recurring events plagued me for more time than I'd care to admit. I'm perfectly happy to clarify anything I've expressed poorly.
a) I had full control of the back-end, so my solution was ultimately fully custom -- I don't have any APIs to point to, sorry.
b) Look at the iCalendar specification if you haven't already if for no other reason than to see what kinds of crazy corner cases they were expecting to support.
c) Generally speaking, making recurring series of events ('recurrence sets' or 'rsets') is "easy", but altering rsets will make you want to kick a small animal. I'm just going to throw out a couple of examples to indicate how annoying this can be.
Assuming each event is in a rset.
1) I move an event on a week calendar from Tuesday to Thursday. Is there an event in the rset in the preceding week that should now be visible on my calendar?
2) I move an event in a series recurring on T/R from T->R. Is the set now R/Sa or is it still T/Th?
3) Do I permit changes to a single event while still allowing it to be a member of its recurrence set (iCalendar allows this!)? More to the point, do subsequent changes of that rset affect the event that you've now changed?
4) I move event from a T/R rset to a Friday. Does it even exist anymore? It's not on a Thursday or Tuesday after all (not joking!).
And there are so many "gotchas" of this variety.
Granted, a lot of this complexity will be the job of the backend, for which you may or may not be responsible. But you still need to affirmatively decide if you're going to ask the user if they want to make a ("this"/"this and following"/"all") type of edit on certain actions and what an action maps to in terms of the API you're using. And if you ever want to implement your own backend, you're locked into that decision and you might hate yourself later.
As for my personal solution (again, I was writing the backend):
Most calendar implementations of recurring events (if the iCalendar specification is followed) comprise a "base" event and a recurrence rule from which the other events may be calculated. I personally settled on a much more rudimentary solution where each event was initially calculated via a recurrence rule but was stored a first-class entity in my database. I also prohibited changing the recurrence rule of for a set of events that was already created. This made it so that the recurrence rule actually had no semantic meaning besides at creation. The individual events were linked in a "group" and things like drag and drop operations were done just by computing the time delta and shifting each individual event by that amount. I NEVER regretted making my calendar "dumber" and almost always regretted making it more clever.
A last word of warning -- if you do not know precisely the "specification" you want to achieve in terms of what actions are permitted on rsets, you are 100% going to rewrite it multiple times (ask me how I know :p).
Woo I have a question! I have attempted something similar but my biggest issue was fixing the layout of overlapping events. Online solutions attempt to divide the column width by the number of overlapping events. But then another issue is the "tolerance" of overlapping events - Apple Calendar, for example, only begins overlapping events if they're > 5min of each other.
Basically what I do is iterate over a list of events, sorted by start time, and for each:
1) figure out if it has an overlap with its next
2) if no, all good -> do nothing. If yes:
3) recursively check upcoming events until none is found with an overlap.
4) for each event in the recursion, set an integer property for how many previous overlapping events there were
5) Use this integer property for adjusting the position of the event with CSS
Why do you have months on separate pages that scroll left to right, instead of a continuous vertical scrolling view?
It's so weird, I don't know why almost all calendars have this bonkers design. We don't paginate web documents anymore. Why calendars?
Google actually got this right at one point with their Android app (the open source one), but then they broke it again with the newer closed source app!
There is a similar but established library already: https://github.com/nhn/tui.calendar. Did you take a look at that one before starting your own? If yes, what were you missing?
I've looked into this one a bit too. The one main thing I'm missing there, is the ability to extend it with custom views. One thing Schedule-X does, which isn't documented yet, is to allow the implementer to only use the calendar header, and then implement the "view" part completely on their own. In that case Schedule-X just provides a control center type of core, with a bunch of utility APIs; basically a framework/DIY calendar.
Also I wanted to lean more towards the Material Design 3 specification.
This is significant. I have been pondering about how to build upon a gcal-style week view but with no sense of month navigation. Very similar to Continuous Calendar https://madebyevan.com/calendar
I appreciate that you can click on the month+year to navigate between years much more quickly than using the previous/next month arrows, but it's a bit non-obvious. There should be a hover effect when the cursor is over the month+year, e.g. a gray rounded rectangle, just like a gray circle appears when you're hovering over the arrows or a date. This would make it clearer that this is clickable.
On the screenshot on the homepage https://schedule-x.dev/ the events are touching the left border of the box for the day, but there's a gap between the event and the right border of the box.
Google Calendar does that as well, and I found it quite ugly. I didn't know whether they did it deliberately (maybe I'm the only one who doesn't like it? or maybe there's some functionality associated with that that I haven't worked out?)
So I can't ask the developers of Google Calendar but I can ask you :) Is there a reason for laying out the calendar that way? Or is that just the convention now that Google Calendar does that?
I see you've obviously put thought into the design so I don't think it's an oversight on your part (although it may be on Google Calendar's part).
Took me a while to figure it out, but when I first built the month grid without subtracting a few pixels per event, it got a bit hard to optically make sense of where an event ended and a new one started. This especially since events are also allowed to stretch over multiple days.
Subtracting a few pixels from each event, helps me to quicker grasp that an event ends on a given date, and isn't to be considered "one" with an event in the next day.
0: https://schedule-x.dev/demos/calendar
Having some white space that pertains to the day, but not to an event, let's you create a new event at the same time of an existing one without having to modify the start hour.
At least that's how I use it.
Have you decided how (or if) you're going to handle recurring events? Because that has huge implications for how otherwise "simple" changes to events behave, both from whatever backend you use as well as the UI.
I intend to implement event recurrence, but this is definitely one of those things which will require changes across most packages of the project code. The details of how it will be implemented is still open :) Will surely look to other open source calendars for inspiration. Do you have any tips on solutions/APIs you like?
While iterative development is the best way, there's something to be said for staring at a whiteboard full of the most common calendaring use cases, and being sure before starting the basics that the data model and code factoring "plan" support foreseeable uses, particularly "must have" ones.
This gets lost sight of in most Agile™ teams, maybe even more so than with solo-dev.
One way to do this in a reasonably light and OSS-friendly way is to pre-write the essential features README for the tool (event recurrence is an essential calendaring feature), then go through and mark them as TODO or blank checkboxes or etc., letting people contribute.
If you've architected the code structure a bit towards those things, the contributions / PRs are easier too, as the third party contributor isn't trying to refactor out from under everyone.
https://dianne.skoll.ca/projects/remind/
a) I had full control of the back-end, so my solution was ultimately fully custom -- I don't have any APIs to point to, sorry.
b) Look at the iCalendar specification if you haven't already if for no other reason than to see what kinds of crazy corner cases they were expecting to support.
c) Generally speaking, making recurring series of events ('recurrence sets' or 'rsets') is "easy", but altering rsets will make you want to kick a small animal. I'm just going to throw out a couple of examples to indicate how annoying this can be.
Assuming each event is in a rset. 1) I move an event on a week calendar from Tuesday to Thursday. Is there an event in the rset in the preceding week that should now be visible on my calendar? 2) I move an event in a series recurring on T/R from T->R. Is the set now R/Sa or is it still T/Th? 3) Do I permit changes to a single event while still allowing it to be a member of its recurrence set (iCalendar allows this!)? More to the point, do subsequent changes of that rset affect the event that you've now changed? 4) I move event from a T/R rset to a Friday. Does it even exist anymore? It's not on a Thursday or Tuesday after all (not joking!). And there are so many "gotchas" of this variety.
Granted, a lot of this complexity will be the job of the backend, for which you may or may not be responsible. But you still need to affirmatively decide if you're going to ask the user if they want to make a ("this"/"this and following"/"all") type of edit on certain actions and what an action maps to in terms of the API you're using. And if you ever want to implement your own backend, you're locked into that decision and you might hate yourself later.
As for my personal solution (again, I was writing the backend): Most calendar implementations of recurring events (if the iCalendar specification is followed) comprise a "base" event and a recurrence rule from which the other events may be calculated. I personally settled on a much more rudimentary solution where each event was initially calculated via a recurrence rule but was stored a first-class entity in my database. I also prohibited changing the recurrence rule of for a set of events that was already created. This made it so that the recurrence rule actually had no semantic meaning besides at creation. The individual events were linked in a "group" and things like drag and drop operations were done just by computing the time delta and shifting each individual event by that amount. I NEVER regretted making my calendar "dumber" and almost always regretted making it more clever.
A last word of warning -- if you do not know precisely the "specification" you want to achieve in terms of what actions are permitted on rsets, you are 100% going to rewrite it multiple times (ask me how I know :p).
How were you able to overcome this challenge?
Solving overlapping events was definitely one of the things were a couple of days went into finding a nice and performant solution. My solution is here: https://github.com/schedule-x/schedule-x/blob/main/packages/...
Basically what I do is iterate over a list of events, sorted by start time, and for each: 1) figure out if it has an overlap with its next 2) if no, all good -> do nothing. If yes: 3) recursively check upcoming events until none is found with an overlap. 4) for each event in the recursion, set an integer property for how many previous overlapping events there were 5) Use this integer property for adjusting the position of the event with CSS
I know you're following Google Calendar but I think Hey has some great ideas here :)
It's so weird, I don't know why almost all calendars have this bonkers design. We don't paginate web documents anymore. Why calendars?
Google actually got this right at one point with their Android app (the open source one), but then they broke it again with the newer closed source app!
But when I think about it, it absolutely makes sense. I'll definitely consider if this should be the default behavior of the month grid in v2.
Thanks a lot for your input!
Also I wanted to lean more towards the Material Design 3 specification.
Small suggestion for the date picker:
I appreciate that you can click on the month+year to navigate between years much more quickly than using the previous/next month arrows, but it's a bit non-obvious. There should be a hover effect when the cursor is over the month+year, e.g. a gray rounded rectangle, just like a gray circle appears when you're hovering over the arrows or a date. This would make it clearer that this is clickable.