<?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>DEV Community: Sinan</title>
    <description>The latest articles on DEV Community by Sinan (@sinandev).</description>
    <link>https://dev.to/sinandev</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%2F458350%2F5ee060cd-e65f-47d2-9c04-2a8f2f2ca5d2.png</url>
      <title>DEV Community: Sinan</title>
      <link>https://dev.to/sinandev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sinandev"/>
    <language>en</language>
    <item>
      <title>CloudYourself Introduction: Setup Raspberry Pi</title>
      <dc:creator>Sinan</dc:creator>
      <pubDate>Wed, 15 Mar 2023 16:21:32 +0000</pubDate>
      <link>https://dev.to/sinandev/cloudyourself-introduction-setup-raspberry-pi-4bic</link>
      <guid>https://dev.to/sinandev/cloudyourself-introduction-setup-raspberry-pi-4bic</guid>
      <description>&lt;p&gt;There are numerous services that may be run within your homelab, however the important question remains: is it truly worthwhile? Throughout this series, we are going to setup some of those services, while simultaneously discussing the pros and cons, so you can decide ahead of time if you wanna do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preface
&lt;/h3&gt;

&lt;p&gt;We will focus on utilizing a Raspberry Pi, although any outdated pc or the Raspberry Pi alternatives &lt;a href="https://www.youtube.com/watch?v=uJvCVw1yONQ" rel="noopener noreferrer"&gt;here&lt;/a&gt; with ubuntu would also do the trick. It is important to note that a Pi possesses sufficient capabilities to support all services discussed in this series, but not for tasks like media encoding for Plex.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chosing an OS
&lt;/h3&gt;

&lt;p&gt;My suggestion is to use an Debian-based headless distribution - we won't need a desktop environment. For those who utilize Pi, Pi OS Lite is an excellent choice, as it offers stability, uses the apt packet manager, remains headless, and excludes extraneous packages.&lt;/p&gt;

&lt;h4&gt;
  
  
  Requirements
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;🍓 Raspberry Pi&lt;/li&gt;
&lt;li&gt;💾 MicroSD Card (+16 GB recommended)&lt;/li&gt;
&lt;li&gt;📡 WiFi or 🔌 Ethernet&lt;/li&gt;
&lt;li&gt;🖥️ Dekstop or 💻 Laptop&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Download Raspberry Pi Imager
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://www.raspberrypi.com/software/" rel="noopener noreferrer"&gt;Raspberry Pi Imager&lt;/a&gt; is  a utility application which helps installing an OS on a &lt;em&gt;microSD&lt;/em&gt; card. After installing it, you should be seeing the following:&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%2F8xhgh39byobyzaymeijp.png" 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%2F8xhgh39byobyzaymeijp.png" alt="Raspberry Pi Imager"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Setup Pi OS 🛠️
&lt;/h2&gt;

&lt;p&gt;Click on &lt;em&gt;CHOOSE OS&lt;/em&gt; -&amp;gt; &lt;em&gt;Raspberry Pi OS (other)&lt;/em&gt; and then scroll down to select &lt;em&gt;Raspberry Pi OS Lite (64-Bit)&lt;/em&gt;.&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%2F0vtcu5p79xqbr0l626tn.png" 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%2F0vtcu5p79xqbr0l626tn.png" alt="Raspberry Pi Imager selecting the OS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next click on the settings icon in the right bottom cornern. Select the first box and set a hostname for your pi. Select &lt;em&gt;Enable SSH&lt;/em&gt; with &lt;em&gt;Use password authentication&lt;/em&gt; and set a username and password below. (We will need it later, when we connect per ssh)&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%2Frf642utsk6d2kdn7dgun.png" 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%2Frf642utsk6d2kdn7dgun.png" alt="Raspberry Pi Imager advanced options"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are not going to use ethernet, we have to setup WiFi. Failing to do so will result in an inability to access the Pi, as it remains headless - without a desktop interface.&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%2F661tnfyqned20tvfiblg.png" 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%2F661tnfyqned20tvfiblg.png" alt="Raspberry Pi Imager advanced options setting up WiFi"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disable the telemetry setting at the bottom and click on &lt;em&gt;save&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Plug your microSD 💾 card and flash ⚡ it
&lt;/h2&gt;

&lt;p&gt;Click on &lt;em&gt;CHOOSE STORAGE&lt;/em&gt; and select your microSD card. Confirm the name and the size before you select it, since everything will be deleted on the storage device you select.&lt;/p&gt;

&lt;p&gt;Click on &lt;em&gt;Write&lt;/em&gt; to start flashing and wait until it is done and you see a message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Start your PI 🍓 and find it
&lt;/h2&gt;

&lt;p&gt;With your microSD card inserted, plug in the pi to power. It will start automatically. It takes about 15 - 30 seconds for the pi to boot.&lt;/p&gt;

&lt;p&gt;To find the local ip address, go to your routers dashboard. After logging in you should see an overview of connected devices. Find your Pi with the hostname you selected in the list and note the ip address of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Connect ☎️ via SSH
&lt;/h2&gt;

&lt;p&gt;Open a terminal on your computer and type&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh USERNAME@YOUR_IP_ADDRESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you entered the correct ip address, it should ask for a password&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;USERNAME@YOUR_IP_ADDRESS's password:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After typing in your password you should see something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;USERNAME@HOSTNAME:~ $ |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Next time we are going to install &lt;strong&gt;Portainer&lt;/strong&gt;, a web interface that helps us manage our docker containers. It's like Docker Desktop, but better. It will be usefull to setup new docker containers and maintain them.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>raspberrypi</category>
      <category>docker</category>
      <category>cloud</category>
    </item>
    <item>
      <title>When centering text is more difficult than you think!</title>
      <dc:creator>Sinan</dc:creator>
      <pubDate>Tue, 23 Aug 2022 01:38:00 +0000</pubDate>
      <link>https://dev.to/sinandev/when-centering-text-is-more-difficult-than-you-think-af6</link>
      <guid>https://dev.to/sinandev/when-centering-text-is-more-difficult-than-you-think-af6</guid>
      <description>&lt;p&gt;Imagine you're an experienced web developer 👨‍💻 and someone asks you to &lt;em&gt;center&lt;/em&gt; a text inside a box. &lt;em&gt;"Not like I didn't do that a 1 Mio times in my life"&lt;/em&gt; you say and continue. Rule after rule is being added by you to the css file, and finally you look at the result. Something is off, the text isn't just a little bit too low. In fact, the text in the button is also too low, it was never really centered. You apply, just like a pro would do, EVERY css rule that has "align" in the name and set it to "center" aaaand doesn't work,&lt;/p&gt;

&lt;p&gt;That wouldn't happen to you? Well, go ahead, logout and check the &lt;em&gt;Log in&lt;/em&gt; and &lt;em&gt;Create account&lt;/em&gt; button from &lt;a href="//dev.to"&gt;dev.to&lt;/a&gt;. Did you notice that the space above is larger than the one below? It is off by a pixel.&lt;/p&gt;

&lt;p&gt;Here you can count them 😉:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MuENnoBd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8e11g3fcxr1853td24sg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MuENnoBd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8e11g3fcxr1853td24sg.png" alt="Picture of the create account button with bad vertical text alignment" width="880" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I don't dare to say that it was by accident in the case of dev.to, and I will go ahead, saying it is &lt;em&gt;by design&lt;/em&gt; like that. But why could that be the case on other websites?&lt;/p&gt;

&lt;p&gt;Well, the cause of the issue is &lt;em&gt;vertical metric&lt;/em&gt; (or rather its poor values), which as the name implies, is there to vertically align the font. If that metric is off, your alignment will be trash. "Only unknown, cheap fonts are affected" I hear you say, to which I can only answer: &lt;strong&gt;No!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just to give you an example: One of them is &lt;strong&gt;Open Sans&lt;/strong&gt;, which according to Google Fonts is used by 130,000,000 websites and the second most used font from Google Fonts... which means 130,000,000 Websites have wrong text alignment 😅&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cheers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Edit #1:&lt;br&gt;
The vertical offset is most likely intentionally by the creator(s) for most fonts. However, I think it would be better if all of them would be centered by default and manually offset by software, e.g. using css-rules.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://drafts.csswg.org/css-inline-3/#leading-trim"&gt;leading-trim&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Edit #2:&lt;br&gt;
Above I said that all 130,000,000 websites are affected by it, but that would only be the case if all of them actually tried to vertically center at least one piece of text using that font.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>discuss</category>
      <category>design</category>
    </item>
    <item>
      <title>Tailwind css dark mode switch with JavaScript</title>
      <dc:creator>Sinan</dc:creator>
      <pubDate>Tue, 25 Aug 2020 21:34:46 +0000</pubDate>
      <link>https://dev.to/sinandev/tailwind-css-dark-mode-switch-with-javascript-2kl9</link>
      <guid>https://dev.to/sinandev/tailwind-css-dark-mode-switch-with-javascript-2kl9</guid>
      <description>&lt;p&gt;Tailwind css is really a greate utility-first framework that provides a lot of preset values (colors, sizes, etc...) that work very well out of the box. I also like the fact that I dont have to jump between the html and the css file, while others prefer the separation.&lt;/p&gt;

&lt;p&gt;Now the problem is that tailwind makes it harder to implement a dark or a colored version, unless you know how it is done. Without tailwind I would add a class like 'scheme-dark' to the html tag and customize every element in my scss file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* style.scss */

h1 {
    color: black;
}

.scheme-dark {
    h1 {
        color: white;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However in tailwind we define the color of the text with a class in the html file, so this is what we want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* index.html */

&amp;lt;h1 class="text-blue-900 dark:text-white"&amp;gt;Hello world!&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The official documentation recommends to add the following to the tailwind config&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* tailwind.config.js */

module.exports = {
  theme: {
    extend: {
      screens: {
        'dark': {'raw': '(prefers-color-scheme: dark)'},
        // =&amp;gt; @media (prefers-color-scheme: dark) { ... }
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but not as good as expected. Why? Because the media rule &lt;code&gt;prefers-color-scheme&lt;/code&gt; looks at the browser setting, it is not possible to change it with e.g. a button and some javascript. So the user would have to go into the browser settings and change to light/dark mode.&lt;/p&gt;

&lt;p&gt;To give the user the option to change to light/dark or any other color mode, we can modify the tailwind configs.&lt;/p&gt;

&lt;p&gt;First we create our custom variant by adding a new plugin in the tailwind configs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ...
    plugins: [
        plugin(function ({ addVariant, prefix }) {
            addVariant('dark', ({ modifySelectors, separator}) =&amp;gt; {
                modifySelectors(({ selector }) =&amp;gt; {
                    return selectorParser((selectors) =&amp;gt; {
                        selectors.walkClasses((sel) =&amp;gt; {
                            sel.value = `dark${separator}${sel.value}`
                            sel.parent.insertBefore(sel, selectorParser().astSync('.scheme-dark '))
                        })
                    }).processSync(selector)
                })
            })
        })
    ]
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name of our variant is &lt;code&gt;dark&lt;/code&gt; and it has a parent class &lt;code&gt;.scheme-dark&lt;/code&gt; (don't forget the space at the end!)? This will be used by tailwind when it generates the css.&lt;/p&gt;

&lt;p&gt;Then we add our custom variant to the properties that we want to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    ...
    variants: {
        textColor: ['dark', 'responsive', 'hover', 'focus'],
        backgroundColor: ['dark', 'responsive', 'hover', 'focus']
    },
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tailwind will now generate every text color class and background color class additionally with the &lt;code&gt;.dark:\&lt;/code&gt; prefix with the parent class &lt;code&gt;.scheme-dark&lt;/code&gt;. So e.g. for the text color &lt;code&gt;text-white&lt;/code&gt; it will create the following css:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.text-white {
    color: #fff;
}

.scheme-dark .dark:\text-white {
    color: #fff;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we can now simply add the &lt;code&gt;scheme-dark&lt;/code&gt; to our html tag and define a text/background color like &lt;code&gt;&amp;lt;h1 class="text-black dark:text-white" &amp;gt;Hello&amp;lt;/h1&amp;gt;&lt;/code&gt; when dark mode is enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    const html = document.getElementsByTagName('html')[0];    

    function toggleDarkMode() {
        if(html.classList.contains('scheme-dark')) {
            html.classList.remove('scheme-dark');
        } else {
            html.classList.add('scheme-dark');
        }
    }
&amp;lt;/script&amp;gt;

&amp;lt;button onclick="toggleDarkMode()"&amp;gt;Toggle dark mode&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the complete tailwind config file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const plugin = require("tailwindcss/plugin");
const selectorParser = require("postcss-selector-parser");

module.exports = {
    theme: {
    ...
    },
    variants: {
        textColor: ['dark', 'responsive', 'hover', 'focus'],
        backgroundColor: ['dark', 'responsive', 'hover', 'focus']
    },
    plugins: [
        plugin(function ({ addVariant, prefix }) {
            addVariant('dark', ({ modifySelectors, separator}) =&amp;gt; {
                modifySelectors(({ selector }) =&amp;gt; {
                    return selectorParser((selectors) =&amp;gt; {
                        selectors.walkClasses((sel) =&amp;gt; {
                            sel.value = `dark${separator}${sel.value}`
                            sel.parent.insertBefore(sel, selectorParser().astSync(prefix('.scheme-dark ')))
                        })
                    }).processSync(selector)
                })
            })
        })
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you might ask me: &lt;strong&gt;What If I want to change the color when hovering the text in dark mode?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No problemo amigo! Justo addo uno plugino:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        plugin(function ({ addVariant, e }) {
            addVariant('dark-hover', ({ modifySelectors, separator}) =&amp;gt; {
                modifySelectors(({ className }) =&amp;gt; {
                    return `.scheme-dark .${e(`dark\:hover${separator}${className}`)}:hover`
                })
            })
        })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and add the variant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    variants: {
        textColor: ['responsive', 'dark', 'dark-hover', 'hover', 'focus'],
    },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;h1 class="text-black dark:text-white dark:hover:text-red-600 hover:text-blue-600"&amp;gt;Hover me&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, this is only the dark mode, you could also do the same for colored versions!&lt;/p&gt;

&lt;p&gt;If you use postcss to remove unused css (recommended!) like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
    purge: [
        './build/app/views/**/*.php',
        './build/public/**/*.php',
    ],
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then you need to add an empty div with the class &lt;code&gt;scheme-dark&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div class="scheme-dark"&amp;gt;&amp;lt;/div&amp;gt;
```


If you don't do this every `scheme-dark` class will be removed!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>design</category>
      <category>tailwindcss</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
