<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Karthik Ganeshram</title><link>/</link><description>Recent content from Karthik Ganeshram</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sat, 14 Mar 2020 21:19:43 -0700</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml"/><item><title>The Friction of the First Pedal Stroke</title><link>/blog/the-friction-of-the-first-pedal-stroke/</link><pubDate>Sun, 31 May 2026 00:00:00 +0000</pubDate><guid>/blog/the-friction-of-the-first-pedal-stroke/</guid><description>&lt;p&gt;This month began with a lot of forward motion.&lt;/p&gt;
&lt;p&gt;Florian and I spent eight days cycling through Champagne and Colmar, covering roughly 625 kilometers. The trip itself wasn’t heavily structured; it was just long stretches of countryside, quiet roads, and the simple daily act of pedaling.&lt;/p&gt;
&lt;p&gt;When I got home, I knew I was physically exhausted. I made the conscious decision to take a couple of days completely off to rest and recover. It felt like the right thing to do.&lt;/p&gt;
&lt;p&gt;But those few days off accidentally removed the guardrails.&lt;/p&gt;
&lt;p&gt;Instead of a clean reset, the sudden lack of movement opened the door for inertia. At the same time, the weight of navigating life began to catch up with me. The habits that normally feel automatic suddenly felt impossibly heavy.&lt;/p&gt;
&lt;p&gt;My mistake wasn&amp;rsquo;t taking a break; it was how I tried to come out of it. Instead of easing back in, accepting my lower energy, and gathering micro-momentum, I demanded that I immediately snap back into my regular rhythm at top speed. I tried to force a 100% output when my emotional tank was running on empty. Because I couldn&amp;rsquo;t match that unrealistic pace, the gears jammed entirely. What was supposed to be a short pause quietly turned into a downward spiral, leaving me jaded, stuck at a complete standstill, and waiting for the motivation to magically return.&lt;/p&gt;
&lt;p&gt;For a week or so, I was incredibly hard on myself.&lt;/p&gt;
&lt;p&gt;When you value consistency, watching yourself slide into a slump feels like a personal failure. I looked at my sluggish mornings with frustration, demanding to know why I couldn&amp;rsquo;t just perform. It is easy for the internal critic to take over during a low week, but beating myself up didn&amp;rsquo;t generate any momentum; it just made the spiral feel heavier.&lt;/p&gt;
&lt;p&gt;Eventually, I had to realize that I had the equation backward. We tend to think that motivation creates action, but when you are in a slump, it’s the other way around. Action has to precede motivation. You have to overcome the initial friction just to get the wheels to turn once, even if the first pedal stroke feels awkward and entirely uninspired. You can&amp;rsquo;t shift straight into the highest gear from a dead stop.&lt;/p&gt;
&lt;p&gt;Coincidentally, I got a new bike after the trip. I didn&amp;rsquo;t buy it to solve my routine or to force myself out of a funk, but in retrospect, it helped a little. It became a low-stakes excuse to just get outside and feel the familiar sensation of movement without any pressure to perform. It was a small way to generate a tiny bit of micro-momentum when everything else felt stagnant.&lt;/p&gt;
&lt;p&gt;I am slowly working my way back up now. The routine isn&amp;rsquo;t flawless, and the pace isn&amp;rsquo;t what it was, but the wheels are turning again.&lt;/p&gt;
&lt;p&gt;I’ve come to accept that brakes do get applied sometimes in the journey, even when we don&amp;rsquo;t intend them to. A downward spiral isn&amp;rsquo;t a permanent loss of discipline; sometimes it’s just the messy, chaotic way our mind and body force us to stop and process things.&lt;/p&gt;
&lt;p&gt;The goal isn&amp;rsquo;t to ride at top speed without ever stopping. It’s learning how to forgive yourself for the weeks you lose momentum, and finding the patience to handle the heavy friction of starting over from zero.&lt;/p&gt;</description></item><item><title>Not yet... Until it is!</title><link>/blog/not-yet...-until-it-is/</link><pubDate>Sun, 26 Apr 2026 00:00:00 +0000</pubDate><guid>/blog/not-yet...-until-it-is/</guid><description>&lt;p&gt;Last year, when I turned 26, I made a simple promise to myself: be consistent.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t want the occasional spark of motivation or the frantic intensity of a short burst. I wanted to be steady, especially when the work felt ordinary. It started well. I built a rhythm and proved I could show up, but even with that rhythm, I hit walls. A 150 km ride was my ceiling. I had set a goal of 200 km in August, but looking at the data, I had to be honest about where I stood. I felt the sting of falling short, which is that familiar disappointment of wanting a version of yourself that hasn&amp;rsquo;t arrived yet.&lt;/p&gt;
&lt;p&gt;That feeling lingered until I reframed it with two small words: &lt;strong&gt;not yet&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It felt trivial at first, but it shifted my perspective. Instead of seeing the gap as a failure, I began to see it as a work in progress. I stopped chasing the outcome and started trusting the quiet background noise of consistency.&lt;/p&gt;
&lt;p&gt;Over time, &amp;ldquo;not yet&amp;rdquo; stopped feeling like a limitation and started feeling like a phase. We often treat readiness as a fixed state, something we either possess or lack, when in reality it is something we inhabit gradually. The work doesn’t always announce itself. It accumulates in the margins and reshapes our &amp;ldquo;normal&amp;rdquo; until the things that once felt impossible are suddenly within arm&amp;rsquo;s reach.&lt;/p&gt;
&lt;p&gt;This past week, I saw the evidence. I rode 160 Kilometers (an imperial century!) last weekend, and this weekend, I cleared 213 kilometers. Somewhere in between, I pulled myself up for my first bodyweight pull-up.&lt;/p&gt;
&lt;p&gt;The strange part? It didn&amp;rsquo;t feel dramatic. There was no cinematic moment where everything clicked and no sudden surge of power. It just felt… normal. That might be the clearest sign of growth: when a milestone no longer feels like a peak, but like the ground you’re standing on.&lt;/p&gt;
&lt;p&gt;The milestones matter less than the way they were built. Most of those days didn&amp;rsquo;t feel like progress; they felt like repetitions. You show up, you do the work, and you move on. But that is the part that compounds. It shifts your baseline invisibly until your &amp;ldquo;not yet&amp;rdquo; looks exactly like &amp;ldquo;now.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I’ve been using the phrase &amp;ldquo;not yet&amp;rdquo; in a lot of my conversations lately, but the inspiration for this title came from a recent talk with someone close to me. We were just talking about my goals when the phrase came up naturally: not yet… until it is. It was a casual moment, but it stayed with me because it put words to a feeling I hadn&amp;rsquo;t fully framed myself. It captured the idea that the distance between where you are and where you want to be isn’t a fixed void. It is just a space that needs time and consistency to close.&lt;/p&gt;
&lt;p&gt;I’m writing this as a reminder to myself. To keep going when the work feels unremarkable. To trust the process when it feels like nothing is happening. I’ve seen now how quietly the world can change.&lt;/p&gt;
&lt;p&gt;Not yet… until it is.&lt;/p&gt;</description></item><item><title>The Millionth Step: A look back on the year so far</title><link>/blog/the-millionth-step-a-look-back-on-the-year-so-far/</link><pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate><guid>/blog/the-millionth-step-a-look-back-on-the-year-so-far/</guid><description>&lt;p&gt;I hit a million steps for the year on my Garmin this week.&lt;/p&gt;
&lt;p&gt;It is a number that represents more than just distance. It represents a steady, consistent momentum that has defined the start of my year. Looking back to January, the foundation I have built is solid. The professional transition into Akamai after the Fermyon acquisition has been going smoothly. There is a satisfying rhythm to the work that I continue to enjoy, and it provides a sense of stability that feels earned.&lt;/p&gt;
&lt;p&gt;Even the physical setbacks have mostly cleared. I started the year hampered by injuries to my lower back and knee, but those are largely memories now. There is just a small amount of tension left in the lower back, but it is not enough to keep me off the bike. I am looking at the coming summer with a lot of real excitement for being back on the road.&lt;/p&gt;
&lt;h2 id="the-space-between-certainty-and-peace"&gt;The Space Between Certainty and Peace&lt;/h2&gt;
&lt;p&gt;I find myself in a peculiar position lately. I am, by all accounts, in a good place. My life is professionally fulfilling and physically rebounding. Yet, there is a distinct fog that has settled over the path ahead.&lt;/p&gt;
&lt;p&gt;It has been an interesting realization to find that I can be internally stable while the external direction of my life feels entirely uncertain. It is not a crisis or a collapse. It is more like biking a familiar route and suddenly reaching a stretch where the road markers have been moved. A situation changes, a possibility shifts, and suddenly the map I was using to navigate the next few months is blank.&lt;/p&gt;
&lt;p&gt;I am still in a good place, but the visibility has dropped.&lt;/p&gt;
&lt;p&gt;In many ways, this feels like a bump in what has been a very smooth growth journey over the last year. Up until now, the path was clear and the progress was linear. Now, I am finding value in this forced pause. It is a chance to recalibrate. When you are moving fast, you don&amp;rsquo;t always check the alignment of your goals. This uncertainty is giving me the space to make sure the direction I eventually choose is the one I actually want.&lt;/p&gt;
&lt;h2 id="expanding-the-map"&gt;Expanding the Map&lt;/h2&gt;
&lt;p&gt;Alongside that recalibration is a quieter but persistent desire to grow. If the long-term path feels unclear, there is still value in expanding what is immediately within reach.&lt;/p&gt;
&lt;p&gt;I want to put myself out there more and grow my social world in a way that feels sustainable. I have not yet found a method that clicks, so I am considering a simple rule: one new thing every month. Something small, intentional, and repeatable.&lt;/p&gt;
&lt;p&gt;It is less about solving the uncertainty and more about staying open despite it. If I cannot see the far horizon right now, I can still choose to explore what is nearby.&lt;/p&gt;
&lt;p&gt;A million steps in, the bike is ready and the foundation is firm. I am still in a good place. For now, that is enough and the next steps will reveal themselves when the road does.&lt;/p&gt;</description></item><item><title>Choosing Your Discomfort</title><link>/blog/choosing-your-discomfort/</link><pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate><guid>/blog/choosing-your-discomfort/</guid><description>&lt;p&gt;I’ve been thinking about how life is full: full of good, full of clarity, full of momentum. Right now, I’m in a place I genuinely love. Things feel aligned. I feel grounded. And even here, even at a point where I can honestly say I’m happy, I notice the choices I make still carry discomfort.&lt;/p&gt;
&lt;p&gt;It’s funny, happiness doesn’t eliminate friction. It just makes it clearer. I see it for what it is. And I realize the only real choice is which discomfort I’m willing to carry.&lt;/p&gt;
&lt;p&gt;I can choose the discomfort of risk - of putting myself forward, stepping into something uncertain, letting outcomes happen that I can’t fully control. It hits fast. Sharp. Immediate. And it teaches. It shows me what works, what doesn’t. It adds texture to life. Even if things don’t go perfectly, I gain clarity. I feel alive.&lt;/p&gt;
&lt;p&gt;Or I can choose the discomfort of hesitation - holding back, wondering, replaying possibilities in my head. It feels safe, even comfortable at first. But it lingers. It doesn’t teach. It doesn’t move me. It just sits quietly, weighing more the longer I leave it alone.&lt;/p&gt;
&lt;p&gt;I find myself drawn to the discomfort that moves me. The one that’s honest and present. The kind that sharpens my sense of self, stretches my understanding, and keeps life vivid. Moving feels better but that doesn’t mean I want to be reckless. I want edges, but edges with awareness.&lt;/p&gt;
&lt;p&gt;Even in a place of happiness, I don’t want comfort to lull me into stasis. I want edges. I want clarity. I want growth. I want the kind of discomfort that makes life feel expansive rather than restrained.&lt;/p&gt;
&lt;p&gt;And so I keep choosing it. Not because life is hard, not because I’m chasing challenge for its own sake, but because the friction of action, of engagement, of presence, adds to the life I already love. It amplifies it.&lt;/p&gt;
&lt;p&gt;Happiness and growth aren’t opposites. They coexist. The discomfort I choose is part of that. It’s part of why life feels alive, why the present feels rich, why I can trust myself to navigate what comes next.&lt;/p&gt;
&lt;p&gt;And that feels like the best kind of discomfort there is.&lt;/p&gt;</description></item><item><title>A Note to Myself: Let Things Unfold</title><link>/blog/a-note-to-myself-let-things-unfold/</link><pubDate>Sat, 31 Jan 2026 00:00:00 +0000</pubDate><guid>/blog/a-note-to-myself-let-things-unfold/</guid><description>&lt;p&gt;Lately, I have been thinking about how some things take shape quietly, without announcement. It has been a calm month, and with all the walking I have been doing, reflection has started to feel less intentional and more natural. Small habits like a morning coffee, practicing latte art, or noticing light while taking photos have become gentle anchors in my day. They ground me while still leaving space to think.&lt;/p&gt;
&lt;p&gt;I am noticing how certain moments feel unexpectedly close, even within the ordinary rhythm of life. There is a subtle richness in paying attention. To someone’s honesty. To shared presence. Or simply to the way things unfold in their own time.&lt;/p&gt;
&lt;p&gt;At the same time, I am becoming more aware of the gaps that naturally exist between people. Differences in timing, perspective, or focus. I am learning to observe those gaps without trying to resolve them, without needing to force connection or define it. There is a quiet freedom in allowing things to be exactly as they are.&lt;/p&gt;
&lt;p&gt;I am also noticing how open I have become to new situations and experiences that arrive without a script. Saying yes, even without knowing what will come of it, feels like a gentle practice in trust. Trust in the present. Trust in myself.&lt;/p&gt;
&lt;p&gt;Some parts of life are still unfolding. Some connections are deepening, while others are loosening. And that feels okay. I am learning to hold curiosity and gratitude at the same time, allowing my own journey to exist alongside the natural currents of life, quietly and without judgment.&lt;/p&gt;
&lt;p&gt;For now, noticing, listening, and being present feels like enough.&lt;/p&gt;</description></item><item><title>A Year in Review: 3.8 Million Steps, Forward</title><link>/blog/a-year-in-review-3.8-million-steps-forward/</link><pubDate>Mon, 29 Dec 2025 00:00:00 +0000</pubDate><guid>/blog/a-year-in-review-3.8-million-steps-forward/</guid><description>&lt;p&gt;I’ve written reflections throughout the year about different parts of my life. Work, health, mindset, relationships. Looking back at them now, there’s a clear thread running through all of it.&lt;/p&gt;
&lt;p&gt;If I had to name this year, I’d call it a year of consistency and growth.&lt;/p&gt;
&lt;p&gt;For most of my life, I was good at starting things but struggled to see them through. That gap quietly bothered me, especially when it came to taking care of myself. This year, something changed. Not through motivation or intensity, but through showing up repeatedly and trusting the process.&lt;/p&gt;
&lt;p&gt;Consistency became a form of self-respect. A way of trusting myself.&lt;/p&gt;
&lt;h2 id="rebuilding-from-an-unsteady-start"&gt;Rebuilding from an unsteady start&lt;/h2&gt;
&lt;p&gt;The year didn’t begin on a high note. I started it feeling mentally off balance, unsure if I was doing enough and uncertain about direction. I carried a quiet pressure about where I should be.&lt;/p&gt;
&lt;p&gt;What turned things around wasn’t a single breakthrough, but momentum built slowly. Small routines. Repeated effort. Less planning, more follow-through.&lt;/p&gt;
&lt;p&gt;Fitness became the anchor!&lt;/p&gt;
&lt;p&gt;With regular gym training and the help of a personal trainer, consistency finally stuck. Having that structure, and someone pushing me beyond comfort zones I would have settled into on my own, made a real difference. Not just physically, but mentally as well. Viktoriia played a big role in that. Her coaching helped me trust discipline over motivation.&lt;/p&gt;
&lt;p&gt;I lost weight, built muscle, and grew more comfortable in my body. The mental health benefits mattered just as much as the physical ones.&lt;/p&gt;
&lt;p&gt;&lt;img src="/review_2025/gym.png" alt="picture in the gym"&gt;&lt;/p&gt;
&lt;h2 id="falling-back-in-love-with-biking"&gt;Falling back in love with biking&lt;/h2&gt;
&lt;p&gt;This year, I rediscovered how much I love biking. After deciding to take it seriously, I rode around 4,000 km, and it quickly became more than just exercise.&lt;/p&gt;
&lt;p&gt;Biking gives me calm. It’s where my thoughts slow down and sort themselves out.&lt;/p&gt;
&lt;p&gt;Some rides stand out. Being challenged into a 150 km ride when I had only asked for a 100 km route and finishing it. Long rides through Belgium, including a memorable one from Ghent to Knokke. Catching the beginning of the Tour de France in Lille. Chasing sunrises and sunsets again, simply for the joy of it.&lt;/p&gt;
&lt;p&gt;Biking also became social in the best way. Shared effort, long conversations, and quiet stretches where nothing needed to be said.&lt;/p&gt;
&lt;p&gt;A big part of that was riding with Florian, who pushed me not just on the bike but in how I approached the year overall. What started with bikes gradually spilled into other parts of life. Having someone around who shares similar values and a desire to keep improving made this year feel less solitary and more grounded.&lt;/p&gt;
&lt;p&gt;I only started riding consistently in June, and I spent large parts of September and October away from home. Knowing that makes this year feel even more encouraging. Next year, my goal is to ride 7,000 km. Not as a stretch for the sake of it, but as a continuation of something that has already become part of who I am. I would also love to take a biking vacation sometime next year.&lt;/p&gt;
&lt;p&gt;&lt;img src="/review_2025/cycling.png" alt="Bikng with friends"&gt;&lt;/p&gt;
&lt;h2 id="presence-not-just-progress"&gt;Presence, not just progress&lt;/h2&gt;
&lt;p&gt;Somewhere along the way, I started living more in the moment.&lt;/p&gt;
&lt;p&gt;I still care about goals, but I’m no longer always chasing the next one at the expense of the present. I find joy now in simple things. Running in the rain. A really good pastry with coffee. Quiet mornings. Being outside whenever I can.&lt;/p&gt;
&lt;p&gt;I’ve intentionally spent less time on tech outside of work and more time grounded in the real world. That shift alone has made life feel lighter and more balanced.&lt;/p&gt;
&lt;p&gt;Some of the most memorable moments this year were the smallest ones. Discovering a love for really good pastries and coffee. Sitting somewhere warm on a slow morning. Chasing sunrises and sunsets without a plan. These moments grounded me in the present and reminded me that joy doesn’t always come from milestones. Sometimes it’s already there if I’m paying attention.&lt;/p&gt;
&lt;p&gt;&lt;img src="/review_2025/small_things.png" alt="collage of pictures"&gt;&lt;/p&gt;
&lt;h2 id="work-balance-and-momentum"&gt;Work, balance, and momentum&lt;/h2&gt;
&lt;p&gt;Professionally, this was a strong year.&lt;/p&gt;
&lt;p&gt;I gave two conference talks, something I had wanted to do for a long time. Not everything went perfectly, including broken demos, but that was never the point. It was a start, and it gave me confidence. I want to do more of this going forward.&lt;/p&gt;
&lt;p&gt;The startup I work for was acquired, and I was promoted to Senior Software Engineer. I’m genuinely enjoying my work and curious about what the next year holds.&lt;/p&gt;
&lt;p&gt;Just as importantly, I’ve improved my work-life balance. I’m more intentional about how I spend my time and better at stepping away when needed. Enjoying my work while still having space for life outside of it has made a meaningful difference.&lt;/p&gt;
&lt;h2 id="reading-and-perspective"&gt;Reading and perspective&lt;/h2&gt;
&lt;p&gt;I read around 20 books this year. The Book of Why and Clear Thinking stood out for how they shaped the way I think.&lt;/p&gt;
&lt;p&gt;Reading, for me, is about expanding perspective. I started strong, slowed down later, and want to be more intentional about it again next year.&lt;/p&gt;
&lt;h2 id="relationships-boundaries-and-growing-up"&gt;Relationships, boundaries, and growing up&lt;/h2&gt;
&lt;p&gt;Some relationships deepened this year, built on shared values, mutual effort, and a genuine desire to keep improving. Others faded, and I learned that it’s okay to let go when the effort becomes unidirectional.&lt;/p&gt;
&lt;p&gt;I’ve become better at setting boundaries and kinder to myself because of it.&lt;/p&gt;
&lt;p&gt;I also changed how I socialize at home. I still enjoy spending time in larger groups, but when it comes to dinners, I started hosting smaller ones more often. I invite one or two people over and cook together. It’s easier on my social battery, simpler logistically, and more meaningful. In many ways, it feels like a quiet step into being a more grounded adult.&lt;/p&gt;
&lt;p&gt;I express gratitude more often now, and I feel more confident talking to people. Next year, I want to keep meeting new people, expanding my circle, and building meaningful connections.&lt;/p&gt;
&lt;h2 id="travel-independence-and-roots"&gt;Travel, independence, and roots&lt;/h2&gt;
&lt;p&gt;I took a short solo trip to Copenhagen on a whim and spent a few days on my own. It reminded me how much I enjoy solo travel, something I used to do more and want to return to.&lt;/p&gt;
&lt;p&gt;I also traveled with close friends and coworkers, each trip meaningful in its own way. Each of those trips helped me builder a closer bond to the people that I travelled with.&lt;/p&gt;
&lt;p&gt;One of the most special moments this year was visiting India to surprise my mom for her 50th birthday. I hadn’t been back in a couple of years, and the trip made me reflect on how living in three different countries has shaped me. There are cultural clashes I’m still figuring out, and I’m okay letting that unfold slowly.&lt;/p&gt;
&lt;h2 id="closing-the-year-with-gratitude"&gt;Closing the year with gratitude&lt;/h2&gt;
&lt;p&gt;Next year, I want to continue, not restart.&lt;/p&gt;
&lt;p&gt;I want to stay consistent. With the gym. With running, with the goal of a half marathon on my birthday. With writing. With how I show up for people. I want to keep living in the moment, educate myself financially, build relationships, and explore new activities.&lt;/p&gt;
&lt;p&gt;Most of all, I’m grateful. For my health, for consistency, and for the people in my life who showed up for me, supported me, challenged me, and shared parts of this year with me, even when they didn’t realize the impact they were having.&lt;/p&gt;
&lt;p&gt;According to my Garmin, I took 3.8 million steps this year. Not all of them were deliberate. Some were hesitant, some confident, some taken on days when motivation was low. Taken together, they moved me forward quietly and consistently, one step at a time.&lt;/p&gt;
&lt;p&gt;I’m grateful for quiet mornings, long rides, meaningful conversations, and a year that reminded me I can trust myself.&lt;/p&gt;
&lt;p&gt;This has been one of the best years of my life. Not because it was perfect, but because I kept stepping forward.&lt;/p&gt;
&lt;p&gt;And that feels like the right place to continue from!&lt;/p&gt;</description></item><item><title>The Joy of the Pause</title><link>/blog/the-joy-of-the-pause/</link><pubDate>Sat, 29 Nov 2025 00:00:00 +0000</pubDate><guid>/blog/the-joy-of-the-pause/</guid><description>&lt;p&gt;I have learned something about myself over the years. Whenever I reach a goal, I celebrate, but I also tend to move on quickly. Historically, my celebrations were short lived bursts of joy. Bright, enthusiastic, and gone almost before I had time to look around and appreciate what I had actually done.&lt;/p&gt;
&lt;p&gt;My happiness has always been genuine. It often involves a pastry, which is probably the most reliable reward system I have ever invented. I sit there with something sweet and comforting, enjoy the moment fully, and feel proud of myself. But by the time I am brushing off the last crumbs, my mind is already getting restless. It starts looking for the next mountain, the next idea, the next challenge. Apparently I have an inner endurance athlete who believes recovery time should be optional.&lt;/p&gt;
&lt;p&gt;Because of that, my life has often felt a bit like completing a long, tough hike, finally reaching the summit, taking one victorious photo, and then immediately checking the map to see what other trails I can do before the sun sets. I do not skip the accomplishment, but I definitely do not linger either. I go from summit to summit at a pace that surprises even me sometimes.&lt;/p&gt;
&lt;p&gt;Recently I have tried something different. I am making a conscious effort to let success settle in. I stay with the feeling of achievement longer. I savor the pastry instead of inhaling it. I let myself sit at the summit before rushing down the trail to find a new challenge.&lt;/p&gt;
&lt;p&gt;And the surprising part is how much lighter everything feels.&lt;/p&gt;
&lt;p&gt;With fewer self imposed deadlines swirling in my head, I have noticed a level of calm that used to feel out of reach. Without the constant pressure to chase the next big thing, I enjoy my days more. I feel more grounded and more present. It is as if my mind finally set down a heavy backpack it did not need to carry. The weight of the endless push forward has eased.&lt;/p&gt;
&lt;p&gt;Giving myself permission to pause has made a real difference in my mental health. I feel more balanced. I feel more able to appreciate small joys. I feel more connected to my own life instead of racing through it. The pauses have not slowed my ambition. They have simply made it more sustainable.&lt;/p&gt;
&lt;p&gt;I still love new goals. I still get excited about fresh possibilities. That part of me is alive and well. It is just no longer running the entire show. It has learned to wait while I enjoy the view at the top. It has learned that rest is not the enemy of progress. It is part of it.&lt;/p&gt;
&lt;p&gt;So here I am, taking longer celebrations, eating pastries at a normal pace, and allowing myself to stay in the moment. The next goal will be there when I am ready. For now, it feels good to breathe a little more and push a little less.&lt;/p&gt;
&lt;p&gt;And yes, the pastries taste even better this way!&lt;/p&gt;</description></item><item><title>Coffee, Pastries, and the Joy in the Little Things</title><link>/blog/coffee-pastries-and-the-joy-in-the-little-things/</link><pubDate>Mon, 20 Oct 2025 00:00:00 +0000</pubDate><guid>/blog/coffee-pastries-and-the-joy-in-the-little-things/</guid><description>&lt;p&gt;I’ve always had a sweet tooth. But this year, that small craving has grown into something much bigger, a lens through which I’ve been noticing joy in unexpected, everyday moments.&lt;/p&gt;
&lt;p&gt;It all started back in March while traveling. In Porto with a friend, we turned bakery-hopping into a game: stopping at nearly every bakery we passed and tasting pastéis de nata and other treats. When I got home, I couldn’t stop thinking about them, so I decided to bake pastéis de nata from scratch. They weren’t perfect, but the process reminded me that small efforts like whisking custard or folding pastry can bring a lot of satisfaction, even if the result isn’t flawless.&lt;/p&gt;
&lt;p&gt;A few weeks later, I was in Barcelona with a coworker, continuing the tradition of wandering into bakeries. By the end of the trip, it was clear that I wasn’t just enjoying pastries, I was savoring the simple, grounding pleasure of experiencing them fully, and it always felt even better with a good cup of coffee in hand.&lt;/p&gt;
&lt;p&gt;Back in Rotterdam, this curiosity for small joys found its rhythm. At first, pastries and coffee were simply a reward for finishing a morning run - a small, deliberate indulgence that made early mornings feel lighter. Soon I began sharing them with a friend, turning it into a weekly ritual: picking up pastries and meeting to catch up over breakfast before work. Those quiet mornings, sipping coffee and savoring something sweet, became one of my favorite parts of the week.&lt;/p&gt;
&lt;p&gt;During the summer, all the biking around Rotterdam made these small pleasures feel earned. My friends and I would detour to little cafés for coffee and pastries. And somewhere along the way, I also fell in love with solitary mornings: waking up early, stepping out while the city is still quiet, grabbing a fresh cup of coffee, and walking along the Maas. Sitting by the bridge or wandering along the water with that warm drink, I realized that these small, intentional moments with a coffee in hand and the city still asleep can feel profoundly joyful.&lt;/p&gt;
&lt;p&gt;As for favorites: &lt;a href="https://share.google/B7L7gDV07JCklXWqc"&gt;Ripsnorter&lt;/a&gt; and &lt;a href="https://share.google/qPCPLUQTjMoKir2NJ"&gt;Cafecito&lt;/a&gt; for coffee; &lt;a href="https://share.google/DvSeEKO3eOeNw8VBs"&gt;Le Petit Jean&lt;/a&gt;, my regular breakfast spot with a friend; &lt;a href="https://share.google/lElWxLrXbHtdrmBql"&gt;Lila Bakery&lt;/a&gt; for its Eastern European cakes. My only frustration used to be that most bakeries in Rotterdam don’t open before 8 a.m. By then I’d already been up, ready for my treat with my nose pressed up against the windows waiting for the shutters to open. But discovering that the Albert Heijn next to my house has fresh pastéis de nata at 7 a.m., paired with a strong cup of coffee, felt like a small victory. A reminder that joy can be found in tiny, everyday discoveries.&lt;/p&gt;
&lt;p&gt;Later in the year, my love for pastries and coffee followed me on trips abroad. In July, a short vacation in Copenhagen was spent with quiet mornings wandering the city with Danish pastries and café lattés, and in late September, a trip to Nice and Paris with a coworker continued the streak with croissants, éclairs, and warm cafés. Those mornings in Copenhagen, Nice, and Paris reinforced a lesson I had been learning all year: delight isn’t reserved for big moments or grand adventures. It can be found in small, intentional acts, pausing to savor a pastry, sipping coffee slowly while the city wakes, or noticing the little rituals that make ordinary days richer.&lt;/p&gt;
&lt;p&gt;What began as a joke in Portugal has become a rhythm that follows me everywhere: runs that end with pastries, breakfasts with friends, bike rides that detour for coffee, and quiet walks with a warm cup in hand. Whether I’m in Rotterdam, Paris, or elsewhere, I’ve realized that joy often hides in the little things, the first sip of morning coffee, the flaky edge of a croissant, the stillness of a city before it wakes up.&lt;/p&gt;
&lt;p&gt;Now that fall is here, I’m excited to keep the ritual alive: to wake up early, explore quiet streets, and let soft, dark mornings remind me that life’s sweetness often comes in small, intentional moments. Coffee and pastries, in their simplicity, have shown me that joy doesn’t always arrive in grand gestures; it often lives quietly, in cups, crumbs, and the deliberate act of slowing down.&lt;/p&gt;</description></item><item><title>A Note to Myself: Practice Gratitude More Often</title><link>/blog/a-note-to-myself-practice-gratitude-more-often/</link><pubDate>Sat, 06 Sep 2025 00:00:00 +0000</pubDate><guid>/blog/a-note-to-myself-practice-gratitude-more-often/</guid><description>&lt;p&gt;Sometimes I catch myself living on autopilot. Days blur together with work, routines, endless scrolling and even if I get a lot done, it still doesn’t feel satisfying. There’s this undercurrent of “I’m not doing enough,” like no matter how much I check off, I’m behind somehow.&lt;/p&gt;
&lt;p&gt;But when I stop and practice gratitude, that feeling shifts. Gratitude pulls me out of the “not enough” loop and reminds me of what’s already good. A good conversation. A quiet moment. The simple fact that I have people in my life I can lean on. When I notice those things, life feels fuller, lighter, and less about what’s missing.&lt;/p&gt;
&lt;p&gt;It also changes my mood in ways I don’t always expect. Gratitude makes me less restless, less caught up in comparing myself to others, and honestly more patient. It slows me down and lets me actually enjoy being here instead of racing toward some future moment that’s supposed to be better. Gratitude doesn’t erase problems, but it makes them feel smaller in the bigger picture.&lt;/p&gt;
&lt;p&gt;Something I’ve been thinking about lately is practicing gratitude more outwardly. It’s easy to feel thankful in my head but never say it. I want to get better at actually expressing it with writing thank you notes, sending quick messages to friends or family, or even just saying it in the moment instead of letting the chance slip by. Because gratitude isn’t only about what I feel, it’s also about what I give. And I know from experience how much a simple “I appreciate you” can shift someone’s whole day.&lt;/p&gt;
&lt;p&gt;The more I think about it, the more I realize gratitude isn’t just about happiness—it’s about connection. When I express it, I feel closer to people. I stop taking their presence for granted. I start to notice the ways they support me, show up for me, or simply make life better by being who they are. Gratitude makes relationships feel richer, not because anything changes on the outside, but because I see them more clearly.&lt;/p&gt;
&lt;p&gt;So maybe this is really just a reminder to myself: gratitude isn’t something to save for special occasions or dramatic moments. It’s something to practice every day inwardly, so I can enjoy my own life more, and outwardly, so the people around me know they matter. Life feels more meaningful when I do, and maybe, if I’m consistent about it, gratitude can ripple out farther than I realize.&lt;/p&gt;</description></item><item><title>Beyond Brutal Honesty: Choosing Truth with Care</title><link>/blog/beyond-brutal-honesty-choosing-truth-with-care/</link><pubDate>Mon, 18 Aug 2025 00:00:00 +0000</pubDate><guid>/blog/beyond-brutal-honesty-choosing-truth-with-care/</guid><description>&lt;p&gt;During my undergraduate years, I convinced myself that brutal honesty was the best way to live. I thought saying exactly what I believed, without filtering or softening, made me authentic and courageous. To me, sugarcoating felt dishonest, and I wanted to be someone who always “told it like it is.”&lt;/p&gt;
&lt;p&gt;Now, at 26, I find myself reflecting differently. The word brutal stands out to me more than it used to. Brutality implies force, harshness, and even a disregard for the other person. And when I look back, I realize that wasn’t really what I wanted to practice. What I valued was the truth, but truth doesn’t have to be brutal.&lt;/p&gt;
&lt;p&gt;I still believe honesty is deeply important. It’s the foundation of trust, self-respect, and real connection. Without honesty, relationships become shallow, and growth becomes difficult. But I’ve learned that honesty, when shared without care, can land in ways that shut people down rather than open them up. Truth alone isn’t enough; how we share it matters just as much.&lt;/p&gt;
&lt;p&gt;Over time, I’ve shifted toward what I now think of as &amp;ldquo;compassionate honesty&amp;rdquo;, speaking the truth with clarity, but also with empathy, so it can be received and reflected on. That doesn’t mean avoiding hard conversations. It means approaching them with compassion, remembering that honesty isn’t only about expressing myself, but also about creating space where growth and understanding are possible.&lt;/p&gt;
&lt;p&gt;Looking back, that phase of brutal honesty taught me to value truth and to have the courage to speak it. But today, I believe honesty becomes most powerful when paired with kindness. The world doesn’t need more &amp;ldquo;brutal&amp;rdquo; truths. It needs honesty that builds trust, nurtures connection, and helps us grow.&lt;/p&gt;
&lt;p&gt;Until next time, keep honesty kind!&lt;/p&gt;</description></item><item><title>The Quiet Shift: Three Months of Showing Up</title><link>/blog/the-quiet-shift-three-months-of-showing-up/</link><pubDate>Sat, 26 Jul 2025 00:00:00 +0000</pubDate><guid>/blog/the-quiet-shift-three-months-of-showing-up/</guid><description>&lt;p&gt;Sometimes the biggest changes come not from grand gestures, but from the quiet routines we build and the small decisions we make every day.&lt;/p&gt;
&lt;p&gt;For a long time, I told myself I wanted to get in shape. It was one of those things that lived in the background, always there, always “something I should start doing.” I’d get a little momentum, then drop it. The intention was always there, the follow-through wasn’t.&lt;/p&gt;
&lt;p&gt;I used to stay up way too late, usually around 1 or 2 a.m., and wake up at 8:30 or 9 still feeling tired and scattered. I didn’t feel terrible, but I didn’t feel great either. Just a constant sense that I wasn’t really operating at my best - not mentally, not physically, not in how I structured my time.&lt;/p&gt;
&lt;p&gt;Things started to shift in March. I was on a trip to Portugal and spent some time hiking in Madeira. It was a beautiful place, and I really enjoyed the hikes — but I could also feel how out of shape I was. I managed fine, but part of me knew I should be feeling stronger than I did. That trip stuck with me, and when I got home, I decided I wanted to change a few things, not because I hit rock bottom or anything dramatic, but because I finally felt ready to prove to myself that I could stick with something and see it through.&lt;/p&gt;
&lt;p&gt;I started going to gym regularly to begin with. Then I signed up for a trial session with a personal trainer mainly because it was free, and honestly, I didn’t expect much. But once I got there, things felt different. About halfway through, I half joked and half seriously said, “I think I might cry if you increase the weight again.” Without missing a beat, she replied, “You can cry, sure, but you are still doing it.” That moment made me realize we would probably work well together.&lt;/p&gt;
&lt;p&gt;I do push myself sometimes, but I also tend to slip into my comfort zone more often than I’d like. Having someone who encourages me and keeps things challenging—but not overwhelming—has made a big difference. She pushes me, checks in regularly, and keeps me moving forward. This balance of structure and support has really helped me stay focused and committed.&lt;/p&gt;
&lt;p&gt;Since then, I’ve built a routine that actually works. I wake up at 6 a.m. now, which felt impossible not that long ago. I go to the gym five times a week, mainly strength training. I’ve lost around 6kg and feel noticeably stronger. I’ve also gotten back into biking consistently, something I’ve always loved! Being in the Netherlands makes it easy to ride everywhere, and on weekends I’ll go out for long rides. Sometimes solo, sometimes with friends.&lt;/p&gt;
&lt;p&gt;Biking has become more social, which I really enjoy. I even convinced one of my friends to get into it. Riding together has become a fun way to hang out. It’s not just about the workout or clearing my head anymore, it’s also about spending time with people I enjoy. That said, I still love my solo rides. There’s something about being on the road alone, with space to think (or not think at all), that really helps me reset.&lt;/p&gt;
&lt;p&gt;One of the recent highlights was a 152km ride. It was my longest in a while, and I finished feeling strong. If I had fueled better, I’m pretty sure I could’ve gone another 50. That kind of ride is a reminder that progress isn’t just a feeling, it’s something you can see and measure. I’ve got a couple longer rides I want to do soon — Rotterdam to Ghent (~160km) and Rotterdam to Groningen (~265km). They’re not about proving anything to anyone else. They’re just quiet benchmarks to see how far I’ve come.&lt;/p&gt;
&lt;p&gt;These past few months, I’ve also started ending my days with nighttime walks. They’ve become one of my favorite parts of the day — a time to reflect, unwind, and take stock of where I’m at. Sometimes I go alone, other times I’ll have a friend join me and we’ll just talk about whatever’s on our minds. It’s simple, but it’s helped me stay more in tune with myself.&lt;/p&gt;
&lt;p&gt;There’s still more I want to do. I’d like to get more comfortable with running again, keep building strength, maybe add new challenges along the way. But I’m not rushing anything. I’m enjoying how this all fits into my life now, without forcing it.&lt;/p&gt;
&lt;p&gt;The shift didn’t come from turning 26 — that was just a marker I used to get started. What’s kept me going is seeing progress, feeling better in my own body, and actually enjoying the process. I’m not chasing some perfect version of myself. I’m just getting closer to the person I’ve always wanted to be. And for now, that feels like more than enough!&lt;/p&gt;</description></item><item><title>One Year In: A Whirlwind of Small Wonders</title><link>/blog/one-year-in-a-whirlwind-of-small-wonders/</link><pubDate>Fri, 18 Apr 2025 00:00:00 +0000</pubDate><guid>/blog/one-year-in-a-whirlwind-of-small-wonders/</guid><description>&lt;p&gt;I moved to Europe on my birthday last year with suitcases packed, heart full, old housemates seeing me off. It was one of those life shifts that felt equal parts exhilarating and heavy. An ending, a beginning, and the quiet whisper that kept repeating in my head: If not now, when?&lt;/p&gt;
&lt;p&gt;Rotterdam has been home for a year now, and though the streets once felt foreign, I’m slowly building a rhythm. Living alone for the first time wasn’t easy. After years of roommates and shared spaces, the quiet was loud. But over time, I began filling that space - with music, books, plants, and spontaneous social adventures that taught me more about myself than I expected.&lt;/p&gt;
&lt;p&gt;Living alone also meant I had to actively seek connection. I joined meetups, used apps like Timeleft to have dinners with strangers, and nudged myself into unfamiliar social settings. One of the wildest nights started with me saying a quick hello to a friend at an African music festival, despite having just cycled 114 km earlier that day and turned into bar hopping, only to end up spontaneously celebrating a complete stranger’s birthday right after we met. Moments like those made me realize how quickly life was beginning to root itself, even in a new place.&lt;/p&gt;
&lt;p&gt;A few familiar faces from undergrad also live here, and reconnecting with them added another layer of comfort. One friend, whose family still lives in the same apartment complex as mine back home, has known me for over 21 years. Sharing the same streets again, this time in a different country brought a strange kind of full circle.&lt;/p&gt;
&lt;p&gt;My social circle here is wildly eclectic. We’ve had board game marathons, dinners that stretch until dawn, and a very committed Mexican food crawl where we jumped from one taqueria to the next, rating tacos like connoisseurs. It’s random, it’s chaotic, and it’s perfect in its own way.&lt;/p&gt;
&lt;p&gt;Cycling has become a cornerstone of my life here. I finally got myself a proper gravel bike and started exploring the Netherlands with it. These long rides has become a my form of meditation to recharge, escape, and feel connected to this new home.&lt;/p&gt;
&lt;p&gt;&lt;img src="/biking_journey.jpg" alt="Bike on top of a bridge"&gt;&lt;/p&gt;
&lt;p&gt;Professionally, this has been a year of growth. I’ve taken on more responsibility, found genuine enjoyment in my work, and gave a talk at Wasm I/O in Barcelona. I flew in early to hang out with a coworker I’d known for a while, and that extra time gave us a chance to connect outside of meetings and work chat. I also now live closer to my manager in Ghent (my favorite city in Belgium), and our co-working sessions there have doubled as excuses to enjoy a city I can’t seem to get enough of.&lt;/p&gt;
&lt;p&gt;&lt;img src="/wasmio_2025_talk.jpg" alt="Speaking at wasmio"&gt;&lt;/p&gt;
&lt;p&gt;One of the highlights was a spontaneous trip to Portugal, specifically Madeira. The trip was with someone I had only met four months earlier, but we clicked quickly and easily. I tagged along on his vacation because I didn’t have plans of my own, and it turned into one of the most fulfilling experiences I’ve had. It reminded me how beautiful and rewarding life can be when you leave space for the unexpected. It was also my first true vacation since starting full-time work after grad school and now I want more of that blend of travel, presence, and freedom.&lt;/p&gt;
&lt;p&gt;&lt;img src="/madeira.jpg" alt="madeira trail"&gt;&lt;/p&gt;
&lt;p&gt;By the end of 2024, I was feeling the weight of uncertainty trying to have everything figured out. But as 2025 began, something shifted. I started embracing the mess, the unknown, the in-between. I’ve been leaning into the idea of don’t prepone your suffering as a reminder not to overthink what hasn’t happened yet, to give the present the attention it deserves.&lt;/p&gt;
&lt;p&gt;That mindset has created space for other joys. I’ve lost 8 kilos, started going to the gym, and while my flat feet interrupted my attempt at running, I’m hoping to pick it back up someday. I’ve started baking more, and I’m on track to read 30 books this year. The Book of Why and Clear Thinking have stuck with me most. I’ve also learned a lot about financial literacy, something I never prioritized before, but now find deeply grounding.&lt;/p&gt;
&lt;p&gt;My apartment is now home to 42 plants (and counting). My dad helped me tend to them during his visits, while my mom took over the kitchen and cooked for me like I was 12 again. We took a mini tour through Germany and Belgium together, exploring cities and small towns I likely wouldn’t have found alone. Those moments felt like gifts, gently stitched into this bigger, unfamiliar year.&lt;/p&gt;
&lt;p&gt;&lt;img src="/home_jungle.jpg" alt="a bunch of new plants"&gt;&lt;/p&gt;
&lt;p&gt;I also got to visit Christmas markets with my friends. Warm drinks in our hands and freezing air, It felt like walking through a living postcard, one I didn’t know I needed.&lt;/p&gt;
&lt;p&gt;This year, I want more. More travel laptop in hand, new cities as my backdrop. More presence. More exploration maybe pottery, definitely terrariums. More openness. Build deeper bonds. More of the good kind of uncertainty.&lt;/p&gt;
&lt;p&gt;A year ago, I didn’t have a roadmap. I just followed a feeling. Now, on the other side of 12 months filled with randomness, beauty, discomfort, and growth. I see how deeply worthwhile it’s all been.&lt;/p&gt;
&lt;p&gt;No, I still don’t have it all figured out but that the best part!&lt;/p&gt;</description></item><item><title> My Biking journey - Davis to San Francisco</title><link>/blog/my-biking-journey-davis-to-san-francisco/</link><pubDate>Tue, 22 Mar 2022 00:00:00 +0000</pubDate><guid>/blog/my-biking-journey-davis-to-san-francisco/</guid><description>&lt;p&gt;I was never the one to be excited about biking. Biking was just a tool to get from place to place for me. Never in my wildest dreams did I ever expect it to become my favorite hobby. It all changed as I returned to Davis to pursue my master&amp;rsquo;s degree.&lt;/p&gt;
&lt;p&gt;I vividly remember the first time I rode to the campus and back. I was so exhausted by it. It was not even a long ride, it was only 10km (6 miles) in total. Going from that to being able to ride from Davis to San Francisco (150km or 93 miles) has been an interesting journey.&lt;/p&gt;
&lt;h2 id="the-beginning"&gt;The Beginning&lt;/h2&gt;
&lt;p&gt;When I arrived in the USA, I was intrigued when I saw Mike (my host) working on bikes. We started having conversations about different things related to biking. Then he along with Cee (my other host) decided to gift me a road bike that fit me. It is one of the best presents I have ever received. I have never had a bike that fit me properly and never a road bike. That is where my biking as a hobby began.&lt;/p&gt;
&lt;p&gt;I started riding around Davis with Mike. Being in Davis, a bike-friendly town helped enhance my experience. Somewhere along the way, biking had gone from just being a means of getting where I needed to be to something that I actually enjoyed doing. I started riding more often and seeing my interest in biking, Mike helped me upgrade the bike and introduced me to biking gear that helped improve my experience much more.&lt;/p&gt;
&lt;h2 id="the-plan"&gt;The plan&lt;/h2&gt;
&lt;p&gt;Eventually, I started biking to places outside Davis towards the end of 2021. As 2022 was approaching, I made it my goal to bike from Davis to San Francisco during the summer of 2022. I wanted to start training for it consistently. I started riding further each time and more regularly. As I rode, I got to go through some nice routes to places like Winters and Dixon.&lt;/p&gt;
&lt;p&gt;As spring break approached, I was feeling ambitious and decided to attempt going from Davis to San Francisco during the break. It was very ambitious for me as I had not even gone half the distance on a single ride before. I just decided to attempt it, thinking that the worst thing that could happen was me failing it. I decided to go for it as I saw it as a win-win situation, no matter what happened. If I didn&amp;rsquo;t make it, I would know how far I could go and how much I had to improve to accomplish the goal.&lt;/p&gt;
&lt;p&gt;&lt;img src="/Davis2SFroute.png" alt="Rotue from Davis to SF"&gt;&lt;em&gt;bike route from Davis to SF&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="the-journey"&gt;The journey&lt;/h2&gt;
&lt;p&gt;I decided to attempt biking from Davis to San Francisco on March 21st, 2022. It was also helpful that my friends decided to drive down to San Francisco to pick me up and drive me back to Davis. I slept early the previous night so I could start early in the morning. I left the house at 4:50am and started pedaling towards the destination.&lt;/p&gt;
&lt;p&gt;&lt;img src="/Davis2SFride.jpg" alt="Biking early in the morning"&gt;&lt;em&gt;Biking early in the morning&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The first 3 hours of the journey were very smooth, I had gone a little more than a third of the way passing Dixon, Vacaville, and reached Fairfield. The roads were pretty good although it was mostly farm roads. As I reached Fairfield, my rear tire had a puncture and I almost decided the ride was over. But as it was to be, the ride was not over. Mike drove up to me and helped me get back on the journey.&lt;/p&gt;
&lt;p&gt;After I got past Fairfield, the terrain changed from being fairly smooth to being hilly. It was where the ride started being challenging. Hills and bridges have always been my least favorite place to ride. My motivation to make it to San Francisco made me climb the uphill slopes. My effort in climbing the uphill slopes was well worth it as the reward of being able to coast downhill was both exciting and terrifying. I got to descend at around 64kmph (40mph) at some places. The roads were not the greatest and therefore I had to start slowing down when riding downhill for my own safety.&lt;/p&gt;
&lt;p&gt;As time went along, it was interesting for me to note that I wasn&amp;rsquo;t feeling tired cycling but the biggest problem was the fact that I had packed too much into my backpack. It was becoming rather uncomfortable as I had to bike leaning forward. It is a lesson that I have learned that will help me in the future. I had carried too much food thinking I would need it. Google maps didn&amp;rsquo;t help my cause much by leading me to a road that was closed and also leading to a trail via a dirt path.&lt;/p&gt;
&lt;p&gt;I was slowly making it towards San Francisco when disaster struck again. I was in the final stretch and my rear tire blew up again. I was within touching distance, I was 12 miles (20km) from the Golden Gate bridge. Although I was pretty confident and energetic about making it, I had to end the ride due to the unfortunate circumstance. The ride came to an end after 8 hours of riding excluding breaks. I was then picked up by my friends. Then, I made it to San Francisco, although it was by car and not by bike as originally planned. I still see the trip as a win. The day ended with us grabbing dinner and spending some time in San Francisco before returning back to Davis to complete an eventful day!&lt;/p&gt;
&lt;p&gt;&lt;img src="/Davis2SFfinal.jpg" alt="Ending the evening at SF"&gt;&lt;em&gt;Ending the day at SF with my friends&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I want to give my special thanks to David and Crystal for deciding to drive down to San Francisco and then pick me up when I needed it. You guys are lifesavers. From having met not so long ago to driving to San Francisco to help me out, I am glad to have met you. I also want to give a big thanks to Mike and Cee from the bottom of my heart for helping me find my passion for biking and making it possible for me to pursue it.&lt;/p&gt;
&lt;p&gt;It was a fun experience and I learned a lot along the way. It was an entirely new experience and my horizons have expanded giving me the confidence to ride more.&lt;/p&gt;
&lt;p&gt;Pedaling onto the next destination!&lt;/p&gt;</description></item><item><title>Adding push notifications to static sites</title><link>/blog/adding-push-notifications-to-static-sites/</link><pubDate>Sat, 05 Jun 2021 00:00:00 +0000</pubDate><guid>/blog/adding-push-notifications-to-static-sites/</guid><description>&lt;p&gt;While reading about cloudflare workers, I had this thought of can I use this to trigger push notifications for my static site that has a blog. That is what I did. In this post. I will describe the steps involved in creating push notifications using Firebase Cloud Messaging and Cloudflare workers. This post must not be considered as a tutorial but merely as a guide.&lt;/p&gt;
&lt;p&gt;Cloudflare workers allows us to write serverless code that allows us to build API using the edge nodes of cloudflare. We will create an API that will allow the users of the site to subscribe/unsubscribe to new post notifications. We will also use the ability of CRON triggers to automate sending notifications.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://firebase.google.com/docs/cloud-messaging"&gt;FCM&lt;/a&gt; API allows us to send push messages to the clients to update them of new data and this is exactly what we will be using to send notifications when there are new posts in our static site. We will also use their library on the front-end to handle notifications.&lt;/p&gt;
&lt;h2 id="firebase-cloud-messaging"&gt;Firebase Cloud Messaging&lt;/h2&gt;
&lt;p&gt;First we will need to add the library to our web app. They can be included using the script tags and it must be initiated. Refer to the &lt;a href="https://firebase.google.com/docs/cloud-messaging/js/client"&gt;official documentation&lt;/a&gt; for additional information. Make sure to create a project in firebase if you have not already done it.&lt;/p&gt;
&lt;h6 id="indexhtml"&gt;index.html&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.....
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;script&lt;/span&gt; &lt;span style="color:#309"&gt;src&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;https://www.gstatic.com/firebasejs/8.5.0/firebase-app.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;script&lt;/span&gt; &lt;span style="color:#309"&gt;src&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;https://www.gstatic.com/firebasejs/8.6.2/firebase-messaging.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;....
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h6 id="indexjs"&gt;index.js&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// Your web app&amp;#39;s Firebase configuration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// For Firebase JS SDK v7.20.0 and later, measurementId is optional
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;var&lt;/span&gt; firebaseConfig &lt;span style="color:#555"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#09f;font-style:italic"&gt;/* enter your firebase project details */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;firebase.initializeApp(firebaseConfig)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// Using a redirect.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also create a file named &amp;ldquo;firebase-messaging-sw.js&amp;rdquo;. This file will contain the service worker that will handle sending notifications when it receives data from FCM. Initialize it with the following contents. We will fill in the rest later.&lt;/p&gt;
&lt;h6 id="firebase-messaging-swjs"&gt;firebase-messaging-sw.js&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;importScripts(&lt;span style="color:#c30"&gt;&amp;#39;https://www.gstatic.com/firebasejs/8.6.2/firebase-app.js&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;importScripts(&lt;span style="color:#c30"&gt;&amp;#39;https://www.gstatic.com/firebasejs/8.6.2/firebase-messaging.js&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; firebaseConfig &lt;span style="color:#555"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#09f;font-style:italic"&gt;/* enter your firebase project configurations */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;firebase.initializeApp(firebaseConfig)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; messaging &lt;span style="color:#555"&gt;=&lt;/span&gt; firebase.messaging()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that the initial setup of firebase is done in the client let us understand how FCM works. So when a client wants to show notifications, it requires the user to allow notifications on the website. Once the notification permission is avaibale, the messaging library of firebase can create a registration token for the user. This token is then shared with the server. The token can be used to send messages to specific devices or can be registed with FCM notifications to subscribe to a topic.&lt;/p&gt;
&lt;p&gt;We will Be registering the token on a topic since, it is a common notification that we desire to all the users. So now let us add the functionality to create the registration token. The vapidKey is available in the cloud messaging tab of the project settings in firebase console.&lt;/p&gt;
&lt;p&gt;&lt;img src="/FCM.jpg" alt="Firebase Cloud Messaging console"&gt;&lt;em&gt;FCM console&lt;/em&gt;&lt;/p&gt;
&lt;h6 id="indexjs-1"&gt;index.js&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; notificationToken
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; notificationStatus &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; getToken &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; token &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; messaging.getToken({ &lt;span style="color:#c30"&gt;&amp;#39;&amp;lt;YOUR_PUBLIC_VAPID_KEY_HERE&amp;gt;&amp;#39;&lt;/span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; token
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; main &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (Notification.permission &lt;span style="color:#555"&gt;!=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;blocked&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notificationToken &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; getToken()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(notificationToken)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;main()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This script will now initially check if the notification permissions are blocked, else it will trigger the firebase function to get the token. If the permissions for notification is not granted, it will request for it. It is better to trigger the &lt;code&gt;getToken&lt;/code&gt; on the request of the user, this is just to show the process.&lt;/p&gt;
&lt;p&gt;Now we have the FCM registration token, we can look at the FCM API. We will be needing 4 APIs to get push notifications to work. We will need an API for registering and unregistering a user to topic, an API to check the status of the user and finally and most importantly the API that will allow us to push notifications to specific topics.&lt;/p&gt;
&lt;p&gt;The reason we need to use REST APIs and not the firebase-admin package in nodejs is because that pacakge cannot be used in cloudflare workers as it depends on certain node specific features which are not available on workers.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// FCM API
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// get user status
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// SERVER_KEY available in firebase cloud messaging tab
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// IID Token is the firebase messaging registration token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// Return an object on success, we are interested in the &amp;#34;rel&amp;#34; key containing the topics subscribed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;statusURL &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;https://iid.googleapis.com/iid/info/&amp;lt;FCM_REGISTRATION_TOKEN&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;statusParameters &lt;span style="color:#555"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#555"&gt;:&lt;/span&gt; {&lt;span style="color:#c30"&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;key=&amp;lt;SERVER_KEY&amp;gt;&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// Sub user
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// API_KEY is the same as SERVER_KEY
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// TOPIC is the name to which we want to subscribe the user to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; suburl &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;https://iid.googleapis.com/iid/v1:batchAdd&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;subParameters &lt;span style="color:#555"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;headers&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;key=&amp;lt;API_KEY&amp;gt;&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;to&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;&amp;lt;TOPIC&amp;gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;registration_tokens&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; [&lt;span style="color:#c30"&gt;&amp;#34;&amp;lt;FCM_REGISTRATION_TOKEN&amp;gt;&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;method&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// unSub user
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// API_KEY is the same as SERVER_KEY
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// TOPIC is the name to which we want to subscribe the user to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; unsubURL &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;https://iid.googleapis.com/iid/v1:batchRemove&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;unsubParameters &lt;span style="color:#555"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;headers&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;key=&amp;lt;API_KEY&amp;gt;&amp;#34;&lt;/span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;to&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;&amp;lt;TOPIC&amp;gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;registration_tokens&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; [&lt;span style="color:#c30"&gt;&amp;#34;&amp;lt;FCM_REGISTRATION_TOKEN&amp;gt;&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;method&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// Send notification
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// GOOGLE_OAUTH_ACCESS_TOKEN is needed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; topicNotificationURL &lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;https://fcm.googleapis.com/v1/projects/329859626245/messages:send&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; topicNotificationParameter &lt;span style="color:#555"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; { &lt;span style="color:#c30"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; data.title, &lt;span style="color:#c30"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;New Post!&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;image&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;https://karthikganeshram.in&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; data.image, &lt;span style="color:#c30"&gt;&amp;#34;tag&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; data.tag, &lt;span style="color:#c30"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;https://karthikganeshram.in&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; data.url},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;topic&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;newPost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Authorization&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Bearer &amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; GOOGLE_OAUTH_ACCESS_TOKEN,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I decided to use the batch api for add and remove as I was not able to find the remove api for a single token and wanted to maintain consitency in the API. Now we can test out the commands using curl. We have everything required to test the API except the &lt;code&gt;GOOGLE_OAUTH_ACCESS_TOKEN&lt;/code&gt;. The method to obtain it will be explained soon. In the meantime you can use the &amp;ldquo;Notifcation composer&amp;rdquo; in firebase console to send test messages after you get the registration token.&lt;/p&gt;
&lt;h2 id="cloudflare-workers"&gt;Cloudflare workers&lt;/h2&gt;
&lt;p&gt;Now it is time to move onto workers. It is assumed that the reader has some familiarity with the basics of cloudflare workers. If not don&amp;rsquo;t worry, the &lt;a href="https://developers.cloudflare.com/workers/"&gt;documentation and examples&lt;/a&gt; will get you started. As explained earlier we will have to use REST APIs instead of the firebase-admin package as it is not compatible. Therefore we must manually retrieve the Oauth token for the google api. This excellent example from &lt;a href="https://community.cloudflare.com/t/example-google-oauth-2-0-for-service-accounts-using-cf-worker/258220"&gt;cloudflare community&lt;/a&gt; will get us started. Now that we are able to retrieve google Oauth tokens, we can use curl to test the apis from the command line.&lt;/p&gt;
&lt;p&gt;Let us build the API for the in the workers that our website will use. We will have 3 rest API end points. One each for subStatus, sub and unsub. We will also add a function to send the notification. These are the 4 apis described above. We will also need to add worker secrets for SERVER_KEY and email address of service account in firebase and the private key.&lt;/p&gt;
&lt;h6 id="indexjscloudflare-worker"&gt;index.js(cloudflare worker)&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; getToken(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#09f;font-style:italic"&gt;/* Use the function provided in the example above and return the token*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; subStatus(data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(data.token)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; res &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; fetch(&lt;span style="color:#c30"&gt;&amp;#34;https://iid.googleapis.com/iid/info/&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; data.token &lt;span style="color:#555"&gt;+&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;?details=true&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#555"&gt;:&lt;/span&gt; { Authorization&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;key=&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; SERVER_KEY },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#555"&gt;!&lt;/span&gt;res.ok) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; { results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;failed&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; data &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; res.json()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; { results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;success&amp;#34;&lt;/span&gt;, subStatus&lt;span style="color:#555"&gt;:&lt;/span&gt; (data.rel&lt;span style="color:#555"&gt;?&lt;/span&gt;.topics&lt;span style="color:#555"&gt;?&lt;/span&gt;.newPost &lt;span style="color:#555"&gt;?&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;true&lt;/span&gt; &lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;false&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; subUnsub(data, action) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(data.token)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; res &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; fetch(&lt;span style="color:#c30"&gt;&amp;#34;https://iid.googleapis.com/iid/v1:batch&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; action, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#555"&gt;:&lt;/span&gt; JSON.stringify({ &lt;span style="color:#c30"&gt;&amp;#34;to&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;/topics/newPost&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;registration_tokens&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; [data.token] }),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Authorization&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;key=&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; SERVER_KEY,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#555"&gt;!&lt;/span&gt;res.ok) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; { results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;failed&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; { results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;success&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; sendNotification(data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; res &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; fetch(&lt;span style="color:#c30"&gt;&amp;#34;https://fcm.googleapis.com/v1/projects/329859626245/messages:send&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#555"&gt;:&lt;/span&gt; JSON.stringify({
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;message&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; { &lt;span style="color:#c30"&gt;&amp;#34;body&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; data.title, &lt;span style="color:#c30"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;New Post!&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;image&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; data.image, &lt;span style="color:#c30"&gt;&amp;#34;tag&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; data.tag, &lt;span style="color:#c30"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; data.url},
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;topic&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;newPost&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Authorization&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Bearer &amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; GOOGLE_OAUTH_ACCESS_TOKEN,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#555"&gt;!&lt;/span&gt;res.ok) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; { results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;failed&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; { results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;success&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; handleRequest(request) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#c30"&gt;&amp;#34;here&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; url &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;new&lt;/span&gt; URL(request.url)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; response
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (request.method &lt;span style="color:#555"&gt;===&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; { headers } &lt;span style="color:#555"&gt;=&lt;/span&gt; request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (headers.get(&lt;span style="color:#c30"&gt;&amp;#34;content-type&amp;#34;&lt;/span&gt;) &lt;span style="color:#555"&gt;==&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;switch&lt;/span&gt; (url.pathname) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;/api/notify/sub&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; subUnsub((&lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; request.json()), &lt;span style="color:#c30"&gt;&amp;#34;Add&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;/api/notify/unsub&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; subUnsub((&lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; request.json()), &lt;span style="color:#c30"&gt;&amp;#34;Remove&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;case&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;/api/notify/subStatus&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; subStatus(&lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; request.json())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response &lt;span style="color:#555"&gt;=&lt;/span&gt; {results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Invalid URL&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;new&lt;/span&gt; Response(JSON.stringify(response), {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#555"&gt;:&lt;/span&gt; { &lt;span style="color:#c30"&gt;&amp;#39;content-type&amp;#39;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#39;Access-Control-Allow-Origin&amp;#39;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#39;Access-Control-Allow-Credentials&amp;#39;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;true&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;addEventListener(&lt;span style="color:#c30"&gt;&amp;#34;fetch&amp;#34;&lt;/span&gt;, event =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; request &lt;span style="color:#555"&gt;=&lt;/span&gt; event.request
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; request.method &lt;span style="color:#555"&gt;===&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#c30"&gt;&amp;#34;here&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event.respondWith(handleRequest(request))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event.respondWith(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;new&lt;/span&gt; Response(&lt;span style="color:#069;font-weight:bold"&gt;null&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; status&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#f60"&gt;405&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; statusText&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Method Not Allowed&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You may notice that the &lt;code&gt;sendNotificaion&lt;/code&gt; function is not being used. If you want to keep it simple and send the push notifications manually, you can add an api endpoint for the push notification but we will go one step further and automate it using CRON triggers so it will become clearer in just a bit. Now how do we automatically detect if the website has been updated. Well let us find out.&lt;/p&gt;
&lt;p&gt;We need to create a new page on our website which will be a static website that will be updated with the details of the last post along with the time it was updated. We will use CRON triggers on the cloudflare worker to check periodically if there was a new post. If there is a new post we push notifications.&lt;/p&gt;
&lt;p&gt;Now to compare if the date specified on the api is newer than the last notification, we must be able to store global variables in the worker script, that is where cloudflare kv comes in. Let us create a namespace for kv. Namespaces in cloudflare are nothing but a way to reference the objects in the worker script. Run the following command in the command line and save its output to &amp;ldquo;wrangler.toml&amp;rdquo;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wrangler kv&lt;span style="color:#555"&gt;:&lt;/span&gt;namespace create &lt;span style="color:#c30"&gt;&amp;#34;NOTIFICATIONS_KV_DB&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let us add a function to check if there is a newer post. I use github pages for my static site so I used the raw user content file from the repository as my api. The return of the API is a JSON that has the required parameters. An example response is as below.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#c30"&gt;&amp;#34;Date&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#f60"&gt;1623083600005&lt;/span&gt;,&lt;span style="color:#c30"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Be selfish to be selfless&amp;#34;&lt;/span&gt;,&lt;span style="color:#c30"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;/blog/posts/be-selfish-to-be-selfless/&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;image&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;/manInSunset.jpg&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;tag&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Life&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h6 id="indexjscloudflare-worker-1"&gt;index.js(cloudflare worker)&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; setCache &lt;span style="color:#555"&gt;=&lt;/span&gt; data =&amp;gt; NOTIFICATIONS_KV_DB.put(&lt;span style="color:#c30"&gt;&amp;#34;lastPostTime&amp;#34;&lt;/span&gt;, data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; getCache &lt;span style="color:#555"&gt;=&lt;/span&gt; () =&amp;gt; NOTIFICATIONS_KV_DB.get(&lt;span style="color:#c30"&gt;&amp;#34;lastPostTime&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; checkLatestPost() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; cache &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; getCache()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#555"&gt;!&lt;/span&gt;cache) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#c30"&gt;&amp;#34;settimg cache&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastBlogPost &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#f60"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastBlogPost &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;parseInt&lt;/span&gt;(cache)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; res &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; fetch(&lt;span style="color:#c30"&gt;&amp;#34;&amp;lt;Static JSON API&amp;gt;&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (&lt;span style="color:#555"&gt;!&lt;/span&gt;res.ok) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; { results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;failed&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; test &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; res.text()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#c30"&gt;&amp;#34;succeded &amp;#34;&lt;/span&gt;, res.status, JSON.parse(test))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; data &lt;span style="color:#555"&gt;=&lt;/span&gt; JSON.parse(test)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (lastBlogPost &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt; data.&lt;span style="color:#366"&gt;Date&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; GOOGLE_OAUTH_ACCESS_TOKEN &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; getToken()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; response &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; sendNotification(data)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (response.results &lt;span style="color:#555"&gt;===&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;success&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastBlogPost &lt;span style="color:#555"&gt;=&lt;/span&gt; data.&lt;span style="color:#366"&gt;Date&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; setCache(JSON.stringify(data.&lt;span style="color:#366"&gt;Date&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; { results&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;success&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;addEventListener(&lt;span style="color:#c30"&gt;&amp;#34;scheduled&amp;#34;&lt;/span&gt;, event =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event.waitUntil(checkLatestPost(event))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now create a CRON trigger using wrangler with the appropriate duration.&lt;/p&gt;
&lt;h2 id="eleventy-static-api"&gt;Eleventy Static API&lt;/h2&gt;
&lt;p&gt;Creating a static API with eleventy is rather straightforward. We just need to create a template file similar to&lt;/p&gt;
&lt;h6 id="lastestpostjson"&gt;lastestPost.json&lt;/h6&gt;
&lt;p&gt;{% raw %}&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;---&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;permalink&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;/api/latestPost.json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;---&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#555"&gt;%&lt;/span&gt; set latestPost &lt;span style="color:#555"&gt;=&lt;/span&gt; collections[&lt;span style="color:#c30"&gt;&amp;#34;post&amp;#34;&lt;/span&gt;].reverse()[&lt;span style="color:#f60"&gt;0&lt;/span&gt;] &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#c30"&gt;&amp;#34;Date&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#f60"&gt;1623083600005&lt;/span&gt;,&lt;span style="color:#c30"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;{{latestPost.data.title}}&amp;#34;&lt;/span&gt;,&lt;span style="color:#c30"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{latestPost.url}}&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;image&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{latestPost.data.mainImage}}&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;tag&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;{{latestPost.data.tags[1]}}&amp;#34;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#555"&gt;%&lt;/span&gt; endraw &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="handling-notifications-on-client"&gt;Handling notifications on client&lt;/h2&gt;
&lt;p&gt;Now that we have all the parts working, We now add the functions to add/remove user from topic and also check the status of the user. I will let the process of calling them based on the action of the users upto you. we can create the event handlers to handle the notification when it arrives.&lt;/p&gt;
&lt;h6 id="indexjs-2"&gt;index.js&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; enableNotifications &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (Notification.permission &lt;span style="color:#555"&gt;===&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;granted&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notificationToken &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; getToken()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; subscribteTopic()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (Notification.permission &lt;span style="color:#555"&gt;===&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;blocked&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; alert(&lt;span style="color:#c30"&gt;&amp;#34;Notification permissions have been blocked, enable notifications permission manually&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; alert(&lt;span style="color:#c30"&gt;&amp;#34;Now you will be asked for notification permissions&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notificationToken &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; getToken()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; subscribteTopic()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; subscribteTopic() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; subUnsub(&lt;span style="color:#c30"&gt;&amp;#34;sub&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; disableNotifications &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notificationToken &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; getToken()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; subUnsub(&lt;span style="color:#c30"&gt;&amp;#34;unsub&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; subUnsub &lt;span style="color:#555"&gt;=&lt;/span&gt; (action) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fetch(notifyApiUrl &lt;span style="color:#555"&gt;+&lt;/span&gt; action, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#555"&gt;:&lt;/span&gt; JSON.stringify({ &lt;span style="color:#c30"&gt;&amp;#34;token&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; notificationToken }),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#555"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#c30"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }).then(res =&amp;gt; res.json()).then(data =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (data.results &lt;span style="color:#555"&gt;===&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;success&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#c30"&gt;&amp;#34;success&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; notificationStatus &lt;span style="color:#555"&gt;=&lt;/span&gt; (action &lt;span style="color:#555"&gt;===&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;sub&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;?&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;true&lt;/span&gt; &lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;false&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; subStatus() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; fetch(notifyApiUrl &lt;span style="color:#555"&gt;+&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;subStatus&amp;#34;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#555"&gt;:&lt;/span&gt; JSON.stringify({ &lt;span style="color:#c30"&gt;&amp;#34;token&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; notificationToken }),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; headers&lt;span style="color:#555"&gt;:&lt;/span&gt; { &lt;span style="color:#c30"&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;application/json&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; method&lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;await&lt;/span&gt; res.json()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; data
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;messaging.onMessage((payload) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#c30"&gt;&amp;#39;Message received. &amp;#39;&lt;/span&gt;, payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now finally to handling the notifications in the service worker. We create a notification object with the data that we receive from the push notification.&lt;/p&gt;
&lt;p&gt;&lt;img src="/mobNotif.jpg" alt="Mobile push notification"&gt;&lt;em&gt;Push notification on mobile&lt;/em&gt;&lt;/p&gt;
&lt;h6 id="firebase-messaging-swjs-1"&gt;firebase-messaging-sw.js&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;messaging.setBackgroundMessageHandler((payload) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#c30"&gt;&amp;#39;[firebase-messaging-sw.js] Received background message &amp;#39;&lt;/span&gt;, payload);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#09f;font-style:italic"&gt;// Customize notification here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; notificationTitle &lt;span style="color:#555"&gt;=&lt;/span&gt; payload.data.title;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;const&lt;/span&gt; notificationOptions &lt;span style="color:#555"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; body&lt;span style="color:#555"&gt;:&lt;/span&gt; payload.data.body,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image&lt;span style="color:#555"&gt;:&lt;/span&gt; payload.data.image,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; myUrl &lt;span style="color:#555"&gt;=&lt;/span&gt; payload.data.url;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self.addEventListener(&lt;span style="color:#c30"&gt;&amp;#39;notificationclick&amp;#39;&lt;/span&gt;, &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt;(event) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event.waitUntil(self.clients.openWindow(myUrl));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event.notification.close();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; self.registration.showNotification(notificationTitle,notificationOptions);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And voilà, there we have working push notifications whenever there is a new post on my website. I will add some common issues you may face.&lt;/p&gt;
&lt;h3 id="note"&gt;Note&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;CORS error when using testing with localhost. The &lt;a href="https://support.cloudflare.com/hc/en-us/articles/200308847-Using-cross-origin-resource-sharing-CORS-with-Cloudflare"&gt;cloudflare example&lt;/a&gt; will help.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need any futher detail or the post is missing, please leave a comment or contact me via my &lt;a href="mailto:raams.karthik@gmail.com"&gt;email&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Be selfish to be selfless</title><link>/blog/be-selfish-to-be-selfless/</link><pubDate>Thu, 27 May 2021 00:00:00 +0000</pubDate><guid>/blog/be-selfish-to-be-selfless/</guid><description>&lt;p&gt;Be selfish to be selfless. Aren’t both the words the antonyms of each other? What is this guy on?&lt;/p&gt;
&lt;p&gt;Even though the statement looks contradictory, looking at it on a deeper level reveals the exquisite intuitiveness it possesses. That is the purpose of this post, to look at the statement on a deeper level and express my views on it.&lt;/p&gt;
&lt;p&gt;Although one might suggest that being a selfless person is the ultimate goal, I would argue that one cannot be a selfless person without being selfish first. In short I would say it means that the more you know yourself, the more you can give.&lt;/p&gt;
&lt;p&gt;Every individual is unique, no other person in the world will share the exact same interests or do things the exact same way. In your journey of understanding yourself, it is required of you to be true to yourself.&lt;/p&gt;
&lt;p&gt;When you are true to yourself, you are being true to the rest of the world. Being true to yourself is the best thing a person can do as you are representing yourself to others as the true individual you are. People get to like you or dislike you for your true self.&lt;/p&gt;
&lt;p&gt;Being selfish here means that you remain true to yourself and do the things that help you learn about yourself. Once you understand yourself, you start understanding what you have that you can provide and allow you to be truly selfless.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The mark of the immature man is that he wants to die nobly for a cause, while the mark of the mature man is that he wants to live humbly for one.
&lt;br&gt;&lt;em&gt;- Wilhelm Stekel&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I also believe that being truly selfless requires a person to find their own happiness first before helping others find their happiness. Unless you are truly happy, you will give only with an expectation of something in return that makes you happy and this makes your selfless act into a selfish act.&lt;/p&gt;
&lt;p&gt;Therefore one must look to fill their life with happiness first before they can help fill happiness in other people’s lives.&lt;/p&gt;
&lt;p&gt;To be truly selfless, one must be able to give without any expectations. It is the expectations that kill true selflessness. Being selfish is the path to true selflessness.&lt;/p&gt;
&lt;p&gt;Until next time, be a little selfish to be a lot more selfless.&lt;/p&gt;</description></item><item><title>Creating content cards using CSS grids in eleventy</title><link>/blog/creating-content-cards-using-css-grids-in-eleventy/</link><pubDate>Sat, 22 May 2021 00:00:00 +0000</pubDate><guid>/blog/creating-content-cards-using-css-grids-in-eleventy/</guid><description>&lt;p&gt;I have always avoided CSS grids and preferred using flexboxes in places where grids would have been the better choice. I finally decided to overcome the fear and finally explore it. In this short post, we will implement a content card using CSS grid which can be used for displaying a list of latest posts or suggest similar posts in an eleventy blog. It should be straightforward to convert it to the other Static site generators.&lt;/p&gt;
&lt;p&gt;We will create a component in nunjucks that takes an input array of eleventy posts and creates a card listing. The component will also optionally take a value that decides if the component places the card in a single row with horizontal scrolling support or allows the listing to span multiple lines.&lt;/p&gt;
&lt;p&gt;We first create the nunjucks template for the suggestion cards.&lt;/p&gt;
&lt;h6 id="suggestedpostsnjk"&gt;suggestedPosts.njk&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#555"&gt;%&lt;/span&gt; raw &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;div &lt;span style="color:#069;font-weight:bold"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;suggested-postsList {% if suggestedPostScrollHorizontal %} grid-horizontal-scroll {% endif %}&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#555"&gt;%&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;for&lt;/span&gt; post &lt;span style="color:#069;font-weight:bold"&gt;in&lt;/span&gt; suggestedPosts &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;a &lt;span style="color:#069;font-weight:bold"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;suggested-item&amp;#34;&lt;/span&gt; href&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{post.url}}&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;img src&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{post.data.mainImage}}&amp;#34;&lt;/span&gt; loading&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;lazy&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;div &lt;span style="color:#069;font-weight:bold"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;content flex flex-column w-100 h-100&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;h2 &lt;span style="color:#069;font-weight:bold"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;pa1&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;{{post.data.title &lt;span style="color:#555"&gt;|&lt;/span&gt; trimTitle }}&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/h2&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;div &lt;span style="color:#069;font-weight:bold"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;description flex-grow-1&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;{{post.data.content &lt;span style="color:#555"&gt;|&lt;/span&gt; trimContent &lt;span style="color:#555"&gt;|&lt;/span&gt; safe }}&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;div &lt;span style="color:#069;font-weight:bold"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;date&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {{post.date &lt;span style="color:#555"&gt;|&lt;/span&gt; formatPostDate }}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;span aria&lt;span style="color:#555"&gt;-&lt;/span&gt;hidden&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;⋅&lt;/span&gt;&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/span&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#555"&gt;%-&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;for&lt;/span&gt; tag &lt;span style="color:#069;font-weight:bold"&gt;in&lt;/span&gt; post.data.tags &lt;span style="color:#555"&gt;-%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#555"&gt;%-&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; tag &lt;span style="color:#555"&gt;!=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;post&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;-%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;span&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {{ tag }}&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/span&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#555"&gt;%-&lt;/span&gt; endif &lt;span style="color:#555"&gt;-%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#555"&gt;%-&lt;/span&gt; endfor &lt;span style="color:#555"&gt;-%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/a&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#555"&gt;%&lt;/span&gt; endfor &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/div&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#555"&gt;%&lt;/span&gt; endraw &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &amp;ldquo;suggested-postsList&amp;rdquo; div is the container for the list of cards. The configurable option that will scroll the grid horizontally instead of shifting the cards to a new row can be set by setting the value of the &lt;em&gt;suggestedPostScrollHorizontal&lt;/em&gt; to true using the nunjucks set directive in the parent layout.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#555"&gt;%&lt;/span&gt; raw &lt;span style="color:#555"&gt;%&lt;/span&gt;}{&lt;span style="color:#555"&gt;%&lt;/span&gt; set suggestedPostScrollHorizontal &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;true&lt;/span&gt; &lt;span style="color:#555"&gt;%&lt;/span&gt;}{&lt;span style="color:#555"&gt;%&lt;/span&gt; endraw &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;em&gt;SuggestedPosts&lt;/em&gt; is an array of eleventy posts set in the parent layout and all the posts in this variable are listed by the component. Therefore, any filtering of the content must be done before the array is before the component is called.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#555"&gt;%&lt;/span&gt; raw &lt;span style="color:#555"&gt;%&lt;/span&gt;}{&lt;span style="color:#555"&gt;%&lt;/span&gt; set suggestedPosts &lt;span style="color:#555"&gt;=&lt;/span&gt; collections[&lt;span style="color:#c30"&gt;&amp;#34;post&amp;#34;&lt;/span&gt;].slice(&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;6&lt;/span&gt;).reverse() &lt;span style="color:#555"&gt;%&lt;/span&gt;}{&lt;span style="color:#555"&gt;%&lt;/span&gt; endraw &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we are done with the nunjucks template, let us add the CSS for the component to complete it. The CSS is pretty straightforward. It utilizes a simple CSS grid with an image covering the entire span of each component. It is necessary to add an overlay over the image to make the text on the card readable. We apply it using gradients. The smaller text sizes need a darker gradient and hence we add a second background gradient on those elements. To add for some interactivity the post content is visible only on hover.&lt;/p&gt;
&lt;h6 id="indexcss"&gt;index.css&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggest-card&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;width&lt;/span&gt;: &lt;span style="color:#f60"&gt;100&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-postsList&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;display&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;grid&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;grid-template-columns&lt;/span&gt;: &lt;span style="color:#c0f"&gt;repeat&lt;/span&gt;(&lt;span style="color:#069;font-weight:bold"&gt;auto&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;fill&lt;/span&gt;,&lt;span style="color:#c0f"&gt;minmax&lt;/span&gt;(&lt;span style="color:#f60"&gt;245&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt;,&lt;span style="color:#f60"&gt;1&lt;/span&gt;fr));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;grid-auto-columns&lt;/span&gt;: &lt;span style="color:#f60"&gt;300&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grid-gap: &lt;span style="color:#f60"&gt;2&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;grid-horizontal-scroll&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;grid-auto-flow&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;column&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;overflow-x&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;auto&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;height&lt;/span&gt;: &lt;span style="color:#f60"&gt;300&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;position&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;relative&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;border-radius&lt;/span&gt;: &lt;span style="color:#f60"&gt;10&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;overflow&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;hidden&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt;::&lt;span style="color:#99f"&gt;before&lt;/span&gt;{&lt;span style="color:#069;font-weight:bold"&gt;display&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;none&lt;/span&gt;;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;img&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;z-index&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;position&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;absolute&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;width&lt;/span&gt;: &lt;span style="color:#f60"&gt;100&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;height&lt;/span&gt;: &lt;span style="color:#f60"&gt;100&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;top&lt;/span&gt;: &lt;span style="color:#f60"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;left&lt;/span&gt;: &lt;span style="color:#f60"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;object-fit&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;cover&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;filter&lt;/span&gt;: &lt;span style="color:#366"&gt;blur&lt;/span&gt;(&lt;span style="color:#f60"&gt;1&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;transition&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;all&lt;/span&gt; &lt;span style="color:#f60"&gt;0.2&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;s&lt;/span&gt; &lt;span style="color:#366"&gt;cubic-bezier&lt;/span&gt;(&lt;span style="color:#f60"&gt;.4&lt;/span&gt;, &lt;span style="color:#f60"&gt;0&lt;/span&gt;, &lt;span style="color:#f60"&gt;.2&lt;/span&gt;, &lt;span style="color:#f60"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;content&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;z-index&lt;/span&gt;: &lt;span style="color:#f60"&gt;2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;width&lt;/span&gt;: &lt;span style="color:#f60"&gt;100&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;white&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;position&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;relative&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;background&lt;/span&gt;: &lt;span style="color:#366"&gt;linear-gradient&lt;/span&gt;(&lt;span style="color:#069;font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;right&lt;/span&gt;, &lt;span style="color:#366"&gt;rgba&lt;/span&gt;(&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0.4&lt;/span&gt;) , &lt;span style="color:#366"&gt;rgba&lt;/span&gt;(&lt;span style="color:#f60"&gt;0&lt;/span&gt;, &lt;span style="color:#f60"&gt;0&lt;/span&gt;, &lt;span style="color:#f60"&gt;0&lt;/span&gt;, &lt;span style="color:#f60"&gt;0.4&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;content&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;h2&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;font-size&lt;/span&gt;: &lt;span style="color:#f60"&gt;1.25&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;margin&lt;/span&gt;: &lt;span style="color:#f60"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;content&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;background&lt;/span&gt;: &lt;span style="color:#366"&gt;linear-gradient&lt;/span&gt;(&lt;span style="color:#069;font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;bottom&lt;/span&gt;, &lt;span style="color:#366"&gt;rgba&lt;/span&gt;(&lt;span style="color:#f60"&gt;2&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;36&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;) &lt;span style="color:#f60"&gt;0&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;%&lt;/span&gt;, &lt;span style="color:#366"&gt;rgba&lt;/span&gt;(&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0.12&lt;/span&gt;) &lt;span style="color:#f60"&gt;10&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;%&lt;/span&gt;, &lt;span style="color:#366"&gt;rgba&lt;/span&gt;(&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0.2&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;font-size&lt;/span&gt;: &lt;span style="color:#f60"&gt;0.9&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;line-height&lt;/span&gt;: &lt;span style="color:#f60"&gt;1.2&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;white&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;margin&lt;/span&gt;: &lt;span style="color:#f60"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;transition&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;all&lt;/span&gt; &lt;span style="color:#f60"&gt;0.2&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;s&lt;/span&gt; &lt;span style="color:#366"&gt;cubic-bezier&lt;/span&gt;(&lt;span style="color:#f60"&gt;.4&lt;/span&gt;, &lt;span style="color:#f60"&gt;0&lt;/span&gt;, &lt;span style="color:#f60"&gt;.2&lt;/span&gt;, &lt;span style="color:#f60"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;overflow&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;hidden&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;padding&lt;/span&gt;:&lt;span style="color:#f60"&gt;1&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;content&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;description&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;opacity&lt;/span&gt;: &lt;span style="color:#f60"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;transform&lt;/span&gt;: &lt;span style="color:#366"&gt;translateY&lt;/span&gt;(&lt;span style="color:#f60"&gt;10&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;content&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;date&lt;/span&gt;{&lt;span style="color:#069;font-weight:bold"&gt;opacity&lt;/span&gt;: &lt;span style="color:#f60"&gt;0.8&lt;/span&gt;;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt;:&lt;span style="color:#99f"&gt;hover&lt;/span&gt; &lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;content&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;date&lt;/span&gt;{&lt;span style="color:#069;font-weight:bold"&gt;background&lt;/span&gt;: &lt;span style="color:#366"&gt;linear-gradient&lt;/span&gt;(&lt;span style="color:#069;font-weight:bold"&gt;to&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;bottom&lt;/span&gt;, &lt;span style="color:#366"&gt;rgba&lt;/span&gt;(&lt;span style="color:#f60"&gt;2&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;36&lt;/span&gt;,&lt;span style="color:#f60"&gt;0.2&lt;/span&gt;), &lt;span style="color:#366"&gt;rgba&lt;/span&gt;(&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0&lt;/span&gt;,&lt;span style="color:#f60"&gt;0.2&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;content&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;p&lt;/span&gt;{&lt;span style="color:#069;font-weight:bold"&gt;margin&lt;/span&gt;: &lt;span style="color:#f60"&gt;0&lt;/span&gt;;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt;:&lt;span style="color:#99f"&gt;hover&lt;/span&gt; &lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;content&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;description&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;opacity&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;transform&lt;/span&gt;: &lt;span style="color:#366"&gt;translateY&lt;/span&gt;(&lt;span style="color:#f60"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;suggested-item&lt;/span&gt;:&lt;span style="color:#99f"&gt;hover&lt;/span&gt; &lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;img&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;filter&lt;/span&gt;: &lt;span style="color:#366"&gt;blur&lt;/span&gt;(&lt;span style="color:#f60"&gt;2&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;transform&lt;/span&gt;: &lt;span style="color:#366"&gt;scale&lt;/span&gt;(&lt;span style="color:#f60"&gt;1.1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that we have the entire component ready, it can be used to display the latest posts or whatever one can imagine in the form of cards. A couple of examples of the components with and without horizontal scrolling can be seen below.&lt;/p&gt;
&lt;video controls autoplay&gt;
&lt;source src="/card-scroll.webm" type="video/webm"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;p&gt;You may also find it at the end of this post if you are reading on a smaller screen.&lt;/p&gt;
&lt;p&gt;Until next time, ciao!&lt;/p&gt;</description></item><item><title>Why strong opinions matter</title><link>/blog/why-strong-opinions-matter/</link><pubDate>Mon, 17 May 2021 00:00:00 +0000</pubDate><guid>/blog/why-strong-opinions-matter/</guid><description>&lt;p&gt;Over the course of my journey of self improvement, I have realized that my ultimate goal in life is to be the best version of myself that I can be. The process that I must adopt to achieve the goal is to be a little less wrong tomorrow than I am today. To do that, I need to assess who I am today, and who I want to be tomorrow. To know the latter, I need to have strong opinions on what is right and what is not.&lt;/p&gt;
&lt;p&gt;Nothing good ever came from meekly following the general consensus unquestioningly. All the advancements we have today were all born from a single question - &amp;ldquo;Why?&amp;rdquo; Aristarchus believed that the Sun was at the centre of the universe. He believed that at a time when everyone was certain that everything revolved around Earth. Today, we know better than Aristarchus, but only because people dared to challenge him.&lt;/p&gt;
&lt;p&gt;Having strong opinions provides people a helping hand in their quest for truth. However, it must be mentioned that having strong opinions without being open to criticism and introspection causes more harm than good. Keeping an open mind and being ready to change your opinions when they have been proven to be wrong is healthier than being steadfast in your beliefs.&lt;/p&gt;
&lt;p&gt;To establish a strong opinion, one must be brutally honest to themselves. Arriving at a strong opinion requires the individual to be able to make a series of logical arguments to justify their conclusion. The process of constructing logical arguments will allow the person to test their own opinion for any gaps in the reasoning.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You are not entitled to your opinion. You are entitled to your informed opinion. No one is entitled to be ignorant.
&lt;br&gt;&lt;em&gt;- Harlan Ellison&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Having strong opinions is good, but having one just for the sake of one is not good. One cannot form an opinion on a subject they know nothing about. The prerequisite to forming an opinion is research. Whether it is politics or religion or anything else thought up by man, to form an opinion, let alone a strong one, an understanding of the topic is mandatory.&lt;/p&gt;
&lt;p&gt;One must also realize that neither do most people need to agree with your opinions, nor do you need to change your opinions to satisfy most of the people. To appear acceptable to most people, one must present their views in a moderate way. But this approach puts you on a path further away from the truth. If you are looking for your truth, you must be willing to articulate your extreme ideas to others because they may not share your views and can offer you perspectives that you may have missed.&lt;/p&gt;
&lt;p&gt;When your opinion is proven wrong, it may make you feel ignorant, but that could be your signal to stop and learn a bit more with an open mind. You must remember it is the cost you must pay in pursuit of the truth. The goal, after all, is to be less wrong tomorrow than you are today.&lt;/p&gt;
&lt;p&gt;Until next time! Hoping to meet you as a better person than I am today!&lt;/p&gt;</description></item><item><title>Adding Comments to Eleventy Website with Google Sheets and Forms</title><link>/blog/adding-comments-to-eleventy-website-with-google-sheets-and-forms/</link><pubDate>Mon, 10 May 2021 00:00:00 +0000</pubDate><guid>/blog/adding-comments-to-eleventy-website-with-google-sheets-and-forms/</guid><description>&lt;p&gt;During my latest redesign of my website, I explored Static Site Generators and ended up using eleventy. Contrary to popular belief, a static website doesn’t mean that you must give up on all the fun that dynamic pages provide. In this article, I will talk about how I added a comments section to my blog posts using free google services (forms, sheets and firebase).&lt;/p&gt;
&lt;p&gt;The path to implementing comments in an eleventy blog includes using google forms to collect comment responses which are in google sheets. I chose firebase for authentication as it is very straightforward to implement it..&lt;/p&gt;
&lt;p&gt;To start things off, let us set up google forms with the required fields. The fields that we will be needing are a post identifier, commenter’s name, the content of the comment and the email address provided by firebase authentication. We will be using the article url as the post identifier. Create a new form with the fields.&lt;/p&gt;
&lt;p&gt;Open up the form in preview mode (use the eye icon) and then open developer console. Now we will note down the unique name attribute for each of the fields. To do this, in the elements tab of the developer console search for “entry.”. This will show us 4 entries. They are the name attributes that we must use in our form while submitting from our blog post, so note them down. The order is the same order of our fields. We also need to note down the url for the action attribute of the form.&lt;/p&gt;
&lt;p&gt;&lt;img src="/forms.png" alt="&amp;ldquo;Google forms with required field&amp;rdquo;"&gt;&lt;em&gt;Google forms with required field&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Now that the google form is set up, it is time to link it to a google sheet.Go into the responses tab of the form and attach it to a spreadsheet. Open up the spreadsheet and add an extra field called isAuthor. This field is never filled up by the form, we will use an ArrayFunction to fill it up as the data arrives. The sheet should be publicly viewable but not editable. Set the permissions in link sharing and save the url as it will be required later.&lt;/p&gt;
&lt;p&gt;Now let us add a couple of formulas and formatting. First, let us set “Timestamp” field formatting to mm-dd-yy. Select the entire column A, then Format =&amp;gt; Number =&amp;gt; More Formats. =&amp;gt; More date and time formats. Create a mm-dd-yy format. You can add a timestamp after the date if you prefer but it will not be used by the comments. Now it is time to use the magical ArrayFunction in the isAuthor column. The isAuthor provides us a boolean value of whether the commenter is the author by comparing the email address field. Insert the following formula in F2.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;ArrayFormula(ISNUMBER(SEARCH(&lt;span style="color:#c30"&gt;&amp;#34;&amp;lt;insert email address&amp;gt;&amp;#34;&lt;/span&gt;, $E$2&lt;span style="color:#555"&gt;:&lt;/span&gt;E)))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="/sheets.png" alt="&amp;ldquo;Google sheets with formula&amp;rdquo;"&gt;&lt;em&gt;Google sheets with formula&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let us set up firebase authentication using google as an auth provider. Create a new project in the firebase console and add a web app. Once the app is added, copy the setup code it provides. In the authentication section enable the required auth provider in the authentication tab under sign-in method tab.&lt;/p&gt;
&lt;p&gt;We are all set to dive into the eleventy and create the comments section. First create two new template names “comments.njk” and “firebase.njk”. The comments.njk file will contain the code for specific implementation of comments and firebase.njk will contain the code concerned with authentication of the user. Let us also add some data to our data file which I have named “site.json” in _data folder. This is where we will add all the links and name attributes we saved earlier. The name attributes we saved go in the postFields object&lt;/p&gt;
&lt;h6 id="_datasitejson"&gt;_data/site.json&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;……,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;“comments”:&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;“readUrl”:&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;“&amp;lt;insert&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;the&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;google&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;sheets&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;link&amp;gt;”,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;“postUrl”:&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;“&amp;lt;insert&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;google&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;form&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;action&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;link&amp;gt;”,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;“postFields”:&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;[&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;&amp;lt;post&lt;/span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;identifier&amp;gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;&amp;lt;name&amp;gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;&amp;lt;content&amp;gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;&amp;lt;email&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a00;background-color:#faa"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let us begin with the firebase code. Remember to insert the firebase setup code that we copied earlier at the top of the file. We initially set up the authentication provider project. The &lt;em&gt;authenticate()&lt;/em&gt; function is used to initiate the google auth on a button click. The &lt;em&gt;onAuthStateChanged()&lt;/em&gt; function allows us to perform actions when the state of user login changes&lt;/p&gt;
&lt;h6 id="firebasenjk"&gt;firebase.njk&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;****************************************
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;INSERT FIREBASE SETUP CODE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;****************************************
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;script&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#09f;font-style:italic"&gt;// Start a sign in process for an unauthenticated user.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;var&lt;/span&gt; provider &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;new&lt;/span&gt; firebase.auth.GoogleAuthProvider()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;provider.addScope(&lt;span style="color:#c30"&gt;&amp;#39;profile&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;provider.addScope(&lt;span style="color:#c30"&gt;&amp;#39;email&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;firebase.auth().signInWithRedirect(provider)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; authenticate() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;firebase.auth().getRedirectResult().then(&lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt;(result) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (result.credential) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;var&lt;/span&gt; token &lt;span style="color:#555"&gt;=&lt;/span&gt; result.credential.accessToken
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;var&lt;/span&gt; user &lt;span style="color:#555"&gt;=&lt;/span&gt; result.user
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; firebase.auth().onAuthStateChanged(&lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt;(user) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (user) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#39;auth&amp;#39;&lt;/span&gt;).style.display &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#39;comments-form&amp;#39;&lt;/span&gt;).style.display &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;block&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; name &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name.innerText &lt;span style="color:#555"&gt;=&lt;/span&gt; user.displayName
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; image &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#34;image&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; image.src &lt;span style="color:#555"&gt;=&lt;/span&gt; user.photoURL
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#069;font-weight:bold"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(&lt;span style="color:#c30"&gt;&amp;#34;not logged in&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#39;auth&amp;#39;&lt;/span&gt;).style.display &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;”&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#39;comments-form&amp;#39;&lt;/span&gt;).style.display &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; signOut() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; firebase.auth().signOut()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The important part of implementing the UI is done in the “comments.njk” file. Let us begin by defining the html structure and CSS. A basic explanation of the structure is that we have a div that shows when there are no comments on that particular article. Then we have an auth and form component whose visibility depends on the auth state. Three of the form fields are hidden as the will be filled automatically from the user authentication and page url. Pleae not that the entire CSS is not attached here and also replace the variables with required values. The &amp;ldquo;&lt;em&gt;google.njk&lt;/em&gt;&amp;rdquo; is a google icon svg.&lt;/p&gt;
&lt;h6 id="commentsnjk"&gt;comments.njk&lt;/h6&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% raw %}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;h2&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;post-subtitle&amp;#34;&lt;/span&gt;&amp;gt;Comments&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;h3&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;commentSection&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comments&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;no-comments&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;post-info no-comments&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;p&lt;/span&gt;&amp;gt;There are currently no comments on this article, be the first to add one below!&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;h3&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;post-subtitle&amp;#34;&lt;/span&gt;&amp;gt;Add a Comment&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;h3&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;auth&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;style&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;display:none;&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;post-info&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; You must authenticate with google in order to post comments.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;button&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;google-button&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;onclick&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;authenticate()&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;d-flex&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;style&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;justify-content:center;align-items:center;&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {% include &amp;#39;icons/google.njk&amp;#39; %} Authenticate with Google
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comments&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comments-form&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;user&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;img&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;image&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;image&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;span&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;name&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;span&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;button&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;button&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;button&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;onclick&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;signOut()&amp;#34;&lt;/span&gt;&amp;gt;Sign Out&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;form&lt;/span&gt; &lt;span style="color:#309"&gt;onsubmit&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;return checkform()&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;action&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;https://docs.google.com/forms/u/0/d/e/1FAIpQLSdeT6VPXKvg5NLo2socJFISOGxkENzV3b1vYacoK8xr-ns7Lg/formResponse&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;target&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comments_hidden_iframe&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comment-form&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;autocomplete&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;off&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;input&lt;/span&gt; &lt;span style="color:#309"&gt;name&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{site.comments.postFields[0]}}&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;type&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;value&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{page.url}}&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;style&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;display:none&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;input&lt;/span&gt; &lt;span style="color:#309"&gt;name&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{site.comments.postFields[3]}}&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;type&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;email&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;style&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;display:none&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;input&lt;/span&gt; &lt;span style="color:#309"&gt;name&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{site.comments.postFields[1]}}&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;type&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;text&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;userName&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;style&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;display:none&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;group&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;style&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;flex-grow:1;&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;textarea&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comment-content&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;maxlength&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;140&lt;/span&gt; &lt;span style="color:#309"&gt;name&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;{{site.comments.postFields[2]}}&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;form&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comment-form&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;required&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;textarea&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;span&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;span&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;label&lt;/span&gt;&amp;gt;Type in your comment&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;label&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;input&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comment-submit&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;class&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;submitButton&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;type&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;submit&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;value&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;Post&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;iframe&lt;/span&gt; &lt;span style="color:#309"&gt;name&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comments_hidden_iframe&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;id&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;comments_hidden_iframe&amp;#34;&lt;/span&gt; &lt;span style="color:#309"&gt;style&lt;/span&gt;&lt;span style="color:#555"&gt;=&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;display:none;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;iframe&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#309;font-weight:bold"&gt;style&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;comments&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;margin-bottom&lt;/span&gt;: &lt;span style="color:#f60"&gt;2&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;no-comments&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;display&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;none&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;post-info&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;text&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;offset);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;show&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;display&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;block&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;comment-name&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;font-size&lt;/span&gt;: &lt;span style="color:#f60"&gt;1.25&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;padding-right&lt;/span&gt;: &lt;span style="color:#f60"&gt;0.25&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;font-weight&lt;/span&gt;: &lt;span style="color:#f60"&gt;400&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;author-tag&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;primary);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;comment-date&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;text&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;offset);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;font-size&lt;/span&gt;: &lt;span style="color:#f60"&gt;0.8&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;comment-item&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;padding&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;margin-bottom&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;background&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;bg&lt;span style="color:#555"&gt;-&lt;/span&gt;offset);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;animation&lt;/span&gt;: fadeInUp &lt;span style="color:#f60"&gt;0.5&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;s&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;ease&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;google-button&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;padding&lt;/span&gt;: &lt;span style="color:#f60"&gt;0.5&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;font-size&lt;/span&gt;: &lt;span style="color:#f60"&gt;1.125&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;width&lt;/span&gt;: &lt;span style="color:#f60"&gt;100&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;background&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;transparent&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;outline&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;secondary);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;border&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;solid&lt;/span&gt; &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;secondary);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;text&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;google-button&lt;/span&gt; &lt;span style="color:#309;font-weight:bold"&gt;svg&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;height&lt;/span&gt;: &lt;span style="color:#f60"&gt;2&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;padding-right&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;google-button&lt;/span&gt;:&lt;span style="color:#99f"&gt;hover&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;secondary);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;background&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;bg&lt;span style="color:#555"&gt;-&lt;/span&gt;offset);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;user&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;display&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;flex&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;justify-content&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;center&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;align-items&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;center&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;user&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;image&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;width&lt;/span&gt;: &lt;span style="color:#f60"&gt;3&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;height&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;auto&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;border-radius&lt;/span&gt;: &lt;span style="color:#f60"&gt;50&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;%&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;overflow&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;hidden&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;user&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;name&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;flex-grow&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;padding-left&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;user&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;button&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;background&lt;/span&gt;: &lt;span style="color:#069;font-weight:bold"&gt;transparent&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;border&lt;/span&gt;: &lt;span style="color:#f60"&gt;1&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;px&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;solid&lt;/span&gt; &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;primary);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;primary);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;padding&lt;/span&gt;: &lt;span style="color:#f60"&gt;0.5&lt;/span&gt;&lt;span style="color:#078;font-weight:bold"&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.&lt;span style="color:#0a8;font-weight:bold"&gt;user&lt;/span&gt; .&lt;span style="color:#0a8;font-weight:bold"&gt;button&lt;/span&gt;:&lt;span style="color:#99f"&gt;hover&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;background&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;primary);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;: &lt;span style="color:#c0f"&gt;var&lt;/span&gt;(&lt;span style="color:#555"&gt;--&lt;/span&gt;&lt;span style="color:#069;font-weight:bold"&gt;color&lt;/span&gt;&lt;span style="color:#555"&gt;-&lt;/span&gt;bg);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;/&lt;span style="color:#309;font-weight:bold"&gt;style&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{% endraw %}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The iframe tag is added to avoid the page refresh when the form is submitted. Once we add javascript in the next step, the comments component will function as follows.&lt;/p&gt;
&lt;p&gt;If the user is logged in, then the comment form will be visible along with the user profile and an option to sign out
&lt;img src="/empty_loggedin.png" alt="&amp;ldquo;Comments section with user logged in&amp;rdquo;"&gt;&lt;em&gt;Comments section with user logged in&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If the user is not logged in, then the form is hidden away and the user has the option to sign in with google to login.
&lt;img src="/empty_loggedout.png" alt="&amp;ldquo;Comments section with user logged out&amp;rdquo;"&gt;&lt;em&gt;Comments section with user logged out&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We are almost at the end. The final ingredient is the javascript logic. The &lt;em&gt;loadComments()&lt;/em&gt; makes a request to the sheet document with that particular page url and gets the comments that belong to it. The &lt;em&gt;displayComments()&lt;/em&gt; parses the csv that is returned and displays it by creating new elements for each comment. The checkForm() is used to verify if the user is signed in before submitting the comment. Once a new comment is submitted, the comments are reloaded and only new one are rendered.&lt;/p&gt;
&lt;h5 id="commentsnjk-1"&gt;comments.njk&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;script&lt;span style="color:#555"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#555"&gt;%&lt;/span&gt; raw &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; months &lt;span style="color:#555"&gt;=&lt;/span&gt; [&lt;span style="color:#c30"&gt;&amp;#34;Jan&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Feb&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Mar&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Apr&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;May&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Jun&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Jul&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Aug&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Sep&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Oct&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Nov&amp;#34;&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#34;Dec&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; noComments &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#34;no-comments&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; commentSection &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#34;commentSection&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; pageUrl &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;{{ page.url }}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; visibleComments &lt;span style="color:#555"&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; formatDate(value) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; months[&lt;span style="color:#069;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#366"&gt;Date&lt;/span&gt;(value).getMonth()] &lt;span style="color:#555"&gt;+&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#366"&gt;Date&lt;/span&gt;(value).getDate() &lt;span style="color:#555"&gt;+&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;, &amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; (&lt;span style="color:#069;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#366"&gt;Date&lt;/span&gt;(value).getYear() &lt;span style="color:#555"&gt;+&lt;/span&gt; &lt;span style="color:#f60"&gt;1900&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; encodeHTML(s) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; s.replace(&lt;span style="color:#3aa"&gt;/&amp;amp;/g&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#39;&amp;amp;amp;&amp;#39;&lt;/span&gt;).replace(&lt;span style="color:#3aa"&gt;/&amp;lt;/g&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#39;&amp;amp;lt;&amp;#39;&lt;/span&gt;).replace(&lt;span style="color:#3aa"&gt;/&amp;#34;/g&lt;/span&gt;, &lt;span style="color:#c30"&gt;&amp;#39;&amp;amp;quot;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; encodeFormData(data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#366"&gt;Object&lt;/span&gt;.keys(data).map(k =&amp;gt; &lt;span style="color:#366"&gt;encodeURIComponent&lt;/span&gt;(k) &lt;span style="color:#555"&gt;+&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#39;=&amp;#39;&lt;/span&gt; &lt;span style="color:#555"&gt;+&lt;/span&gt; &lt;span style="color:#366"&gt;encodeURIComponent&lt;/span&gt;(data[k])).join(&lt;span style="color:#c30"&gt;&amp;#39;&amp;amp;&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; displayComments(comments) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (comments &lt;span style="color:#555"&gt;==&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;null&lt;/span&gt; &lt;span style="color:#555"&gt;||&lt;/span&gt; comments &lt;span style="color:#555"&gt;==&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (visibleComments.length &lt;span style="color:#555"&gt;==&lt;/span&gt; &lt;span style="color:#f60"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; noComments.classList.add(&lt;span style="color:#c30"&gt;&amp;#34;show&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; console.log(comments)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; comments &lt;span style="color:#555"&gt;=&lt;/span&gt; comments.replace(&lt;span style="color:#3aa"&gt;/&amp;#34;/g&lt;/span&gt;,&lt;span style="color:#c30"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; commentList &lt;span style="color:#555"&gt;=&lt;/span&gt; comments.split(&lt;span style="color:#c30"&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commentList &lt;span style="color:#555"&gt;=&lt;/span&gt; commentList.map(&lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt;(k) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; temp &lt;span style="color:#555"&gt;=&lt;/span&gt; k.split(&lt;span style="color:#c30"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; formatDate(temp[&lt;span style="color:#f60"&gt;0&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; temp[&lt;span style="color:#f60"&gt;3&lt;/span&gt;] &lt;span style="color:#555"&gt;=&lt;/span&gt; (temp[&lt;span style="color:#f60"&gt;3&lt;/span&gt;] &lt;span style="color:#555"&gt;==&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;TRUE&amp;#34;&lt;/span&gt; &lt;span style="color:#555"&gt;?&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;true&lt;/span&gt; &lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;false&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; temp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commentList.forEach(&lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt;(element) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (visibleComments.includes(JSON.stringify(element))) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; authorTag &lt;span style="color:#555"&gt;=&lt;/span&gt; element[&lt;span style="color:#f60"&gt;3&lt;/span&gt;] &lt;span style="color:#555"&gt;?&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;true&lt;/span&gt; &lt;span style="color:#555"&gt;:&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; item &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.createElement(&lt;span style="color:#c30"&gt;&amp;#34;div&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; item.className &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;comment-item&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; title &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.createElement(&lt;span style="color:#c30"&gt;&amp;#34;div&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title.className &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;comment-title&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; titleText &lt;span style="color:#555"&gt;=&lt;/span&gt; encodeHTML(element[&lt;span style="color:#f60"&gt;1&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; name &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.createElement(&lt;span style="color:#c30"&gt;&amp;#34;span&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; date &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.createElement(&lt;span style="color:#c30"&gt;&amp;#34;div&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; date.className &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;comment-date&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name.className &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;comment-name&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; date.append(formatDate(element[&lt;span style="color:#f60"&gt;0&lt;/span&gt;]))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name.append(titleText)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title.append(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (authorTag) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; author &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.createElement(&lt;span style="color:#c30"&gt;&amp;#34;small&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author.className &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;author-tag&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; author.innerText &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;Author&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title.append(author)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; title.append(date)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; comment &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.createElement(&lt;span style="color:#c30"&gt;&amp;#34;div&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; comment.className &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;comment-content&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; commentText &lt;span style="color:#555"&gt;=&lt;/span&gt; encodeHTML(element[&lt;span style="color:#f60"&gt;2&lt;/span&gt;].replace(&lt;span style="color:#069;font-weight:bold"&gt;new&lt;/span&gt; &lt;span style="color:#366"&gt;RegExp&lt;/span&gt;(&lt;span style="color:#c30"&gt;&amp;#39;\r?\n&amp;#39;&lt;/span&gt;,&lt;span style="color:#c30"&gt;&amp;#39;g&amp;#39;&lt;/span&gt;), &lt;span style="color:#c30"&gt;&amp;#39;&amp;lt;br /&amp;gt;&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; comment.append(commentText)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; item.append(title)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; item.append(comment)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commentSection.append(item)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; visibleComments.push(JSON.stringify(element))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; noComments.classList.remove(&lt;span style="color:#c30"&gt;&amp;#34;show&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; loadComments() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;var&lt;/span&gt; sqlQuery &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;encodeURIComponent&lt;/span&gt;(&lt;span style="color:#c30"&gt;`SELECT A, C,D, F WHERE B =&amp;#34;&lt;/span&gt;&lt;span style="color:#a00"&gt;${&lt;/span&gt;pageUrl&lt;span style="color:#a00"&gt;}&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;#34;`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fetch (&lt;span style="color:#c30"&gt;`{{site.comments.readUrl}}/gviz/tq?tqx=out:csv&amp;amp;sheet=comments&amp;amp;tq=&lt;/span&gt;&lt;span style="color:#a00"&gt;${&lt;/span&gt;sqlQuery&lt;span style="color:#a00"&gt;}&lt;/span&gt;&lt;span style="color:#c30"&gt;&amp;amp;headers=0`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .then(response =&amp;gt; response.text())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .then(response =&amp;gt; displayComments(response))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;let&lt;/span&gt; commentIframe &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#34;comments_hidden_iframe&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commentIframe.addEventListener(&lt;span style="color:#c30"&gt;&amp;#34;load&amp;#34;&lt;/span&gt;, &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt;(){submit()}, &lt;span style="color:#069;font-weight:bold"&gt;true&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; submit() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#34;comment-content&amp;#34;&lt;/span&gt;).value &lt;span style="color:#555"&gt;=&lt;/span&gt; &lt;span style="color:#c30"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; showSnackbar(&lt;span style="color:#c30"&gt;&amp;#34;Submitted Comment&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; loadComments()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;function&lt;/span&gt; checkform() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;if&lt;/span&gt; (firebase.auth().currentUser) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;).value &lt;span style="color:#555"&gt;=&lt;/span&gt; firebase.auth().currentUser.email
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#366"&gt;document&lt;/span&gt;.getElementById(&lt;span style="color:#c30"&gt;&amp;#34;userName&amp;#34;&lt;/span&gt;).value &lt;span style="color:#555"&gt;=&lt;/span&gt; firebase.auth().currentUser.displayName
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; alert(&lt;span style="color:#c30"&gt;&amp;#34;Authenticate with google to sign in&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#069;font-weight:bold"&gt;return&lt;/span&gt; &lt;span style="color:#069;font-weight:bold"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; loadComments()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#555"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a00;background-color:#faa"&gt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&lt;span style="color:#555"&gt;%&lt;/span&gt; include &lt;span style="color:#c30"&gt;&amp;#39;firebase.njk&amp;#39;&lt;/span&gt; &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#555"&gt;%&lt;/span&gt; endraw &lt;span style="color:#555"&gt;%&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Include the comments layout in the proper location in the post and voila! There we have it, a simple working comments section for a static site with just google sheets and forms and authentication for a static website with firebase auth.&lt;/p&gt;
&lt;p&gt;Until next time, adiós!&lt;/p&gt;</description></item><item><title> A peek into my life and a Brief Introduction</title><link>/blog/a-peek-into-my-life-and-a-brief-introduction/</link><pubDate>Fri, 07 May 2021 00:00:00 +0000</pubDate><guid>/blog/a-peek-into-my-life-and-a-brief-introduction/</guid><description>&lt;p&gt;I was born into a wonderful family in Chennai, a city in South India. And this blog is all about how I got to where I am today, where I am going from here and the journey in between. It will have tech, some thoughts about the last book I read, and a lot of musings about philosophy.&lt;/p&gt;
&lt;p&gt;My fascination with computers and programming began, as might be the case for many of you, with “Hello World.” Typing out the program and seeing the words appear on the screen gave me a feeling of satisfaction that I can still remember. Begining there my life has taken me through a journey towards finding my interests in computer architecutre and parallel processing. Apart from it, I enjoy web development. With a healthy mix of hard work and luck, I was able to get into UC Davis and where I hope to do what I love the most - understanding computers in depth.&lt;/p&gt;
&lt;p&gt;My first memories of videogames are those of me playing Age of Empires on my PC after school while my cousin sat with me and cheered me on. I play the game even today. Well, that and any outdoor sport. I enjoy reading and discussing various viewpoints in life.&lt;/p&gt;
&lt;p&gt;And you might be wondering why my blog is anything different from the hundreds of others. It isn’t! It is just a way for me document my journey towards a better personal and professional life. If my journey and experience helps you somehow, that is an added plus!&lt;/p&gt;
&lt;p&gt;Until next time, stay safe and healthy!&lt;/p&gt;</description></item></channel></rss>