cho.sh
2,244 notes
Liked on XI made a mistake. When Jack and I decided to organize this $100K Hackathon, my biggest concern was cheating. Unlike a regular hackathon, everyone participates from home. And because the winner is whoever makes the most PROFIT (not revenue), there are so many ways to manipulate the numbers. So I added a $99 entry fee. One thing I’ve learned from building businesses is that people tend to be more committed when they pay for something. I thought it would filter out spam, attract serious builders, and make the competition fairer for everyone. But I completely missed the other side. 3 years ago, I was a living with my apps. Today, I’m financially free. My life changed so much that somewhere along the way, I lost perspective on what $99 means to someone else. For someone in India, someone struggling to pay their bills, or someone trying to make their first dollar online, $99 can be a lot of money. And those are exactly the people I want to help! I’ve spent years sharing what I’ve learned, reviewing random people’s landing pages, and organizing free meetups around the world. Not because there’s money to make, but because I genuinely love seeing people build things and succeed. And somehow, I was so focused on keeping cheaters out that I didn’t realize I was also keeping genuine builders out. That’s my mistake. So I’ve decided to make the $100K Hackathon completely FREE :) Everyone who already paid got a full refund. I’m still worried about cheating. That problem hasn’t disappeared, and Jack and I will have to figure out better ways to verify profits and keep the leaderboard fair. But that’s our problem to solve. Thank you to everyone who shared their thoughts. Sometimes you need people to remind you of things you’ve forgotten. I genuinely hope this hackathon changes someone’s life. ❤️Liked on XIt’s time to bring back Cafe CursorPosted on Xyo @pendev i can't live share edit a .pen file? it's the most needed featurePosted on XI'm coining a word "Agentsplaining" Agents condescendingly assuming the user don't know fuck and repeating the obviousReplied on X@jayhxmo @raycast Hard to explain. It's the small detailsPosted on X@raycast iOS keyboard is insanely good. Might resub Raycast pro just cuz of thisReplied on X@ishuagra02 I saw a theory that F5.2 is a post train of 5.1, but O5.5 is a distillation of F5.5(unreleased new pretrain) and thats why O5.5 > F5.2Liked on X@anaclumos don't know what is actually improved 👀Posted on Xam i the only one underimpressed with 5.2 fableReplied on X@bvvst great minds think alike https://t.co/YRFWhb1mcOLiked on XClaude Fable 5.2 is out and it's the perfect time to run this god-level prompt on your codebase: "I want you to audit our codebase, is anything here unnecessary, overly complicated in our current setup, or based on weak assumptions? Challenge them. - What can be deleted entirely? - What can be simplified now that unnecessary pieces are gone? Then make the changes. Prefer deleting over simplifying, simplifying over optimizing, and optimizing over automating. This product is being used by thousands of customers, do not cut any corners." This prompt is one I run often and leads to the best results.Replied on X@akshayadinesh19 @bot can we please BYOM for botsLiked on XTo be fair, the Chinese models have a much lower refusal rateLiked on XF*CK IT, im creating a library of AI slop indicators. my feed is full of taste larpers. stay tuned https://t.co/5Muv8nQRm3Liked on XJENSEN! THATS NOT WHAT WE MEANT BY CHINESE MODELS!!!!!!!Liked on Xmy parents had to declare bankruptcy when I was a kid, so they had every reason to pressure me towards a more stable career path instead, they were so patient with me, even as i failed startup after startup for years so getting to tell my mom that everything turned out okay was a really special moment :) for those who can't read Korean, her first message roughly translates to: "Really!!! Congratulations my son. So much :') Thank you God!" i know I've been posting about my exit for like a week now, but it really is a big deal for me and my family! i'll share more about how this all happened and some tactical advice later this week, but for now i'm just riding out all the emotions to quote my mom, thank you God!!!!Liked on X@thefarmers_Dog it stands for Egg GzampleLiked on X🦍 Introducing Memorilla: memory as a modality for LLMs Memorilla turns everything a frozen LLM needs to remember into just 16 memory embeddings ⚡ It uses up to 4,212× fewer tokens and is 27× faster than full context inference while more accurate 🎯 Pushes the Pareto frontier over RAG and agentic memory in personalization, long-document and knowledge QA A thread 🧵:Liked on XThere's a huge product opportunity downstream of Tailscale etc too. At @tldraw we've started designing for "high trust networks" and it's been a beautiful thing. 1. Features that would be a nightmare on the internet are okay on my computer and mostly okay on private networks. Websites that execute user-generated scripts, control production systems, allow for users to send desktop notifications, spend tokens, or directly prompt my agent. Crazy stuff that would only make sense if everyone was in a committed polycule. 2. When everyone on the server can be trusted to have _external_ reasons for good behavior (eg keeping their job, or maintaining friends, family, church, mafia org), the software can be designed in a default social way. Most apps default to isolation, low horizontal visibility, highly restricted collaboration. Makes sense if the social contracts are inside of the app, but if you can trust that they live outside of the app then you can get wild. Example being small group chat names as an emergent comedy phenomenon because anyone in the group can change the name. 3. This can work for commercial products, too, not just internal stuff. Security matters more than ever but the way we've seen that manifest is that more companies are more confident in their own solutions than anything we could provide, to the point where owning data and access is a better value proposition than having it managed. Always has been true for big companies but now even small companies can get a good secure network set up pretty easily, and the hooks we need to build are easy enough. tldraw offline got us started down this road (and shout out to @pendev for first introducing me to the idea of desktop + mcp). We've been bringing that product to private networks and it's been exciting to see how much we can get away with. Keep an eye outReplied on X@saradu birds eye viewLiked on XApplications close in 2 days!! If you have a cool bio idea, and $5-10k will go a long way for you, please apply!!! https://t.co/NCvmrNC53cLiked on XWe've been early adopters of Rust since 2018 and a lot of our infra, voice tech, and even some of the current client is already Rust, the holy grail was getting the whole client there. That means Electron goes away, and so does React Native, mobile keeps native UI but shares app code with desktop via Rust. We hope to get it into testing with users soon™. (And it's not Tauri, it's GPUI, so good guess everyone.) Alright, back to staring at the WoW Forever login screen.Reposted on XOh boy. https://t.co/X7SSskGuGvLiked on XRIP .si domain squatters
Creating Calendar in JavaScript

Creating Calendar in JavaScript

Warning

Over a year old. May be outdated

A calendar in plain JavaScript, no libraries. This project is based on my internship at Woowa Bros, a unicorn food-delivery startup in Seoul, back in .

Show me the code first.

GitHub - anaclumos/calendar.js: Vanilla JS Calendar

Show me the demo first.

Goals

  • Use functional programming* over object-oriented programming.
  • No DOM manipulation after init. This philosophy is based on React (or any Single Page Application library). DOM manipulation can be highly confusing if 30 different codes try to edit the same thing, so we will rerender the components if we need to edit something.

💡 Don't fix it. Buy a new one.

That's rerendering in front-end, in one line.

Stack

  • JavaScript Date Object
  • CSS display: grid will be useful.

Basic Idea

  • There will be a global displayDate object that represents the displaying month.
  • navigator.js will change this displayDate object, and trigger renderCalendar() function with displayDate as an argument.
  • renderCalendar() will rerender with the calendar.

Before anything, prettier!

helps write clean and neat code with automatic formatting.

json
// `.prettierrc`
{
  "semi": false,
  "singleQuote": true,
  "arrowParens": "always",
  "tabWidth": 2,
  "useTabs": false,
  "printWidth": 60,
  "trailingComma": "es5",
  "endOfLine": "lf",
  "bracketSpacing": true
}

Now throw in some HTML.

html
<!-- `index.html` -->
<!doctype html>
<html lang="en">
  <head>
    <title>JavaScript Calendar</title>
  </head>
  <body>
    <div id="navigator"></div>
    <div id="calendar"></div>
  </body>
  <script>
    // code for rendering
  </script>
</html>

I generated this boilerplate with VS Code.

Then trick VS Code to read JS String as HTML Tags.

Vanilla JavaScript means no JSX-style highlighting. Our HTML will live in JavaScript strings, with no highlighting or Intellisense. So, a function that tricks VS Code into reading the string as HTML.

js
// `util.js`
const html = (s, ...args) => s.map((ss, i) => `${ss}${args[i] || ""}`).join("");

to be added - screenshot of highlighting

calendar.js

Then we connect calendar.js and index.html.

html
<!-- `index.html` -->
<script src="calendar.js"></script>

Defining constants will help before writing renderCalendar().

js
// `calendar.js`
const NUMBER_OF_DAYS_IN_WEEK = 7;
const NAME_OF_DAYS = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
const LONG_NAME_OF_DAYS = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
];
const ACTUAL_TODAY = new Date();

NUMBER_OF_DAYS_IN_WEEK kills the magic number. A random 7 in code can be tough to decipher. A constant like this increases maintainability.

js
for (let d = 0; d < NUMBER_OF_DAYS_IN_WEEK; d++) {
  // do something
}

If there was a random 7, who knows if we are iterating through the number of Harry Potter Books?

This code block will be the baseline for our calendar generation. We will pass in the HTML target and a day object. today is the month on screen and comes from navigator.js. Navigator gives the real date for this month and the first of the month for other months.

js
// `calendar.js`
const renderCalendar = ($target, today) => {
  let html = getCalendarHTML(today);
  // minify html
  html = html.replace(/\n/g, "");
  // replace multiple spaces with single space
  html = html.replace(/\s{2,}/g, " ");
  $target.innerHTML = html;
};

Now, we need four different Date objects for displaying the calendar. We could've used fewer, but that's up to the implementation. Cutting date objects here would buy a tiny bit of performance and make the code much harder to follow, so four objects feels like a fair middle ground.

Four Date objects we need

  • The last day of last month: needed to highlight last month's weekend and display the correct date for last month's row.
  • The first day of this month: needed to highlight this month's weekend and figure out how many days of last month we need to render.
  • The last day of this month: needed for rendering this month with iteration.
  • The first day of next month: needed to highlight the weekend of next month.

I made a function that would process these four dates when inputted a specific Date.

js
// `calendar.js`
const processDate = (day) => {
  const month = day.getMonth();
  const year = day.getFullYear();
  return {
    lastMonthLastDate: new Date(year, month, 0),
    thisMonthFirstDate: new Date(year, month, 1),
    thisMonthLastDate: new Date(year, month + 1, 0),
    nextMonthFirstDate: new Date(year, month + 1, 1),
  };
};

The function bundles these four dates into an object and returns it. It takes a Date object as its argument, and in this calendar that's the Date for "today."

js
const processDate = (day) => {
  const date = day.getDate();
  const month = day.getMonth();
  const year = day.getFullYear();
  return {
    lastMonthLastDate: new Date(year, month, 0),
    thisMonthFirstDate: new Date(year, month, 1),
    thisMonthLastDate: new Date(year, month + 1, 0),
    nextMonthFirstDate: new Date(year, month + 1, 1),
  };
};

2-2. Create getCalendarHTML

Now let's actually draw the calendar. getCalendarHTML returns the calendar's contents as HTML. It gets a bit bulky, so here's the skeleton first.

js
const getCalendarHTML = () => {
  let today = new Date();
  let {
    lastMonthLastDate,
    thisMonthFirstDate,
    thisMonthLastDate,
    nextMonthFirstDate,
  } = processDate(today);
  let calendarContents = [];

  // ...

  return calendarContents.join("");
};

First, a row at the top for the days of the week. The const from earlier keeps the magic number out.

js
for (let d = 0; d < NUMBER_OF_DAYS_IN_WEEK; d++) {
  calendarContents.push(
    html`<div class="${NAME_OF_DAYS[d]} calendar-cell">
      ${NAME_OF_DAYS[d]}
    </div>`,
  );
}

Then last month. If this month starts on a Wednesday, this loop fills in last month's Sunday, Monday, and Tuesday. Sundays get the sun class.

js
for (let d = 0; d < thisMonthFirstDate.getDay(); d++) {
  calendarContents.push(
    html`<div
      class="
          ${d % 7 === 0 ? "sun" : ""}
          calendar-cell
          past-month
        "
    >
      ${lastMonthLastDate.getMonth() + 1}/${
        lastMonthLastDate.getDate() - thisMonthFirstDate.getDay() + d
      }
    </div>`,
  );
}

This month works the same way. Today gets the today class and a "today" label, and Saturdays and Sundays get sat and sun.

js
for (let d = 0; d < thisMonthLastDate.getDate(); d++) {
  calendarContents.push(
    html`<div
      class="
          ${today.getDate() === d + 1 ? "today" : ""}
          ${(thisMonthFirstDate.getDay() + d) % 7 === 0 ? "sun" : ""}
          ${(thisMonthFirstDate.getDay() + d) % 7 === 6 ? "sat" : ""}
          calendar-cell
          this-month
        "
    >
      ${d + 1} ${today.getDate() === d + 1 ? " today" : ""}
    </div>`,
  );
}

Finally, draw the days of the next month in the remaining cells.

js
let nextMonthDaysToRender = 7 - (calendarContents.length % 7);

for (let d = 0; d < nextMonthDaysToRender; d++) {
  calendarContents.push(
    html`<div
      class="
          ${(nextMonthFirstDate.getDay() + d) % 7 === 6 ? "sat" : ""}
          calendar-cell
          next-month
        "
    >
      ${nextMonthFirstDate.getMonth() + 1}/${d + 1}
    </div>`,
  );
}

3. Writing CSS

3-1. Using display: grid

display: grid lays an element's children out neatly in a grid, like a table.

  • grid-template-columns sets how the columns are laid out. 1fr means one fraction, and it's written 7 times, so you get 7 columns of equal width.
  • grid-template-rows sets row sizes. There's only one 3rem here, so only the first row is 3rem.
  • grid-auto-rows sets the size of every row after that. It says 6rem, so the rest are 6rem.

The rest is plain styling.

css
#App {
  /* grid */
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
  grid-template-rows: 3rem;
  grid-auto-rows: 6rem;

  /* style */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  border: 1px solid black;
  max-width: 720px;
  margin-left: auto;
  margin-right: auto;
}
  • When you draw a table, you want every cell wrapped in the same border, like Excel. But sometimes only the outermost cells end up with thinner lines, because in HTML terms the border only goes on th and td.
  • I like to put n px on every cell border and n px on the table border. That gives you a uniform 2n px border everywhere.
css
.calendar-cell {
  border: 1px solid black;
  padding: 0.5rem;
}

3-2. Highlighting Saturdays, Sundays, and today

css
.past-month,
.next-month {
  color: gray;
}

.sun {
  color: red;
}

.sat {
  color: blue;
}

.past-month.sun {
  color: pink;
}

.next-month.sat {
  color: lightblue;
}

.today {
  color: #e5732f;
}

I felt that

  • At first I got a little lost hooking up JS to "initialize" the calendar. I had attached renderCalendar at the top of body. The DOM runs top to bottom, so renderCalendar ran before the #App div existed and couldn't find it.
  • I also forgot how to get what I'd built in JS onto the screen. The answer was simple. Grab the app with querySelector in the JS that plays the role of index.js, and set its innerHTML.
  • The Woowa Tech Camp project had magic numbers. This time I took them out for readability.
  • The Woowa Tech Camp project was object-oriented JavaScript (a Singleton, to be precise). This time it's small functions.
  • I tried to use ES6+ syntax, like template literals with backticks and destructuring what processDate returns. Mostly let and const, too.
  • I wish I could have made getCalendarHTML a bit shorter.
Backlinks1