<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tom Österlund</title>
    <description>The latest articles on Forem by Tom Österlund (@tomosterlund).</description>
    <link>https://forem.com/tomosterlund</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1248783%2Fdbf1dc1e-9a36-46f0-bfa4-9da237d7ff27.jpeg</url>
      <title>Forem: Tom Österlund</title>
      <link>https://forem.com/tomosterlund</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/tomosterlund"/>
    <language>en</language>
    <item>
      <title>Event Calendar with Shadcn theme 💅</title>
      <dc:creator>Tom Österlund</dc:creator>
      <pubDate>Tue, 10 Dec 2024 07:57:41 +0000</pubDate>
      <link>https://forem.com/tomosterlund/event-calendar-with-shadcn-theme-l8k</link>
      <guid>https://forem.com/tomosterlund/event-calendar-with-shadcn-theme-l8k</guid>
      <description>&lt;p&gt;&lt;em&gt;Shadcn has become wildly popular, and is an easy way to set up a web-app with some well-styled components. But lately, when I've seen my users integrate the Schedule-X scheduler into an app with Shadcn, it just doesn't look right...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🎉 &lt;em&gt;Enter &lt;code&gt;@schedule-x/theme-shadcn&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, I will walk you through how to integrate Schedule-X into a Shadcn project. Also, a demo app with this calendar can be viewed at: &lt;a href="https://schedule-x-shadcn.vercel.app/" rel="noopener noreferrer"&gt;https://schedule-x-shadcn.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1) Install Schedule-X
&lt;/h2&gt;

&lt;p&gt;First let's install the dependencies we need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @schedule-x/react @schedule-x/calendar @schedule-x/theme-shadcn 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2) Import and set up some CSS
&lt;/h2&gt;

&lt;p&gt;Then we need to import the CSS for the calendar...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// layout.tsx or _app.tsx depending on your router&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@schedule-x/theme-shadcn/dist/index.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and set some styles for the calendar wrapper element. The calendar does not have any default height set to it, since this will differ for every use-case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* globals.css */&lt;/span&gt;
&lt;span class="nc"&gt;.sx-react-calendar-wrapper&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;700px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3) Create calendar component
&lt;/h2&gt;

&lt;p&gt;Then the most important piece; the event calendar component. In this case we're using &lt;code&gt;useNextCalendarApp&lt;/code&gt;. If you're using React but not in a Nextjs-app, you should use the &lt;code&gt;useCalendarApp&lt;/code&gt;-hook instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// EventCalendar.tsx&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ScheduleXCalendar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useNextCalendarApp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@schedule-x/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createViewDay&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createViewMonthAgenda&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createViewMonthGrid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createViewWeek&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@schedule-x/calendar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;EventCalendar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;calendarApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useNextCalendarApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;views&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nf"&gt;createViewWeek&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nf"&gt;createViewDay&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nf"&gt;createViewMonthAgenda&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="nf"&gt;createViewMonthGrid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shadcn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;calendars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;johndoe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;colorName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;johndoe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;lightColors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hsl(210 40% 93.1%)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;onContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hsl(210 40% 93.1%)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;selectedDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-12-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Coffee with John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-12-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-12-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;calendarId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;johndoe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Breakfast with Sam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Discuss the new project&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Starbucks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-11-29 05:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-11-29 06:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;calendarId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;johndoe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gym&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-11-27 06:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-11-27 07:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;calendarId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;johndoe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Media fasting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-12-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-12-03&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;calendarId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;johndoe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some appointment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;people&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-12-03 03:00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2023-12-03 04:30&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;calendarId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;johndoe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ScheduleXCalendar&lt;/span&gt; &lt;span class="na"&gt;calendarApp&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;calendarApp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're almost there! Lastly, you need to import the &lt;code&gt;EventCalendar.tsx&lt;/code&gt; component you just created, in your app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EventCalendar&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!&lt;/p&gt;

&lt;p&gt;For more info and documentation for the Schedule-X event calendar, please visit: &lt;a href="https://schedule-x.dev" rel="noopener noreferrer"&gt;https://schedule-x.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you like the project, you can also show some appreciation by leaving a star for the GitHub repo, over at: &lt;a href="https://github.com/schedule-x/schedule-x" rel="noopener noreferrer"&gt;https://github.com/schedule-x/schedule-x&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>react</category>
    </item>
    <item>
      <title>Light- and dark mode CSS, the easy way</title>
      <dc:creator>Tom Österlund</dc:creator>
      <pubDate>Mon, 13 May 2024 06:22:20 +0000</pubDate>
      <link>https://forem.com/tomosterlund/light-and-dark-mode-css-the-easy-way-3jkc</link>
      <guid>https://forem.com/tomosterlund/light-and-dark-mode-css-the-easy-way-3jkc</guid>
      <description>&lt;p&gt;One of the never-ending tech-battles on social media right now is the battle between light- and dark mode. Everyone knows this is ultimately a matter of good and evil. Light side of the force vs. dark side of the force.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgar5uhikxi6f9oqvubdd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgar5uhikxi6f9oqvubdd.gif" alt="Luke vs Darth Vader" width="498" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whether you prefer light mode or dark mode: as a web developer, there is a high chance you need to know how to support both modes. When building the &lt;a href="https://github.com/schedule-x/schedule-x"&gt;Schedule-X calendar&lt;/a&gt;, I used a method for supporting both modes with &lt;em&gt;very low effort&lt;/em&gt;. And here's how you can do it too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: define all colors as CSS custom properties
&lt;/h2&gt;

&lt;p&gt;Firstly, you need to define all the colors you want to use in CSS custom properties. You can create a &lt;code&gt;color.scss&lt;/code&gt; file, looking something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#6750a4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-on-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-primary-container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eaddff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-on-primary-container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#21005e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#625b71&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="err"&gt;and&lt;/span&gt; &lt;span class="err"&gt;so&lt;/span&gt; &lt;span class="err"&gt;on&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you're using SCSS like I do, you might be tempted to write these colors as SCSS variables, like &lt;code&gt;$color-primary&lt;/code&gt;. But don't! These SCSS variables only exist at compile-time, and won't be around to be altered at run-time where we will need them later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: using the CSS custom property-colors &lt;em&gt;everywhere&lt;/em&gt; in your application
&lt;/h2&gt;

&lt;p&gt;If you want to successfully use this method, you need to stick consistently to this rule. Wherever you use colors in the application: use them through CSS custom properties. Even if it's "just" a simple border color, don't cheat; border colors typically need a very different nuance in dark mode than they do in light mode! So for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.my-rule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-neutral&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Create a single dark-mode rule
&lt;/h2&gt;

&lt;p&gt;In the same file where you defined your colors for light mode, you can add a rule for the selector &lt;code&gt;.is-dark&lt;/code&gt;, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.is-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#d0bcff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-on-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#371e73&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-primary-container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4f378b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-on-primary-container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eaddff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ccc2dc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="err"&gt;other&lt;/span&gt; &lt;span class="err"&gt;properties&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now what does this rule do?&lt;/p&gt;

&lt;p&gt;Basically, it just means, that whenever you slap the class name &lt;code&gt;is-dark&lt;/code&gt; onto an element, the dark mode colors will start being applied to &lt;em&gt;everything&lt;/em&gt; inside of that element - as long as you consistently used the custom properties for defining color.&lt;/p&gt;

&lt;p&gt;And that's it! Creating a dark mode for your web project does not have to be hard.&lt;/p&gt;

&lt;p&gt;If you want to play around with the concept yourself, here's a brief demo for how it works on Codepen:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/tomosterlund/embed/ExzabNR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Yes, for now you can skip some basics and learn a framework</title>
      <dc:creator>Tom Österlund</dc:creator>
      <pubDate>Thu, 25 Jan 2024 17:17:11 +0000</pubDate>
      <link>https://forem.com/tomosterlund/yesfor-nowyou-can-skip-some-basics-and-learn-a-framework-3nad</link>
      <guid>https://forem.com/tomosterlund/yesfor-nowyou-can-skip-some-basics-and-learn-a-framework-3nad</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;There is a lot of hype online around statements such as "things you need to learn before learning a framework".&lt;/li&gt;
&lt;li&gt;While skipping the basics of a language completely is not a good idea, it is &lt;strong&gt;perfectly possible&lt;/strong&gt; to learn the foundations of a programming language, parallel to learning a development framework for that language.&lt;/li&gt;
&lt;li&gt;A better rule of thumb is: "never stop learning your programming language".&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My story
&lt;/h2&gt;

&lt;p&gt;I started learning JavaScript in the spring of 2020. Due to the covid lockdowns of Berlin I had a lot of spare time to learn programming. If you're learning a programming language with even remotely as broad a usage as JS, you will know that it's impossible to learn the language well in a couple of months.&lt;/p&gt;

&lt;p&gt;Needless to say, when I started learning Vue.js through a Udemy course after 2 months of coding, I was still a proper noob. &lt;/p&gt;

&lt;p&gt;Having spent the first 5 years of my professional career as a teacher, I know how crucial motivation is for achieving learning results. If a student is not motivated, chances are high their results and grades will tank. If a student remains motivated and is having fun while learning, chances are way higher they will also achieve some sort of academic success.&lt;/p&gt;

&lt;p&gt;Coding is not an exception.&lt;/p&gt;

&lt;p&gt;It therefore didn't take me much consideration to ignore all the warnings, and start learning Vue.js, even though I wouldn't know how to use any of the &lt;code&gt;Array.prototype&lt;/code&gt; methods by heart, except maybe &lt;code&gt;push&lt;/code&gt;. Even less so did I understand even a fragment of how JavaScript objects work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But wait! If you don't learn all the JS-basics before learning Vue, you will always be a noob!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I call bullshit.&lt;/p&gt;

&lt;p&gt;Going forward, I kept learning not only Vue, but also React and Node.js during that first year. I was having a blast and and was programming almost around the clock when not working. I was super excited about building full-stack applications, and using the best tools for doing so. I couldn't limit myself to learning programming basics, without dealing a blow to my high level of motivation. What might have set me apart from some people who never truly learn the intricacies of JavaScript is this: Sure, I started using frameworks right away, &lt;em&gt;but I never stopped learning the foundations of JS&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better attitude
&lt;/h2&gt;

&lt;p&gt;And I think this is the correct attitude to promote: &lt;em&gt;never stop learning your programming language&lt;/em&gt;. We shouldn't discourage people from having fun and learning stuff they are interested in. But we should encourage people to never neglect the basics of a programming language.&lt;/p&gt;

&lt;p&gt;You can start learning Vue without understanding all the ins and outs of the &lt;code&gt;this&lt;/code&gt; keyword. But in the long run you will be a much better developer if you have a strong understanding of this complicated little word.&lt;/p&gt;

&lt;p&gt;You can learn how React hooks can help you manage state and build some nice little learning projects with it. But in the long run you will be better off if you also understand reactivity in JavaScript through concepts like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy"&gt;Proxies&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get?retiredLocale=de"&gt;getters and setters&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I don't see anything stopping people from learning these concepts in parallel though.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a matter of fact, if I had to try and teach someone how reactivity works in single page applications, I'm pretty sure I would NOT start by trying to teach someone Proxies and ES6 classes with getters and setters. Using React hooks or Vue.js &lt;code&gt;ref&lt;/code&gt; might even be better abstractions to learn JavaScript reactivity with for a beginner. &lt;strong&gt;Then&lt;/strong&gt; one could tackle more advanced "vanilla JS" topics later.&lt;/p&gt;

&lt;h2&gt;
  
  
  A word of caution
&lt;/h2&gt;

&lt;p&gt;Okay, so learning a development framework is possible to do parallel to learning a language. Still, I would not have peace with this text without ending it with a few words of caution:&lt;/p&gt;

&lt;p&gt;Sure, you can skip the basics and start learning a framework.&lt;/p&gt;

&lt;p&gt;❗️&lt;strong&gt;For now&lt;/strong&gt;❗️&lt;/p&gt;

&lt;p&gt;If you cheat, and just skip learning the basics of your programming language forever, you will likely forever stay a noob.&lt;/p&gt;

&lt;p&gt;You will try to debug the state of your Redux or Pinia store, and not understand &lt;em&gt;when&lt;/em&gt; state mutated in an unexpected way, and even less understand &lt;em&gt;why&lt;/em&gt;. You will assign new objects to objects passed a function parameters, and desperately scroll through Stack Overflow to try and understand why the damn object doesn't update as you'd expect. You will curse when you have memory leaks, because you didn't understand that a class method doesn't have the same reference value when referenced once in &lt;code&gt;document.addEventListener&lt;/code&gt; and then later in &lt;code&gt;document.removeEventListener&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For me, a couple of courses on Udemy helped me build up a level of understanding for JS, which I am now very grateful for having learned &lt;em&gt;parallel to learning frameworks&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.udemy.com/course/javascript-the-complete-guide-2020-beginner-advanced/"&gt;JavaScript - The complete Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.udemy.com/course/es6-bootcamp-next-generation-javascript/"&gt;Accelerated ES6 JavaScript Training&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the message here resonates with you: glad to hear from you! If this triggers you and you strongly disagree, don't hesitate to let me know 😊&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How I built a cross-framework frontend library</title>
      <dc:creator>Tom Österlund</dc:creator>
      <pubDate>Wed, 17 Jan 2024 15:47:14 +0000</pubDate>
      <link>https://forem.com/tomosterlund/how-i-built-a-cross-framework-frontend-library-2m6b</link>
      <guid>https://forem.com/tomosterlund/how-i-built-a-cross-framework-frontend-library-2m6b</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Web-frontends today are built using an ever-increasing number of frameworks. Vendor lock-in is a real problem.&lt;/li&gt;
&lt;li&gt;Writing cross-framework frontend libraries does not have to be complex. It merely requires careful planning.&lt;/li&gt;
&lt;li&gt;Teleport / Portals are key for giving the implementer freedom to customize.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article will discuss the general concept of building a cross-framework frontend library. It will also display some examples of how this was applied when building the event calendar &lt;a href="https://github.com/schedule-x/schedule-x" rel="noopener noreferrer"&gt;Schedule-X&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with framework-overflow
&lt;/h2&gt;

&lt;p&gt;If you start building a frontend web-application today, one of the first technical decisions you will have to take is which framework to choose. Probably you will end up with one of the 3-4 most popular ones, but there are so many out there.&lt;/p&gt;

&lt;p&gt;Now, I don't agree that this would be inherently bad. People come up with a lot of different solutions to similar problems all the time. People are essentially different, and will prefer different solutions. You're never going to convince me that mass producing Mercedes-Benz and Peugeot was unnecessary, just because Henry Ford had already started mass producing cars before them. Similarly: Vue, React and Angular do not make newer options redundant.&lt;/p&gt;

&lt;p&gt;One of the challenges that do arise due to this abundance of frameworks, is a thing called vendor lock-in; you pick one framework, and chances are high you will stick to this for the duration of the project. Even if the project lasts for many years.&lt;/p&gt;

&lt;p&gt;This too, does not necessarily need to be a problem. If the framework thrives, and its eco-system does too, this does not matter. When it does matter, is when the framework you chose, or its surrounding eco-system, starts slowing down. Your company might need a whiteboard software. But all the cool whiteboard projects out there are only compatible with all the other frameworks, not yours. What do you do?&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing cross-framework libraries is not that hard
&lt;/h2&gt;

&lt;p&gt;Some of the problems that arise due to vendor lock-in, could be resolved if more libraries chose to work without frameworks. Here is an illustration of how this can work conceptually:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpoyagmg7g1jua4wqyq7r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpoyagmg7g1jua4wqyq7r.jpg" alt="Cross-framework library architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;OK, what is going on in this image? Let us go through this architecture piece by piece.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The library core
&lt;/h3&gt;

&lt;p&gt;At the very bottom of the image, there are 3 blocks that I chose to call &lt;strong&gt;application components&lt;/strong&gt;. If you are building a cross-framework library, these can be built with whatever tools you want! Only catch is, all the tools you use to build it, will be needed by everyone consuming it. So choose wisely, and be mindful of how many kilobytes of third party code you will need in order to ship. In Schedule-X, I chose to use &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;Preact&lt;/a&gt;. You will probably be fine with most lightweight virtual DOM libraries, and just like with frameworks there are a few to pick from.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Pure JS/TS function or object
&lt;/h3&gt;

&lt;p&gt;In order to be consumed by different types of frameworks, your library will need to be compiled to regular JavaScript. So in the end, after you have run your build scripts, the result you need is a &lt;em&gt;vanilla JavaScript&lt;/em&gt; function or object, which under the hood mounts your application. Here is how I did this in Schedule-X:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CalendarWrapper&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/calendar-wrapper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;CalendarAppSingleton&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@schedule-x/shared/src/interfaces/calendar/calendar-app-singleton&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CalendarApp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CalendarAppSingleton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CalendarWrapper&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$app&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I here have a very slimmed down class, &lt;code&gt;CalendarApp&lt;/code&gt;, with a &lt;code&gt;render&lt;/code&gt; method. All the &lt;code&gt;render&lt;/code&gt; method is doing, is call the &lt;code&gt;render&lt;/code&gt; function of Preact, mounting my calendar to the DOM. Anyone instantiating this class and calling the &lt;code&gt;render&lt;/code&gt; method with an element passed to it, will get a calendar to consume.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Consuming the library
&lt;/h3&gt;

&lt;p&gt;The last puzzle piece of the image above is the consumer-piece.&lt;/p&gt;

&lt;p&gt;After I have compiled this TypeScript file above into JavaScript, anyone executing JavaScript in a modern browser can consume it: React, Angular, Vue: they can all run it. At this point, we already have a framework agnostic library, ready to be consumed by many frontend teams, regardless of their choice of tooling. One important piece is missing though.&lt;/p&gt;

&lt;h2&gt;
  
  
  Giving the implementer ultimate freedom
&lt;/h2&gt;

&lt;p&gt;So you have built your frontend library. You spent countless hours on perfecting the looks and feel of it. Every pixel looks perfect to you. Still, if you're going to offer this library to the multitudes, chances are it won't fit into the design system others are working with.&lt;/p&gt;

&lt;p&gt;I, for example, built the Schedule-X calendar leaning heavily towards Material Design 3. While this might work for some, it surely won't work for all. It is therefore important that I offer implementers a way of customizing what shows up in the calendar. Ultimately, it would be nice if implementers of my library could replace some components of my code with their own. This can seem like a daunting task at first. It sure did to me. Nevertheless, the solution does not have to be very complex.&lt;/p&gt;

&lt;p&gt;In the example below, I will demonstrate how this can be done with React, but the same principles apply to any framework where UI components have reactive state that triggers DOM changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feduhvq4p8h9okkq4m9px.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feduhvq4p8h9okkq4m9px.jpg" alt="Custom components in cross-framework libraries"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how it works, broken down into 4 steps. Each of the steps below correlate to the same number in the image above.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;There needs to be a React component, let us call it the &lt;code&gt;ReactAdapter&lt;/code&gt; component. This component has a rendering function like all others, which places some content in the DOM. Most importantly, this component has a &lt;code&gt;customComponentFn&lt;/code&gt; function. We can pass this function as part of the configuration to the library components. Also note that the function receives an argument: &lt;code&gt;element&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the presence of &lt;code&gt;customComponentFn&lt;/code&gt;, our application component notices that we want to render som custom stuff. Doing so, instead of rendering its own content, it just calls &lt;code&gt;customComponentFn&lt;/code&gt;. Here, we also pass an element, which is empty for now, but sits exactly in the place of the DOM where we want to render some custom content.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;code&gt;customComponentsMeta&lt;/code&gt; variable of our React component, our &lt;code&gt;customComponentFn&lt;/code&gt; now saves some metadata about the custom component we want to render, as well as the element it should be rendered onto.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Updating the state of &lt;code&gt;ReactAdapter&lt;/code&gt; causes our React &lt;code&gt;render&lt;/code&gt; function to run again. This time around, we have some metadata about a custom component and the element to render it to. Using &lt;a href="https://react.dev/reference/react-dom/createPortal" rel="noopener noreferrer"&gt;&lt;code&gt;createPortal&lt;/code&gt;&lt;/a&gt; (in Vue this would be &lt;strong&gt;Teleport&lt;/strong&gt;) we can now render our custom component.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voila! We have built a cross-framework library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Curious for more?
&lt;/h2&gt;

&lt;p&gt;If these concepts sound interesting to you, but you need more precise examples of how this actually pans out in code, you can check out the actual source code that this article is based on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;React implementation: &lt;a href="https://github.com/schedule-x/react/blob/main/src/schedule-x-calendar.tsx" rel="noopener noreferrer"&gt;https://github.com/schedule-x/react/blob/main/src/schedule-x-calendar.tsx&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Application component calling a &lt;code&gt;customComponent&lt;/code&gt; function provided by React: &lt;a href="https://github.com/schedule-x/schedule-x/blob/main/packages/calendar/src/components/week-grid/date-grid-event.tsx#L79" rel="noopener noreferrer"&gt;https://github.com/schedule-x/schedule-x/blob/main/packages/calendar/src/components/week-grid/date-grid-event.tsx#L79&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>From teacher to software developer in less than 1 year</title>
      <dc:creator>Tom Österlund</dc:creator>
      <pubDate>Wed, 10 Jan 2024 08:15:55 +0000</pubDate>
      <link>https://forem.com/tomosterlund/from-teacher-to-software-developer-in-less-than-1-year-1hk2</link>
      <guid>https://forem.com/tomosterlund/from-teacher-to-software-developer-in-less-than-1-year-1hk2</guid>
      <description>&lt;p&gt;In 2020, I made a journey, starting the first week of COVID lockdowns in the beginning of March, and ending with a signature on the contract of my first developer job, in December the same year. Since then, I’ve been asked many times by people I met, how one can simply switch from pedagogical work into IT, like I did. So without further ado, here’s how it happened:&lt;/p&gt;

&lt;h2&gt;
  
  
  The beginning: coding the night away
&lt;/h2&gt;

&lt;p&gt;In the beginning of 2020, I was working as a primary school teacher at an international school in Berlin. I had no hard feelings about the profession itself. Working in schools has the potential to be very stressful, but also to be a lot of fun.&lt;/p&gt;

&lt;p&gt;During a conversation with a coworker, this then colleague of mine told me that he had started thinking of switching into IT. He had just taken on the role of doing IT-support for us other teachers, and was learning to program in PHP in his spare time.&lt;/p&gt;

&lt;p&gt;At the time, I found this a peculiar concept. A coding teacher? I then asked him a question, which I have since also received myself from numerous people: “don’t you need to be really good at maths and science for that stuff?”&lt;/p&gt;

&lt;p&gt;Fast forward a few weeks, and I was sitting in an old, noisy, Berlin subway car on my way home from work. COVID was still new. Due to the circumstances, it was clear that this had just been my last day on-site at the school in a while. Working with children in the age of 7–9, it was also clear that we would not be able to spend nearly as much time as otherwise with our students during the lockdown to come. The students were simply too young to be able to spend their whole day in front of a computer. This left me with a thought: if I end up having more spare time than usual, should I maybe try and learn some new skill? Walking out of that very same subway car some 25 minutes later, I had decided: I’m gonna try out programming.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;But hey, how does one simply start programming, with no previous knowledge of computer science?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For me, it started with coming home, opening up my laptop, and typing “learn programming” into Google. The top search result in my case, was a course in HTML on freeCodeCamp.&lt;/p&gt;

&lt;p&gt;In retrospect, I have realized how such a trivial thing as a Google search, had a huge impact on my career. I simply picked the first result. Had Google instead suggested me a course in Golang or Python, maybe my career would have taken a different turn. In my case, that night in March, I ended up embarking on a journey of learning web development, starting with a simple “hello world” HTML exercise. Many exercises and a first simple webpage later, I shut my laptop and went to bed, just as the sun was about to rise over Berlin for a new day. I knew right away: being so excited about something that I could barely allow myself to go to bed, that doesn’t happen all the time. I knew, going to bed after that first long night of coding, that I had to try and change careers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The journey: hard work, doubts, and overcoming
&lt;/h2&gt;

&lt;p&gt;The first few weeks of learning to build software, I finished that first course in HTML and CSS, and I started a course in programming with JavaScript, also on freeCodeCamp.&lt;/p&gt;

&lt;p&gt;Though the world was experiencing a major change in the way we live and interact with others, and despite all uncertainty of how things would turn out, I was genuinely excited about the future, much thanks to this new hobby.&lt;/p&gt;

&lt;p&gt;I built a webpage dedicated to my then fiancé, now beloved wife.&lt;/p&gt;

&lt;p&gt;I built a quiz application for my students, which I proudly managed to deploy to Google Cloud, with the help of a friend. Here's a screenshot of the site, where my students battled the "grammar monster" in some different punctuation and spelling exercises.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Lq0JkSFM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kpvftzt6yvvsd88o3s45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lq0JkSFM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kpvftzt6yvvsd88o3s45.png" alt="Old quiz webpage" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I could barely find words to describe my joy, as I received the first emails with feedback from parents: the quiz website was a hit! Are there more quizzes coming?&lt;/p&gt;

&lt;p&gt;I started staying up late almost every night, learning about control structures, data structures, CSS tricks and so on. When going for walks alone, I would more often than not hear podcasts on web development. The &lt;a href="https://syntax.fm/"&gt;Syntax podcast&lt;/a&gt; became one of my new highlights every week. Most of the time, I enjoyed it a lot. But this story would not be complete without the other side of the story: &lt;em&gt;the doubts&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Many people hold the belief, that one can only become a software developer through studying computer science at a university. I shared this belief up until 2020. Though I heard from a lot of people that this is not true, I also had my moments of doubt.&lt;/p&gt;

&lt;p&gt;I remember a particularly frustrating week, still in the spring of 2020, where I was doing some algorithm challenges at freeCodeCamp. The website provided a task, such as: “write a function that takes an array-parameter ‘people’, that returns a list of all people older than 18, and sorts them in descending order according to their age.” You could then write a function directly in their interactive editor, and test it right away. In the beginning, I got almost every algorithm challenge wrong, which was very unsatisfying at times. Learning to work with data structures and algorithms can be hard work, regardless of whether you have a computer science background or not.&lt;/p&gt;

&lt;p&gt;Some nights, I shut my laptop in pure frustration, thinking maybe I’m too stupid for this. Though for me I wouldn’t call it impostor syndrome, which is a very popularized term among people in IT, I did struggle. Sometimes hard. For me being a believer in God, this sometimes led me to long doubtful conversations with him, in despair over my perceived inabilities.&lt;/p&gt;

&lt;p&gt;Though for every night of doubts and struggle, there came a morning of excitement. I just couldn’t get enough!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting serious: learning frameworks and starting the job search
&lt;/h2&gt;

&lt;p&gt;In may of 2020, only a couple of months after that first night, I took the decision to start learning a JavaScript framework for developing websites: Vuejs. Though some people warn not to start learning frameworks too soon, and instead keep learning the foundations of programming, I was too excited to keep learning the basics.&lt;/p&gt;

&lt;p&gt;Here, in retrospect, I would also tone down the importance of such decisions: if you’re new to coding and excited to learn a framework: do it! You can, and probably should, always come back to the basics later. Most likely over and over again.&lt;/p&gt;

&lt;p&gt;So I started learning Vue. I took a class with the brilliant teacher &lt;a href="https://www.udemy.com/user/maximilian-schwarzmuller/"&gt;Maximillian Schwarzmiller on Udemy&lt;/a&gt;. His class was so inspiring, that I ended up taking his class on Nodejs a couple of months later, and thus I also learned the foundations of building server-side applications. For anyone new to web development, I highly recommend the Academind course platform, from Maximillian and his colleague. For the price of a normal lunch in Germany, you get monthly access to some high quality courses.&lt;/p&gt;

&lt;p&gt;So it happened, that my basic knowledge in JavaScript, CSS and HTML, turned into basic knowledge of Vuejs and Node. I had no idea if this would be enough to land me a job, but being curious if it would, I sat down to write my first couple of job applications at the end of July in 2020, only 4 months into my learning journey!&lt;/p&gt;

&lt;p&gt;Out of 2 applications, I even ended up having 1 interview!&lt;/p&gt;

&lt;p&gt;The startup in question was working on a software for higher education, and they found it interesting that a teacher was applying for a job with them. Though I had a nice chat with the founders, and though the outcome of the talk seemed positive to me, they got back to me the following week: “thanks for your interest, but we’re moving on with a person with a bit more experience”.&lt;/p&gt;

&lt;p&gt;I was disappointed, for sure. But it also made sense, that software employee n1 apart from the founders themselves, should preferably be someone experienced.&lt;/p&gt;

&lt;p&gt;So the month of August came around, and school started again. Hopes about a new career in front of a code editor, turned into the reality of a classroom. I was once again walking around the classroom, helping first graders write their first few sentences, and playing catch with my students in the breaks. I had once been very passionate about being a teacher. But now my passion was so clearly somewhere else.&lt;/p&gt;

&lt;p&gt;Though many might hold this to be foolish for an adult, I took the decision to quit my job, without having found a new one. Since I had a three month notice period, and was only allowed to quit at the end of each semester, this left me a 5-month deadline to find something new.&lt;/p&gt;

&lt;p&gt;Thus began the most intense job hunt I ever went through. I talked to recruiters. I sent my resumé around. Probably to over 20 different companies. Out of those numerous applications, only two turned into proper interviews.&lt;/p&gt;

&lt;p&gt;The first turndown, in October, I handled quite OK. I had time.&lt;/p&gt;

&lt;p&gt;Though after traveling all the way to Munich by train, just to be turned down at the second company, this time in December, the doubts started coming around again. Had I quit my job to soon? In 2 months I would not have a job anymore, and now I also had no lead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The breakthrough
&lt;/h2&gt;

&lt;p&gt;Desperate to start my career in software, I took to desperate measures. Having previously restricted my job search to job postings written in English, I now translated my CV into German.&lt;/p&gt;

&lt;p&gt;Then, I went for quantity.&lt;/p&gt;

&lt;p&gt;I went over my template for a personal letter one more time, and made it generic enough to be passed around to the masses.&lt;/p&gt;

&lt;p&gt;Over the course of the next couple of weeks, I applied to nearly a 100 different jobs!&lt;/p&gt;

&lt;p&gt;Though it probably isn’t the best strategy to land a job, to send around those generic letters, with no mention of the company you’re applying to, it seemed like my only hope at the time. If you’re as desperate as I was at the time, I don’t see anything wrong in this strategy. Just one word of caution here: if you go for quantity, try and make sure you don’t burn any bridges with companies that seem extra attractive to you. For example, I deliberately &lt;em&gt;did not apply&lt;/em&gt; to the company where I now work, since I knew that they might be a great future employer. If you have some company in mind for the future, maybe they shouldn’t yet see that impersonal, generic letter of yours.&lt;/p&gt;

&lt;p&gt;So now I had almost a hundred job applications sent out. At times, I wasn’t even sure myself if this made sense, or if I was acting crazy.&lt;/p&gt;

&lt;p&gt;But as a matter of fact: it worked!&lt;/p&gt;

&lt;p&gt;As an early Christmas present for me, a few days before Christmas, I finally signed for my first job as a software developer. Silly as it might sound: that felt like one of the best things that had ever happened to me. I had worked so hard. I had poured my heart into this career change, learned JavaScript and TypeScript, learned three different development frameworks, and spent many long nights in front of my computer. And now the new career could begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  The career change in numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;10 months from first line of code, until first job&lt;/li&gt;
&lt;li&gt;~60 Euro spent on Udemy courses&lt;/li&gt;
&lt;li&gt;Foundations of 3 web frameworks learned: Vue, React &amp;amp; Express.js&lt;/li&gt;
&lt;li&gt;Foundations of 2 programming languages: JavaScript &amp;amp; TypeScript&lt;/li&gt;
&lt;li&gt;~100 job applications sent&lt;/li&gt;
&lt;li&gt;3 interviews without a job offer&lt;/li&gt;
&lt;li&gt;1 job offer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A few words on what I'm doing now
&lt;/h2&gt;

&lt;p&gt;Nowadays I work at the German media company ProSiebenSat.1. It's a great company, treating their employees with respect, bringing fun, local content to the German speaking countries, and where I've had the chance to grow on many levels over the past year.&lt;/p&gt;

&lt;p&gt;In my spare time I'm developing an open source event calendar. You can check it out at: &lt;a href="https://github.com/schedule-x/schedule-x"&gt;https://github.com/schedule-x/schedule-x&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>career</category>
    </item>
    <item>
      <title>3 awesome libraries I started using in 2023</title>
      <dc:creator>Tom Österlund</dc:creator>
      <pubDate>Mon, 08 Jan 2024 17:14:09 +0000</pubDate>
      <link>https://forem.com/tomosterlund/3-awesome-libraries-i-started-using-in-2023-hie</link>
      <guid>https://forem.com/tomosterlund/3-awesome-libraries-i-started-using-in-2023-hie</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Just want the hot tips, and no blabbering? Here's the summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/javierbrea/eslint-plugin-boundaries"&gt;eslint-plugin-boundaries&lt;/a&gt;: Eslint plugin for keeping large code bases organized and promoting module encapsulation.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ttarnowski/ts-sinon"&gt;ts-sinon&lt;/a&gt;: Easily create stubs for all types of objects in your TypeScript project.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt;: 3kb alternative to React&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Awesome software I started using in 2023
&lt;/h2&gt;

&lt;p&gt;One of the things I love about starting new projects, or joining a new team, is that I get to learn new technologies. In 2023 I joined a new company, and started a new open source project. This gave me two chances of learning new stacks and new tools. Without further ado, here are 3 tools that have transformed the way I write code in the last year:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/javierbrea/eslint-plugin-boundaries"&gt;eslint-plugin-boundaries&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I love organizing my frontend code as monorepos. This helps me define different packages of modules, and define how they relate to one another. It's a great pattern for scaling. One problem that all monorepos need to solve, is defining rules for how packages are allowed to depend on one another. This is where &lt;strong&gt;eslint-plugin-boundaries&lt;/strong&gt; can be a huge help. With this plugin I can define packages as such in my eslint-config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boundaries/elements&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;helpers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;helpers/*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components/*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;modules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pattern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;modules/*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then I can enforce rules for how they are allowed to depend on one another like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boundaries/element-types&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;disallow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;components&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;helpers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;modules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;allow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;helpers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, all dependencies between packages is disabled by default. Then I allow &lt;code&gt;components&lt;/code&gt; and &lt;code&gt;modules&lt;/code&gt; to depend on the &lt;code&gt;helpers&lt;/code&gt; package.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/ttarnowski/ts-sinon"&gt;ts-sinon&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;My unit tests used to be way more bloated and unreadable than they are now. To some extent, ts-sinon helped me reduce this. Though this library is far from new, and not very famous, I love it! Given a TypeScript interface as such:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;sale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used to create mock objects in my tests such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mockProduct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Engine Wroom x100&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;asdfghjkl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With ts-sinon, I instead do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;stubInterface&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ts-sinon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stubInterface&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I then need to set a property to a certain value, I just override that one property of the stub. For larger objects, this saves me from a great many lines of redundant test code.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://preactjs.com/"&gt;Preact&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Most importantly, I last year started working with Preact. Basically, the objective of this library is to offer the same APIs as React, but in 3kb of minified code! Isn't that great?&lt;/p&gt;

&lt;p&gt;I had come across the name Preact many times over the last couple of years, but never checked it out. Now, I'm a big fan. For any project where a heavy JavaScript-framework is unnecessary, but I still want a library to perform most of the heavy lifting for me, I would now reach for Preact. No doubt.&lt;/p&gt;

&lt;p&gt;Preact also offers it's own solution for state management called &lt;a href="https://preactjs.com/guide/v10/signals/"&gt;signals&lt;/a&gt;. Coupled with the Context API, signals can help you manage global state, similar to if you were working in a regular React application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lastly
&lt;/h2&gt;

&lt;p&gt;If this content is helpful to you, consider checking out my &lt;a href="https://twitter.com/TomOesterlund"&gt;profile on X&lt;/a&gt;, where I'm blogging about building an open source calendar: &lt;a href="https://github.com/schedule-x/schedule-x"&gt;https://github.com/schedule-x/schedule-x&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I built an open source web calendar inspired by the Google calendar</title>
      <dc:creator>Tom Österlund</dc:creator>
      <pubDate>Mon, 08 Jan 2024 06:10:52 +0000</pubDate>
      <link>https://forem.com/tomosterlund/i-built-an-open-source-web-calendar-inspired-by-the-google-calendar-1328</link>
      <guid>https://forem.com/tomosterlund/i-built-an-open-source-web-calendar-inspired-by-the-google-calendar-1328</guid>
      <description>&lt;p&gt;&lt;strong&gt;Shameless plug coming right up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hi everyone. My name is Tom, and I’ve done it again.&lt;/p&gt;

&lt;p&gt;“Ok?” You might think. “I don’t know you. What have you done?”&lt;/p&gt;

&lt;p&gt;For some reason, I have become very fond of building web calendars. As a matter of fact, I’ve spent some time almost every day for the past two years doing so. For my third stab at building a calendar, I decided to lean heavily on the Material Design 3 specification. I did my research. Studied the Google calendar and a few other similar ones. Then I coded. The result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://schedule-x.dev"&gt;https://schedule-x.dev&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy to receive feedback of any kind, good or bad.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
