<?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: Bogdan Bendziukov</title>
    <description>The latest articles on DEV Community by Bogdan Bendziukov (@bogdanfromkyiv).</description>
    <link>https://dev.to/bogdanfromkyiv</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%2F1102270%2F0e1dc4da-7cfb-4495-be84-6b068f77cd28.jpg</url>
      <title>DEV Community: Bogdan Bendziukov</title>
      <link>https://dev.to/bogdanfromkyiv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bogdanfromkyiv"/>
    <language>en</language>
    <item>
      <title>Responsive Layouts Without Media Queries</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Thu, 03 Oct 2024 06:07:16 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/responsive-layouts-without-media-queries-1e73</link>
      <guid>https://dev.to/bogdanfromkyiv/responsive-layouts-without-media-queries-1e73</guid>
      <description>&lt;p&gt;How often do you use media queries when building web layouts? I’ve spent too much time on them!&lt;/p&gt;

&lt;p&gt;First you spent quite a lot of time trying to make the layout exactly like in the design. But then you need to resize your browser to all possible screen resolutions to make sure your page still looks good on all of them. And I mean to resize not only by width, but by height too - especially, if you have full height sections.&lt;/p&gt;

&lt;p&gt;Eventually, your CSS become full of lines like these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@media screen and (max-width: 1199px) { /*styles here*/ }
@media screen and (max-width: 1023px) { /*more styles here*/ }
@media screen and (max-width: 767px) { /*another styles here*/ }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s annoying! Won’t it be much easier if you can include responsiveness kind of like automatically? Of course, you still need to provide the rules for the responsiveness, but without need to write them for dozens of screen resolutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Units system
&lt;/h2&gt;

&lt;p&gt;The first thing you need to understand about responsive design is that you have to forget about pixels.&lt;/p&gt;

&lt;p&gt;I know it might be hard to switch from one unit to another, but using pixels is the voice from the past.&lt;/p&gt;

&lt;p&gt;The biggest problem with using pixels as a size unit is that you don’t get in count the user's device from which it views your website.&lt;/p&gt;

&lt;p&gt;The default root font size for modern browsers is &lt;code&gt;16px&lt;/code&gt;. That means &lt;code&gt;1rem = 16px&lt;/code&gt;. But that doesn't mean users cannot change that value in browser settings to whatever they want.&lt;/p&gt;

&lt;p&gt;So imagine the user's default browser font size is &lt;code&gt;24px&lt;/code&gt;. But you setted up the font size of the body tag to &lt;code&gt;16px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s what user expects to see:&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A3VwX4v-A9mpBvS-U" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2A3VwX4v-A9mpBvS-U"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Root font size equals 24px&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And this is what user actually sees:&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%2Fcdn-images-1.medium.com%2Fmax%2F960%2F0%2AswdeFDz6CC1r6km-" 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%2Fcdn-images-1.medium.com%2Fmax%2F960%2F0%2AswdeFDz6CC1r6km-"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Root font size equals 16px&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It especially affects people with vision problems, thus your page won’t be very accessible for them.&lt;/p&gt;

&lt;p&gt;Of course, they can always zoom your page, but in this case it will affect other opened websites, which may not be supposed to be zoomed in.&lt;/p&gt;

&lt;p&gt;BTW, the Lorem Ipsum site is a very “good” bad example of how non UX-friendly a page can look if you’re using pixels for fonts, margins, paddings etc.&lt;/p&gt;

&lt;p&gt;If you're not familiar with the relative units like rem and vw, you should check this article on the MDN, where you can deep dive into CSS units and values: &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup variables
&lt;/h2&gt;

&lt;p&gt;To make it easier to build the layout, let's set up global &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" rel="noopener noreferrer"&gt;variables&lt;/a&gt; first. Luckily, in CSS we have that opportunity. Since custom variables are subject to the cascade and inherit their value from their parent, we will define them on the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:root" rel="noopener noreferrer"&gt;&lt;code&gt;:root&lt;/code&gt;&lt;/a&gt; pseudo-class, thus they can be applied to the whole HTML document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
  --primary-color: green;
  --primary-font: Helvetica, sans-serif;
  --text-font-size: clamp(1rem, 2.08vw, 1.5rem);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks pretty simple - we define a variable name, which must begin with a double hyphen (&lt;code&gt;--&lt;/code&gt;). Then provide a variable value, which can be any valid CSS value.&lt;/p&gt;

&lt;p&gt;Then we can use those variables for any element or even pseudo-class in the document using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var" rel="noopener noreferrer"&gt;&lt;code&gt;var()&lt;/code&gt;&lt;/a&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;color: var(--primary-color);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, we can use our &lt;code&gt;--primary-color&lt;/code&gt; variable for all the headings on the page 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;h1, h2, h3, h4, h5, h6 {
  color: var(--primary-color);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the primary colour will use quite a lot of different elements on the page, it’s very handy to use the variable instead of writing each time the colour itself.&lt;/p&gt;

&lt;p&gt;The last variable &lt;code&gt;--text-font-size: clamp(1rem, 2.08vw, 1.5rem)&lt;/code&gt; might look odd: what’s the clamp and what’s it doing on the font size variable?&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic font scaling
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clamp" rel="noopener noreferrer"&gt;clamp()&lt;/a&gt; CSS function clamps a middle value within a range of values between a defined minimum bound and a maximum bound.&lt;/p&gt;

&lt;p&gt;You need to provide a minimum value (which is &lt;code&gt;1rem&lt;/code&gt; from the example above), a preferred value (&lt;code&gt;2.08vw&lt;/code&gt;) and the maximum allowed value (&lt;code&gt;1.5rem&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The most tricky part here is to set the preferred value. It should be in some viewport relative units (like &lt;code&gt;vw&lt;/code&gt; or &lt;code&gt;vh&lt;/code&gt;). Thus when a user resizes its browser or changes the device’s orientation the font size will scale proportionally.&lt;/p&gt;

&lt;p&gt;I’ve made this formula for calculating the preferred value:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;value = AMValue * remInPx / (containerWidth / 100)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here comes an explanation, no panic:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AMValue&lt;/code&gt; - arithmetic mean, between the minimum and maximum allowed values in rem. In our example it equals &lt;code&gt;(1rem + 1.5rem) / 2 = 1.25rem&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;remInPx&lt;/code&gt; - default size of &lt;code&gt;1rem&lt;/code&gt; in pixels, depending on your design, usually it equals to &lt;code&gt;16px&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;containerWidth&lt;/code&gt; - the maximum width of your content container block (in pixels). We need to divide that value to 100 to get the 1% of the width. In the example it equals &lt;code&gt;960px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So if you replace the arguments in that equation with real numbers you will get:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;value = 1.25 \* 16 / (960 / 100) = 2.08&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let’s check how it will scale:&lt;/p&gt;

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

&lt;p&gt;I know it’s not a perfect solution. Besides, we attach again to pixels, when calculating the preferred value. It’s just one of many possible options to make our fonts scale between viewports sizes.&lt;/p&gt;

&lt;p&gt;You can use other CSS functions like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/min" rel="noopener noreferrer"&gt;&lt;code&gt;min()&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/max" rel="noopener noreferrer"&gt;&lt;code&gt;max()&lt;/code&gt;&lt;/a&gt;, or create a custom method to calculate the preferred value in the &lt;code&gt;clamp()&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;I wrote an article about dynamic font size scaling, only for pixel units. It’s a bit outdated, but still you might find it helpful:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/bogdanfromkyiv/dynamic-font-size-using-only-css3-31kj"&gt;Dynamic font-size using only CSS3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, enough of the fonts, let’s go further to the layout!&lt;/p&gt;

&lt;h2&gt;
  
  
  Layout with equal column width
&lt;/h2&gt;

&lt;p&gt;Let’s start with some simple layout with 6 equal columns.&lt;/p&gt;

&lt;p&gt;With media queries you need to write a bunch of extra CSS code to handle how they should wrap on different screen sizes. 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;/* by default we have 6 columns */
.column {
  float: left;
  width: calc(100% / 6);
}
/* decrease to 4 columns on the 1200px breakpoint */
@media screen and (max-width: 1200px) {
  .column {
    width: calc(100% / 4);
  }
}
/* decrease to 3 columns on the 1024px breakpoint */
@media screen and (max-width: 1024px) {
  .column {
    width: calc(100% / 3);
  }
}
/* finally, decrease to 2 columns for the viewport width less than or equal to 768px */
@media screen and (max-width: 768px) {
  .column {
    width: calc(100% / 2);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Woah! That’s a lot of code, I must say! Wouldn't it be better to just make it scale automatically?&lt;/p&gt;

&lt;p&gt;And here’s how, thanks to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout" rel="noopener noreferrer"&gt;CSS grid layout&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.row {
  display: grid;
  grid-template-columns: repeat( auto-fit, minmax(10em, 1fr) );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;All we need to do is to set the parent block of our columns to be displayed as a grid. And then, create a template for our columns, using &lt;code&gt;grid-template-columns&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;This is called RAM technique (stands for Repeat, Auto, Minmax) in CSS, you can read about it in more details here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/bogdanfromkyiv/ram-technique-in-css-1lk7"&gt;RAM Technique in CSS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In that property we use the CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/repeat" rel="noopener noreferrer"&gt;&lt;code&gt;repeat()&lt;/code&gt;&lt;/a&gt; function.&lt;/p&gt;

&lt;p&gt;The first argument is set to &lt;code&gt;auto-fit&lt;/code&gt;, which means it FITS the CURRENTLY AVAILABLE columns into the space by expanding them so that they take up any available space. There’s another value for that argument: &lt;code&gt;auto-fill&lt;/code&gt;. To understand the difference between them check this pen:&lt;/p&gt;

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

&lt;p&gt;Also, I highly recommend to read this article from CSS tricks about auto sizing columns in CSS grid: &lt;a href="https://css-tricks.com/auto-sizing-columns-css-grid-auto-fill-vs-auto-fit/" rel="noopener noreferrer"&gt;https://css-tricks.com/auto-sizing-columns-css-grid-auto-fill-vs-auto-fit/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The second argument is using another function &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/minmax" rel="noopener noreferrer"&gt;&lt;code&gt;minmax()&lt;/code&gt;&lt;/a&gt;, which defines the size of each column. In our example each column should not be less than 10em and should be stretched to the remaining space.&lt;/p&gt;

&lt;p&gt;Looks fine, but we have a problem - the number of columns can be bigger than 6!&lt;/p&gt;

&lt;p&gt;To make a limit of columns, we need some custom formula again. But hey, it’s still in CSS! And it’s not that scary, basically, you just need to provide a gap for the grid, a minimal column width and the max number of columns.&lt;/p&gt;

&lt;p&gt;Here’ the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.grid-container {

  /** * User input values. */
  --grid-layout-gap: 1em;
  --grid-column-count: 4;
  --grid-item--min-width: 15em;

  /** * Calculated values. */
  --gap-count: calc(var(--grid-column-count) - 1);
  --total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
  --grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));

  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr));
  grid-gap: var(--grid-layout-gap);

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

&lt;/div&gt;



&lt;p&gt;And here’s what we achieve with that:&lt;/p&gt;

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

&lt;p&gt;As you can see, we can use the relative values for the columns min width and gap, which makes this code like the perfect solution. Until they build the native CSS property for that, of course 😄&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Important notice!&lt;/em&gt;&lt;/strong&gt; If you don't need a gap between columns, you need to set it to 0px or 0em, not just 0 (pure number). I mean you have to provide the units, otherwise the code won’t work.&lt;/p&gt;

&lt;p&gt;I’ve found that solution on &lt;a href="https://css-tricks.com/" rel="noopener noreferrer"&gt;CSS tricks&lt;/a&gt;, so in case you want to dive deeper to how that formula works, here’s the original article about it: &lt;a href="https://css-tricks.com/an-auto-filling-css-grid-with-max-columns/" rel="noopener noreferrer"&gt;https://css-tricks.com/an-auto-filling-css-grid-with-max-columns/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Layout with different column width
&lt;/h2&gt;

&lt;p&gt;The solution above works perfectly for the grids with equal width of the columns. But how to handle layouts with unequal columns? The most common example is a content area with a sidebar, so let’s work with this one.&lt;/p&gt;

&lt;p&gt;Here’s a simple markup of the content area along with sidebar:&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;section class="content"&amp;gt;
  &amp;lt;aside&amp;gt;
    &amp;lt;h2&amp;gt;This is sidebar&amp;lt;/h2&amp;gt;
    &amp;lt;section class="grid"&amp;gt;
      &amp;lt;div class="grid-item"&amp;gt;Grid Item 1&amp;lt;/div&amp;gt;
      &amp;lt;div class="grid-item"&amp;gt;Grid Item 2&amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/aside&amp;gt;
  &amp;lt;article&amp;gt;
    &amp;lt;h2&amp;gt;This is content&amp;lt;/h2&amp;gt;
    &amp;lt;section class="grid"&amp;gt;
      &amp;lt;div class="grid-item"&amp;gt;Grid Item 1&amp;lt;/div&amp;gt;
      &amp;lt;div class="grid-item"&amp;gt;Grid Item 2&amp;lt;/div&amp;gt;
      &amp;lt;div class="grid-item"&amp;gt;Grid Item 3&amp;lt;/div&amp;gt;
      &amp;lt;div class="grid-item"&amp;gt;Grid Item 4&amp;lt;/div&amp;gt;
    &amp;lt;/section&amp;gt;
  &amp;lt;/article&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the &lt;code&gt;.content&lt;/code&gt; section let’s use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox" rel="noopener noreferrer"&gt;flex box layout&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.content {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 1rem;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The flex-wrap property here is important and should be set as wrap in order to force the columns (sidebar and content area) stack under each other.&lt;/p&gt;

&lt;p&gt;For the sidebar and content columns we need to set flex properties like grow and basis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Sidebar */
.content &amp;gt; aside {
  border: 1px solid var( - primary-color);
  padding: var( - primary-padding);
  flex-grow: 1;
  flex-basis: 15em;
}

/* Content */
.content &amp;gt; article {
  border: 1px solid var( - primary-color);
  padding: var( - primary-padding);
  flex-grow: 3;
  flex-basis: 25em;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-basis" rel="noopener noreferrer"&gt;&lt;code&gt;flex-basis&lt;/code&gt;&lt;/a&gt; property sets the initial size of the flex item. Basically, it’s a minimum width which the flex item should have.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow" rel="noopener noreferrer"&gt;&lt;code&gt;flex-grow&lt;/code&gt;&lt;/a&gt; property sets the flex grow factor — similar to the proportion of the flex item compared to the other flex items. It’s a very rough and approximate explanation, to understand better the &lt;code&gt;flex-grow&lt;/code&gt; property I highly recommend to read this article from CSS tricks: &lt;a href="https://css-tricks.com/flex-grow-is-weird/" rel="noopener noreferrer"&gt;https://css-tricks.com/flex-grow-is-weird/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So if we set the &lt;code&gt;flex-grow: 1&lt;/code&gt; for the sidebar and &lt;code&gt;flex-grow: 3&lt;/code&gt; for the content area, that means the content area will take &lt;strong&gt;&lt;em&gt;approximately&lt;/em&gt;&lt;/strong&gt; three times more space than the sidebar.&lt;/p&gt;

&lt;p&gt;I also added the grid section from the previous example to demonstrate that it works inside the flex layout as well.&lt;/p&gt;

&lt;p&gt;Here’s what we have in the final result:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Stackable columns
&lt;/h2&gt;

&lt;p&gt;It’s pretty common, when you have a grid layout where text comes next to image on one row and then in reverse order on the next row:&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%2Fcdn-images-1.medium.com%2Fmax%2F988%2F0%2AJrcAmDvxm2DkL3Vk" 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%2Fcdn-images-1.medium.com%2Fmax%2F988%2F0%2AJrcAmDvxm2DkL3Vk"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But when the columns become stacked you want them to be in a specific order, where text comes always before image, but they don’t:&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%2Fcdn-images-1.medium.com%2Fmax%2F327%2F0%2AfAT_at0kkymmYrP9" 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%2Fcdn-images-1.medium.com%2Fmax%2F327%2F0%2AfAT_at0kkymmYrP9"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To achieve that we need to detect somehow when the columns become stacked.&lt;/p&gt;

&lt;p&gt;Unfortunately, it’s impossible (yet) to do that with pure CSS. So we need to add some JS code to detect that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
* Detect when elements become wrapped
*
* @param {NodeList} items - list of elements to check
* @returns {array} Array of items that were wrapped
*/
const detectWrap = (items) =&amp;gt; {
  let wrappedItems = [];
  let prevItem = {};
  let currItem = {};

  for (let i = 0; i &amp;lt; items.length; i++) {
    currItem = items[i].getBoundingClientRect();

    if (prevItem) {
      let prevItemTop = prevItem.top;
      let currItemTop = currItem.top;

      // if current's item top position is different from previous
      // that means that the item is wrapped
      if (prevItemTop &amp;lt; currItemTop) {
        wrappedItems.push(items[i]);
      }

    }

    prevItem = currItem;

  }

  return wrappedItems;
};

const addWrapClasses = (wrapper, cover) =&amp;gt; {
  const items = wrapper.querySelectorAll(":scope &amp;gt; *");

  // remove ".wrapped" classes to detect which items was actually wrapped
  cover.classList.remove("wrapped");

  // only after that detect wrap items
  let wrappedItems = detectWrap(items); // get wrapped items

  // if there are any elements that were wrapped - add a special class to menu
  if (wrappedItems.length &amp;gt; 0) {
    cover.classList.add("wrapped");
  }

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

&lt;/div&gt;



&lt;p&gt;The function &lt;code&gt;addWrapClasses()&lt;/code&gt; accepts two arguments.&lt;/p&gt;

&lt;p&gt;The first one is wrapper — it’s a parent element of the items which we should check whether they are wrapped (stacked) or not.&lt;/p&gt;

&lt;p&gt;The second argument cover is an element to which we apply a special CSS class &lt;code&gt;.wrapped&lt;/code&gt;. Using this class you can change your layout when the columns become stacked.&lt;/p&gt;

&lt;p&gt;If you want to apply the &lt;code&gt;.wrapped&lt;/code&gt; class directly to the wrapper element you can pass the same element as the second argument.&lt;/p&gt;

&lt;p&gt;For better understanding my “wonderful” explanation please see the pen below, hope it will become more clear for you:&lt;/p&gt;

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

&lt;p&gt;You can also use it to detect when the header menu should be collapsed into the burger. You can read about that case in my article here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/bogdanfromkyiv/an-easy-way-to-make-an-auto-responsive-menu-2dh4"&gt;An Easy Way to Make an Auto Responsive Menu&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining all together
&lt;/h3&gt;

&lt;p&gt;Here’s a pen with all the techniques I mentioned in this article combined:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;I’ve used the techniques from this article in my recent project and it worked very well. The web pages look fine on every screen with no need to optimise them manually on multiple breakpoints.&lt;/p&gt;

&lt;p&gt;Of course I will be lying if I tell you I didn’t use media queries at all. It all depends on the design and how flexible you can be with modifying page layout. Sometimes it’s much faster and simpler just to add a couple of breakpoints and then fix CSS for them. But I think eventually CSS media queries will be replaced by CSS functions like clamp() which allow developers to create responsive layouts automatically.&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv" rel="noopener noreferrer"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace to you!&lt;/p&gt;

</description>
      <category>responsivewebdesign</category>
      <category>web</category>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>An Easy Way to Make an Auto Responsive Menu</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Thu, 03 Oct 2024 06:07:00 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/an-easy-way-to-make-an-auto-responsive-menu-2dh4</link>
      <guid>https://dev.to/bogdanfromkyiv/an-easy-way-to-make-an-auto-responsive-menu-2dh4</guid>
      <description>&lt;p&gt;In this article I’m gonna show you a little hack about how to automatically collapse header’s menu into a “burger” and vice-versa.&lt;/p&gt;

&lt;p&gt;So the idea is simple — when the menu’s items cannot fit into the header they should be hidden and the mobile menu toggle button (the burger) will be shown. And it should be done automatically, without css media queries. So your menu still looks good on any device regardless of number of items, their font-sizes etc.&lt;/p&gt;

&lt;p&gt;This is how it looks like:&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%2Fn4sw48u9b9e9lieund0y.gif" 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%2Fn4sw48u9b9e9lieund0y.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ve made a very basic markup, with just a logo and a menu.&lt;br&gt;&lt;br&gt;
Then added some CSS to make it look nicer.&lt;/p&gt;

&lt;p&gt;So in the “desktop” mode the header looks like this:&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%2Fahyvrsfmxl2w7nqiw2af.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%2Fahyvrsfmxl2w7nqiw2af.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But when the viewport is too small to fit that menu, the menu items become “wrapped”:&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%2Fbdm7kba1muh3igqzouv7.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%2Fbdm7kba1muh3igqzouv7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We don’t need this! If there’s no space for them — then it’s time to hide the menu and show the menu toggle instead.&lt;/p&gt;

&lt;p&gt;This is where a bit of JS gets handy. We need just to detect when the top position of any of the menu items will be different from the rest ones.&lt;/p&gt;

&lt;p&gt;Here’s the trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
 * Detect when elements become wrapped
 *
 * @param {NodeList} items - list of elements to check
 * @returns {array} Array of items that were wrapped
 */
const detectWrap = (items) =&amp;gt; {
  let wrappedItems = [];
  let prevItem = {};
  let currItem = {};

  for (let i = 0; i &amp;lt; items.length; i++) {
    currItem = items[i].getBoundingClientRect();

    if (prevItem) {
      let prevItemTop = prevItem.top;
      let currItemTop = currItem.top;

      // if current's item top position is different from previous
      // that means that the item is wrapped
      if (prevItemTop &amp;lt; currItemTop) {
        wrappedItems.push(items[i]);
      }
    }

    prevItem = currItem;
  }

  return wrappedItems;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can use that function to check if menu items were wrapped and if so — hide the menu and show a mobile toggle button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const addWrapClasses = () =&amp;gt; {
  const menu = document.querySelector(".menu");
  const menuItems = document.querySelectorAll(".menu &amp;gt; ul &amp;gt; li");

  // remove ".wrapped" classes to detect which items was actually wrapped
  menu.classList.remove("wrapped");

  // only after that detect wrap items
  let wrappedItems = detectWrap(menuItems); // get wrapped items

  // if there are any elements that were wrapped - add a special class to menu
  if (wrappedItems.length &amp;gt; 0) {
    menu.classList.add("wrapped");
  }
};

// execute function on page load
addWrapClasses();

// execute function on window resize
window.addEventListener("resize", addWrapClasses);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it looks fine:&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%2F5o0ixni8j0bvo4ii33k6.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%2F5o0ixni8j0bvo4ii33k6.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s the full demo of what we ahcieved:&lt;/p&gt;

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

&lt;p&gt;Check another trick for responsive font-size with only CSS:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/bogdanfromkyiv/dynamic-font-size-using-only-css3-31kj"&gt;Dynamic font-size using only CSS3&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv" rel="noopener noreferrer"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace to you!&lt;/p&gt;

</description>
      <category>responsivewebdesign</category>
      <category>javascript</category>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>No JS required — you can do this with CSS!</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Fri, 20 Sep 2024 08:35:52 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/no-js-required-you-can-do-this-with-css-14fa</link>
      <guid>https://dev.to/bogdanfromkyiv/no-js-required-you-can-do-this-with-css-14fa</guid>
      <description>&lt;p&gt;The less you use JS code on your site the better performance you’ll have.&lt;/p&gt;

&lt;p&gt;Why? Here are several reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lesser JavaScript means lesser &lt;a href="https://web.dev/articles/tbt" rel="noopener noreferrer"&gt;TBT&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;CSS is faster to parse and execute than JavaScript;&lt;/li&gt;
&lt;li&gt;When you use JS, the layout will be re-rendered, which is not the case for CSS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, if you have an error in your CSS, it won’t stop from loading your page. While an error in JS code may (and often will) break the whole page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer! I’m not telling you to stop using JS, of course not 😅&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But in cases when you can easily replace it with CSS — please do that.&lt;/p&gt;

&lt;p&gt;Have a look at a few examples of useful features for your site using only CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sub menu on hover
&lt;/h3&gt;

&lt;p&gt;Let’s start with something simple. To make a sub menu there’s no need to use JS. We all know this pseudoclass &lt;code&gt;:hover&lt;/code&gt;, so don’t hesitate to use it.&lt;/p&gt;

&lt;p&gt;Here’s a simple menu markup:&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;nav&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;About&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Blog 🠋&amp;lt;/a&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;News&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Interviews&amp;lt;/a&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Travel&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Art&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Tech&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;a href="#"&amp;gt;Contacts&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generally, to make the sub menu hidden people often use &lt;code&gt;display: none;&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;I prefer to use a combination of &lt;code&gt;opacity&lt;/code&gt; and &lt;code&gt;visibility&lt;/code&gt; CSS properties, to make the hover appearance smooth (browsers cannot animate the display CSS property, yet). The &lt;code&gt;visibility: hidden;&lt;/code&gt; is used to make our sub menu non-focusable until it becomes visible (until its parent element hovered).&lt;/p&gt;

&lt;p&gt;To prevent submenu from immediate disappear on hover out of menu, we should add a &lt;code&gt;transition-delay&lt;/code&gt; to our sub-menu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* don't forget to set the relative position for the parent element of the sub menu */
nav &amp;gt; ul &amp;gt; li {
  position: relative;
}
/* sub menu is hidden by default */
nav &amp;gt; ul ul {
  position: absolute;
  opacity: 0;
  visibility: hidden;
  translate: 0 25%;
  transition: all 250ms cubic-bezier(.33,.65,.67,.81);
  transition-delay: 300ms;
}
/* make it visible on hover */
nav &amp;gt; ul &amp;gt; li:hover &amp;gt; ul {
  opacity: 1;
  visibility: visible;
  translate: 0 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here’s the final result:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Tabbed content
&lt;/h2&gt;

&lt;p&gt;Combining &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;input type="radio"&amp;gt;&lt;/code&gt; can do magic! And all because when you click on a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;, the browser will automatically pass the focus to its associated input. In case with the radio input, that means it will be checked.&lt;/p&gt;

&lt;p&gt;The layout is simple: we create several pairs of input-label elements and the related number of divs for content.&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;!-- Radio buttons --&amp;gt;
&amp;lt;input type="radio" id="tab1" name="tabs" checked&amp;gt;
&amp;lt;label for="tab1"&amp;gt;Tab 1&amp;lt;/label&amp;gt;

&amp;lt;input type="radio" id="tab2" name="tabs"&amp;gt;
&amp;lt;label for="tab2"&amp;gt;Tab 2&amp;lt;/label&amp;gt;

&amp;lt;input type="radio" id="tab3" name="tabs"&amp;gt;
&amp;lt;label for="tab3"&amp;gt;Tab 3&amp;lt;/label&amp;gt;

&amp;lt;!-- Content --&amp;gt;
&amp;lt;div class="tab-content" id="content1"&amp;gt;
  &amp;lt;h2&amp;gt;Content for Tab 1&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;This is the content for the first tab.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class="tab-content" id="content2"&amp;gt;
  &amp;lt;h2&amp;gt;Content for Tab 2&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;This is the content for the second tab.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div class="tab-content" id="content3"&amp;gt;
  &amp;lt;h2&amp;gt;Content for Tab 3&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;This is the content for the third tab.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the beauty of CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Hide radio buttons */
input[type="radio"] {
  /*display: none;*/

  /* To make radio buttons accessbile by a keyboard, let's hide them visually only */
  border: 0;
  clip: rect(1px, 1px, 1px, 1px);
  clip-path: inset(50%);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  width: 1px;
  word-wrap: normal !important;
}

/* Highlight the selected tab when using a keyboard */
input[type="radio"]:focus + label {
  outline: 2px solid;
}

/* Style labels as tabs */
label {
  display: inline-block;
  padding: 10px 20px;
  margin: 0;
  cursor: pointer;
  background-color: #eee;
  border: 1px solid #ccc;
  border-bottom: none;
  transition: background-color 0.3s ease;
}

/* Style active tab */
  input[type="radio"]:checked + label {
  background-color: #fff;
  border-bottom: 1px solid #fff;
  font-weight: bold;
}

/* Style content areas */
.tab-content {
  display: none;
  padding: 20px;
  border: 1px solid #ccc;
  border-top: none;
  background-color: #fff;
}

/* Show active content */
#tab1:checked ~ #content1,
#tab2:checked ~ #content2,
#tab3:checked ~ #content3 {
  display: block;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSS has a native &lt;code&gt;:checked&lt;/code&gt; selector for the checked inputs (both radios and checkboxes).&lt;/p&gt;

&lt;p&gt;And thanks to the &lt;code&gt;~&lt;/code&gt; (tilde), which is officially called the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Subsequent-sibling_combinator" rel="noopener noreferrer"&gt;subsequent-sibling combinator&lt;/a&gt;, we can toggle visibility of the content blocks. The only requirement is that content divs should be placed AFTER radio inputs.&lt;/p&gt;

&lt;p&gt;Here’s the final result:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Snap block
&lt;/h2&gt;

&lt;p&gt;Ever scrolled YouTube Shorts on a desktop? After each wheel scroll the next video snaps to the top of the page:&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%2Fcdn-images-1.medium.com%2Fmax%2F480%2F0%2AMOeAtJyZmL9PFmfQ" 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%2Fcdn-images-1.medium.com%2Fmax%2F480%2F0%2AMOeAtJyZmL9PFmfQ" alt="CSS scroll-snap in action. Even YouTube uses it!"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;CSS scroll-snap in action. Even YouTube uses it!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And if you think that it’s some complex JS code — you’re wrong! It’s a native CSS feature called &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_scroll_snap" rel="noopener noreferrer"&gt;&lt;code&gt;scroll-snap&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So first, you need to set up a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-type" rel="noopener noreferrer"&gt;scroll-snap-type&lt;/a&gt; for the parent container. To make it work like in YouTube Shorts, we will set it to y mandatory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.scroll-container {
  display: flex;
  flex-direction: column;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  width: 300px;
  height: 80%;
  background-color: #ddd;
  padding: 20px;
  box-sizing: border-box;
  scroll-padding: 10px;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means the scroll container snaps in its vertical axis only and the content must snap to a snap position if it isn’t currently scrolled. Check MDN Docs for the detailed explanation of each possible value for this property.&lt;/p&gt;

&lt;p&gt;And for each of the scrolled items we just need to provide a snap position of the item within its snap area. In other words, do we want the item to be snapped to the top (start), center or to the bottom (end). It’s possible thanks to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-align" rel="noopener noreferrer"&gt;&lt;code&gt;scroll-snap-align&lt;/code&gt;&lt;/a&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.scroll-item {
  scroll-snap-align: start;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at the demo, it’s really very cool feature and using CSS only:&lt;/p&gt;

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

&lt;p&gt;Those were some tricks popped in my head, but they are just the tip of the iceberg. If you want the second part please click on the “like” button so I can see it is something you interested in 😊&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv" rel="noopener noreferrer"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace to you!&lt;/p&gt;

</description>
      <category>css3</category>
      <category>web</category>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>7 Old-School Practices in HTML Should Be Avoided</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Fri, 20 Sep 2024 08:06:26 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/7-old-school-practices-in-html-should-be-avoided-3o75</link>
      <guid>https://dev.to/bogdanfromkyiv/7-old-school-practices-in-html-should-be-avoided-3o75</guid>
      <description>&lt;p&gt;Just some daily habits in HTML coding we all should get rid off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using type attribute for &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;❌ Stop writing 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;style type="text/css"&amp;gt;/* CSS styles here */&amp;lt;/style&amp;gt;
&amp;lt;script type="text/javascript"&amp;gt;/* JS code here */&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Instead, you should simply write:&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;style&amp;gt;/* CSS styles here */&amp;lt;/style&amp;gt;
&amp;lt;script&amp;gt;/* JS code here */&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need to specify the type attribute, modern browsers are smart enough to understand that &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; is for CSS, &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; is for JavaScript and V for Vendetta!&lt;/p&gt;

&lt;h2&gt;
  
  
  Accordion (FAQ) block requires JS code
&lt;/h2&gt;

&lt;p&gt;Easy! Watch:&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;details&amp;gt;
  &amp;lt;summary&amp;gt;To be or not to be?&amp;lt;/summary&amp;gt;
  &amp;lt;div&amp;gt;
    That is the question!
  &amp;lt;/div&amp;gt;
&amp;lt;/details&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the result:&lt;/p&gt;

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

&lt;p&gt;And with some extra styles:&lt;/p&gt;

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

&lt;p&gt;Ok, but what about smooth open/close action, you might ask.&lt;/p&gt;

&lt;p&gt;It’s tricky, so if you need fancy smooth animation you should include some JavaScript.&lt;/p&gt;

&lt;p&gt;But if you can deal with simple one-direction CSS transition, here you go:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Use &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; only once per page
&lt;/h2&gt;

&lt;p&gt;Despite some people thinking that &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; elements represent header and footer of the &lt;strong&gt;&lt;em&gt;page&lt;/em&gt;&lt;/strong&gt; respectively, it’s a false statement.&lt;/p&gt;

&lt;p&gt;Those elements relate to the nearest sectioning content, which means being a child of one of the following elements: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thus, you can (actually, you &lt;strong&gt;&lt;em&gt;should&lt;/em&gt;&lt;/strong&gt; ) use &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; and/or &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; elements when you create another section on your page.&lt;/p&gt;

&lt;p&gt;Here’s an example of an author info box with the right elements:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Using &lt;code&gt;frameborder="0"&lt;/code&gt; to remove border around &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;It’s a deprecated attribute of an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; element. Just like you (hopefully) don’t use align attribute to handle text alignment, you should avoid using frameborder attribute.&lt;/p&gt;

&lt;p&gt;Instead, use the CSS property border to handle &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; borders.&lt;/p&gt;

&lt;p&gt;Nonetheless, when you right click on a YouTube or Vimeo video and then click on “Copy embed code” you get an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; element with that frameborder="0" attribute. So after applying embed code to your page make sure to remove that frameborder attribute and set border property to your &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; in CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Include support for IE 8
&lt;/h2&gt;

&lt;p&gt;❌ The following script provides basic HTML5 styling for Internet Explorer 6–8:&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;!--[if lt IE 9]&amp;gt;
  &amp;lt;script src="scripts/html5shiv.js"&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;![endif]--&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seriously, it’s a voice from the past. Stop including supporting scripts for Internet Explorer. And I’m not only talking about IE 8, but ALL Explorers! Even Microsoft stopped support for IE 11 in June 2022. So you should.&lt;/p&gt;

&lt;h2&gt;
  
  
  Randomly choose heading tags
&lt;/h2&gt;

&lt;p&gt;I hope you already know there should be only one &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; tag on the page as for the primary title. But what about the rest of the heading tags &lt;code&gt;&amp;lt;h2&amp;gt;…&amp;lt;h6&amp;gt;&lt;/code&gt; ?&lt;/p&gt;

&lt;p&gt;We used to choose them approximately, depending on the size of the heading from the design. So if we have some heading, which looks small in the Figma mockup, we want to set the &lt;code&gt;&amp;lt;h4&amp;gt;&lt;/code&gt; tag, for example. Despite the previous heading tag on the page being &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;That’s very bad practice. Your markup becomes invalid and messy for screen readers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remember, accessibility matters!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should provide heading tags depending on page structure, not page design. And those tags should be in a descending order. So if your last heading tag was &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;, the next one should be either &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; (if it’s another section on the page) or &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; if it’s a heading for the child section of &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; heading.&lt;/p&gt;

&lt;p&gt;❌ So DO NOT do like this (randomly provided heading tags):&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&amp;gt;Primary title&amp;lt;/h1&amp;gt;
&amp;lt;section&amp;gt;
  &amp;lt;h3&amp;gt;Section title&amp;lt;/h3&amp;gt;
  &amp;lt;p&amp;gt;Some text inside the section&amp;lt;/p&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;section&amp;gt;
  &amp;lt;h3&amp;gt;Another section title&amp;lt;/h3&amp;gt;
  &amp;lt;p&amp;gt;Some text inside the section&amp;lt;/p&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;h5&amp;gt;Subtitle&amp;lt;/h5&amp;gt;
    &amp;lt;p&amp;gt;Some text inside the section&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Keep the order of your heading tags and logic structure (keep the descending order of headings):&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&amp;gt;Primary title&amp;lt;/h1&amp;gt;
&amp;lt;section&amp;gt;
  &amp;lt;h2&amp;gt;Section title&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;Some text inside the section&amp;lt;/p&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;section&amp;gt;
  &amp;lt;h2&amp;gt;Another section title&amp;lt;/h2&amp;gt;
  &amp;lt;p&amp;gt;Some text inside the section&amp;lt;/p&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;h3&amp;gt;Subtitle&amp;lt;/h3&amp;gt;
    &amp;lt;p&amp;gt;Some text inside the section&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/section&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Adding &lt;code&gt;="1"&lt;/code&gt; for boolean attributes
&lt;/h2&gt;

&lt;p&gt;I mean such attributes, as disabled for inputs, loop, muted or autoplay for videos etc. The mere fact that those attributes present means they are equal to true.&lt;/p&gt;

&lt;p&gt;It won’t affect functionality, so your input will still be disabled, but this is an error for the W3C validator and just an unnecessary piece of code.&lt;/p&gt;

&lt;p&gt;❌ So DO NOT code like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;input type="text" value="This input is disabled" disabled="1" /&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;✅ Keep it simple:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;input type="text" value="This input is disabled" disabled /&amp;gt;&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv" rel="noopener noreferrer"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace to you!&lt;/p&gt;

</description>
      <category>css</category>
      <category>a11y</category>
      <category>validation</category>
      <category>html</category>
    </item>
    <item>
      <title>Enhancing Web Accessibility: Locking the TAB Button within Modals and Menus</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Wed, 26 Jul 2023 20:36:10 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/enhancing-web-accessibility-locking-the-tab-button-within-modals-and-menus-3774</link>
      <guid>https://dev.to/bogdanfromkyiv/enhancing-web-accessibility-locking-the-tab-button-within-modals-and-menus-3774</guid>
      <description>&lt;p&gt;This is a very important accessibility feature to use on your site.&lt;/p&gt;

&lt;p&gt;You might not know this, but many users use keyboard navigation. Some of them have motor disabilities, some may use a keyboard for navigation because of preference or efficiency.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The point is your site must be accessible with the keyboard alone.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First of all, I hope you already know you should highlight all focusable elements on your site (like links and buttons) when they are on focus. At least when they are focused by the keyboard TAB button.&lt;/p&gt;

&lt;p&gt;There’s a very useful pseudo-class in CSS called &lt;code&gt;:focus-visible&lt;/code&gt;. In simple words, it applies to an element when it is focused by the TAB button. You can read more about it on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But let’s get back to the case.&lt;/p&gt;

&lt;p&gt;So you have a site with a fullscreen menu or some other section that opens full screen size. Something like this:&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AI1BfTpYeXbjqsg6T" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AI1BfTpYeXbjqsg6T"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A site with a fullscreen menu&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then a user reaches to that section with a keyboard navigation (TAB button) and opens it by hitting the ENTER button:&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%2Fcdn-images-1.medium.com%2Fmax%2F600%2F0%2AT_p3TjQPRMx6LtAW" 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%2Fcdn-images-1.medium.com%2Fmax%2F600%2F0%2AT_p3TjQPRMx6LtAW"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What should happen after the user reaches the last link in the section?&lt;/p&gt;

&lt;p&gt;Unfortunately, it’s a very common case when the user keeps navigating to the next links &lt;strong&gt;&lt;em&gt;outside&lt;/em&gt;&lt;/strong&gt; that full screen section.&lt;/p&gt;

&lt;p&gt;As a result, users get totally lost on your site. They don’t see focused links anymore (because they are hidden below the fullscreen section) and yet they keep navigating on the page.&lt;/p&gt;

&lt;p&gt;A good example of how it &lt;strong&gt;&lt;em&gt;should&lt;/em&gt;&lt;/strong&gt; work demonstrates &lt;a href="https://fancyapps.com/" rel="noopener noreferrer"&gt;the Fancybox JS plugin&lt;/a&gt;. Go to their &lt;a href="https://fancyapps.com/fancybox/#examples" rel="noopener noreferrer"&gt;examples section&lt;/a&gt;, click on any picture and then navigate through the opened modal only with your keyboard. You won’t get outside that modal until you close it.&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%2Fcdn-images-1.medium.com%2Fmax%2F600%2F0%2AX2gYEq4E70hukRMz" 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%2Fcdn-images-1.medium.com%2Fmax%2F600%2F0%2AX2gYEq4E70hukRMz"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Lock TAB inside Fancybox&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So what’s the solution?&lt;/p&gt;

&lt;p&gt;It’s quite simple — when the fullscreen section was opened, the user should be able to navigate only within that section by a keyboard.&lt;/p&gt;

&lt;p&gt;This is what we expect to get:&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%2Fcdn-images-1.medium.com%2Fmax%2F600%2F0%2A0ARLE4UAs2jujcgn" 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%2Fcdn-images-1.medium.com%2Fmax%2F600%2F0%2A0ARLE4UAs2jujcgn"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To achieve that, let’s write some JavaScript code to lock the TAB within the fullscreen menu:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Get the menu button element
const menuBtn = document.querySelector(".menu-btn");

// Get the close menu button element
const closeMenuBtn = document.querySelector(".close-btn");

// Get the fullscreen menu element
const fullscreenMenu = document.getElementById("fullscreenMenu");

// Function to toggle the menu
function toggleMenu() {
  fullscreenMenu.classList.toggle("active");
  menuBtn.classList.toggle("active");
}

// Add event listener to the menu button for the 'click' event
menuBtn.addEventListener("click", toggleMenu);
closeMenuBtn.addEventListener("click", toggleMenu);

// Add event listener to the 'keydown' event on the document
document.addEventListener("keydown", function (e) {
  const target = e.target;
  const shiftPressed = e.shiftKey;

  // If TAB key pressed
  if (e.keyCode === 9) {
    // If inside a fullscreen menu (determined by attribute role="dialog")
    if (target.closest('[role="dialog"]')) {
      // Find the first or the last input element in the dialog parent (depending on whether Shift was pressed).
      // Get the focusable elements (links and buttons)
      let focusElements = target
        .closest('[role="dialog"]')
        .querySelectorAll("a[href], button");
      let borderElem = shiftPressed
        ? focusElements[0]
        : focusElements[focusElements.length - 1];

      if (borderElem) {
        // If the current target element is the first or last focusable element in the dialog, prevent the default behaviour.
        if (target === borderElem) {
          e.preventDefault();

          // move focus to the first element when the last one is reached and vice versa
          borderElem === focusElements[0]
            ? focusElements[focusElements.length - 1].focus()
            : focusElements[0].focus();
        }
      }
    }
  }
});


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

&lt;/div&gt;
&lt;p&gt;Here’s the demo how it should be:&lt;/p&gt;

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

&lt;p&gt;Thus, we can prevent elements from focusing outside full screen menus or modal popups while they are opened.&lt;/p&gt;




&lt;p&gt;Check my other articles about web optimisation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/bogdanfromkyiv/ram-technique-in-css-1lk7"&gt;RAM Technique in CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@bogdanfromkyiv/lazy-load-for-videos-with-plain-js-ef0671dda64" rel="noopener noreferrer"&gt;Lazy Load for videos with plain JS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@bogdanfromkyiv/dynamic-font-size-using-only-css3-757ea901d0fe" rel="noopener noreferrer"&gt;Dynamic font-size using only CSS3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv" rel="noopener noreferrer"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace be with you!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>a11y</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>RAM Technique in CSS</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Sat, 15 Jul 2023 21:21:13 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/ram-technique-in-css-1lk7</link>
      <guid>https://dev.to/bogdanfromkyiv/ram-technique-in-css-1lk7</guid>
      <description>&lt;p&gt;This is a simple yet effective CSS technique to fit blocks on a webpage. In this context RAM stands for &lt;strong&gt;Repeat, Auto, Minmax&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The RAM is very useful when you have multiple blocks on a page with equal width and you need them to fit the whole width of the page (or page’s section).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eClpOQmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/0%2Aiyf6Q-gmUjZIQsFe" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eClpOQmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/0%2Aiyf6Q-gmUjZIQsFe" alt="" width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before browsers got full support of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout"&gt;grid&lt;/a&gt; layout developers used media queries to set the number of blocks per row for different screen resolutions.&lt;/p&gt;

&lt;p&gt;We had to provide different CSS rules for mobiles, tablets, laptops, desktops. Sometimes even for portrait/landscape mode! The more blocks per row we have the more breakpoints we need to cover to make the blocks look good on each screen size.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@media only screen and (max-width: 30em) {
  .block {
    width: 50%;
  }
}
@media only screen and (max-width: 45em) {
  .block {
    width: 25%;
  }
}
@media only screen and (max-width: 60em) {
  .block {
    width: 20%;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to the grid layout, we can handle it with just a few lines of CSS.&lt;/p&gt;

&lt;p&gt;Here’s how it works with RAM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;display: grid;
grid-template-columns: repeat(auto-fit, minmax(15em, 1fr));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is what you get using that CSS code:&lt;/p&gt;

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

&lt;p&gt;Try to change the width of the frame — the blocks will fit perfectly in each frame size.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s under the hood?
&lt;/h3&gt;

&lt;p&gt;First of all we use display: grid to let the browser know we want our blocks to be displayed in a grid.&lt;/p&gt;

&lt;p&gt;Secondly, we use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns"&gt;grid-template-columns&lt;/a&gt; property to define how exactly our grid’s elements should be displayed.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/repeat"&gt;repeat()&lt;/a&gt; function returns a pattern, which will be repeated for all columns in the grid.&lt;/p&gt;

&lt;p&gt;This function accepts two arguments —  &lt;strong&gt;repeat count&lt;/strong&gt; and  &lt;strong&gt;tracks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;repeat count&lt;/strong&gt; argument determines how many times the track list (grid elements) should be repeated. It can be either an integer number from 1 or a special keyword values &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fill"&gt;auto-fill&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/repeat#auto-fit"&gt;auto-fit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The auto-fit keyword means the grid elements will be stretched to fit all the available space in a section.&lt;/p&gt;

&lt;p&gt;If you use the auto-fill keyword the elements will NOT be stretched out, but keep the minimal width defined in the &lt;strong&gt;tracks&lt;/strong&gt; argument.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;tracks&lt;/strong&gt; argument specifies the set of tracks (elements) which will be repeated. We use a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/minmax"&gt;minmax()&lt;/a&gt; CSS function to provide a minimum width of a block (15em) and maximum width (1fr). That means each of our blocks will not be narrower than 15em and will be equally stretched.&lt;/p&gt;

&lt;p&gt;Read more about the fr unit here: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout#the_fr_unit"&gt;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Basic_concepts_of_grid_layout#the_fr_unit&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  With modern CSS properties you can handle your layouts with less code and in a more efficient way.
&lt;/h4&gt;

&lt;p&gt;Check my other CSS related articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@bogdanfromkyiv/dynamic-font-size-using-only-css3-757ea901d0fe"&gt;Dynamic font-size using only CSS3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/bogdanfromkyiv/an-easy-way-to-make-an-auto-responsive-menu-59mk-temp-slug-5214337"&gt;An Easy Way to Make an Auto Responsive Menu&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace be with you!&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>5 useful tips for Wordpress developers</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Fri, 30 Jun 2023 13:52:13 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/5-useful-tips-for-wordpress-developers-2dph</link>
      <guid>https://dev.to/bogdanfromkyiv/5-useful-tips-for-wordpress-developers-2dph</guid>
      <description>&lt;p&gt;If you are a WordPress developer, you may find these tips on how to develop a WordPress website helpful.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Use less plugins, write more code.
&lt;/h2&gt;

&lt;p&gt;There are plenty of plugins which provide very small functionality. They can easily be replaced with custom code in your theme’s &lt;code&gt;functions.php&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;Each plugin affects your website’s loading time a bit. Don’t get me wrong, I’m not saying that plugins are bad and each plugin adds seconds to the site’s loading time (although it depends on the plugin 🙂). &lt;/p&gt;

&lt;p&gt;But the bigger and more complicated your site becomes, the more plugins you might install. Just review your plugins list and if you find some with only a couple lines of code — move that code to your theme’s &lt;code&gt;functions.php&lt;/code&gt; file and delete that plugin.&lt;/p&gt;

&lt;p&gt;I recently noticed it myself when I reviewed the plugins list of my client’s website. I found a plugin which only adds a tiny CSS code into the admin panel to stretch the Gutenberg editor to the full width. So I simply got that function from the plugin’s file and pasted it into functions.php file.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Escape everything!
&lt;/h2&gt;

&lt;p&gt;WordPress haters like to say that one of the biggest problems of our beloved CMS is security. This is because beginner developers don’t write code with proper standards and don’t filter data which users (or a potential hackers) inputs. &lt;/p&gt;

&lt;p&gt;For example, they can enter some SQL injection into your form input and once it is sent to the database it can break your site. &lt;/p&gt;

&lt;p&gt;To prevent this and many other ways to hack your website, just use escape functions to **all data **users provide.&lt;/p&gt;

&lt;p&gt;Here are some most common escaping functions you should use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;esc_html($your_text)&lt;/code&gt; — use on text you want to display inside an HTML tag. This function will remove HTML from &lt;code&gt;$your_text&lt;/code&gt; variable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// don't forget to use the echo function
    &amp;lt;div&amp;gt;&amp;lt;?php echo esc_html( $your_text ); ?&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;esc_url($your_link)&lt;/code&gt; — use on all URLs, including those in the src and href attributes of an HTML element. This function checks and cleans a URL (&lt;code&gt;$your_link&lt;/code&gt; variable).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;a href="&amp;lt;?php echo esc_url( $your_link ); ?&amp;gt;"&amp;gt;
      &amp;lt;?php echo esc_html( $your_text ); ?&amp;gt;
    &amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;esc_attr($your_data)&lt;/code&gt; — use on everything else that’s printed into an HTML element’s attribute. This will encode the &amp;lt;, &amp;gt;, &amp;amp;, ” and ‘ characters.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;a class="&amp;lt;?php echo esc_attr( $your_data ); ?&amp;gt;" href="&amp;lt;?php echo esc_url( $your_link ); ?&amp;gt;"&amp;gt;
      &amp;lt;?php echo esc_html( $your_text ); ?&amp;gt;
    &amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find all the escaping functions at the official WordPress developer’s resource: &lt;a href="https://developer.wordpress.org/apis/security/escaping/"&gt;https://developer.wordpress.org/apis/security/escaping/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use child theme.
&lt;/h2&gt;

&lt;p&gt;If you don’t develop a website from scratch (means working on an existing site or using a theme from the other author) — use a child theme. &lt;/p&gt;

&lt;p&gt;It’s important because once the parent theme is updated you will lose all your changes. &lt;/p&gt;

&lt;p&gt;And even if the parent theme is not from a &lt;a href="https://wordpress.org/themes/"&gt;WordPress theme repository&lt;/a&gt; (or other themes storage like ThemeForest) it is a good practice to write your code in a child theme. &lt;/p&gt;

&lt;p&gt;Thus you can prevent breaking existing theme and revert your changes easily.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Prepare content for translation.
&lt;/h2&gt;

&lt;p&gt;Do not write texts in your php-files as regular text. Always use WordPress internationalization (translation) functions like &lt;code&gt;_e()&lt;/code&gt;, &lt;code&gt;_ex()&lt;/code&gt;, &lt;code&gt;_nx()&lt;/code&gt; etc.&lt;/p&gt;

&lt;p&gt;I mean don’t 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;div&amp;gt;This is some sample text you may find in some php-file. Unfortunately, it cannot be translated.&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead write text inside HTML 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;    &amp;lt;div&amp;gt;&amp;lt;?php _e('This is some sample text you may find in some php-file. Luckily, it can be translated because of the internationalization function.', 'theme-slug'); ?&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you write inside HTML using internationalization functions you prepare your website for translation. &lt;/p&gt;

&lt;p&gt;So when you install some translation plugin it will automatically detect all your strings in HTML and make them available for translation.&lt;/p&gt;

&lt;p&gt;You can read more about internationalization functions here: &lt;a href="https://developer.wordpress.org/apis/internationalization/internationalization-functions/"&gt;https://developer.wordpress.org/apis/internationalization/internationalization-functions/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Use actions and filters.
&lt;/h2&gt;

&lt;p&gt;WordPress has two awesome features — actions and filters. Use them! They are very convenient when you need to modify existing code without actual digging into that code source. &lt;/p&gt;

&lt;p&gt;A good example of using actions and filters is the WooCommerce plugin. You can modify almost all the data from your theme’s code without overriding the core code of the plugin. &lt;/p&gt;

&lt;p&gt;So when you develop a custom theme (or especially plugin) — try to make it possible for other developers to have access to modify or adjust your code.&lt;/p&gt;

&lt;p&gt;Let me demonstrate to you a quick example of how actions work. For example, you need to rearrange elements on a WooCommerce product page and you want to move the product’s price below the product’s short description.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ou9V23rI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2104/1%2AtgsxPr2L8eqRsEvyx5fvhg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ou9V23rI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2104/1%2AtgsxPr2L8eqRsEvyx5fvhg.png" alt="" width="800" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look at the WooCommerce core template file &lt;code&gt;templates/content-single-product.php&lt;/code&gt; you will notice that almost all the code is written using actions. &lt;/p&gt;

&lt;p&gt;So to move that price below description have a look at this part of the code (see the full code here &lt;a href="https://github.com/woocommerce/woocommerce/blob/7.5.0/plugins/woocommerce/templates/content-single-product.php"&gt;https://github.com/woocommerce/woocommerce/blob/7.5.0/plugins/woocommerce/templates/content-single-product.php&lt;/a&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;?php
    /**
    * Hook: woocommerce_single_product_summary.
    *
    * @hooked woocommerce_template_single_title - 5
    * @hooked woocommerce_template_single_rating - 10
    * @hooked woocommerce_template_single_price - 10
    * @hooked woocommerce_template_single_excerpt - 20
    * @hooked woocommerce_template_single_add_to_cart - 30
    * @hooked woocommerce_template_single_meta - 40
    * @hooked woocommerce_template_single_sharing - 50
    * @hooked WC_Structured_Data::generate_product_data() - 60
    */
    do_action( 'woocommerce_single_product_summary' );
    ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we need to remove the &lt;code&gt;'woocommerce_template_single_price'&lt;/code&gt; action from the hook &lt;code&gt;'woocommerce_single_product_summary'&lt;/code&gt; (mind the last priority argument, it &lt;strong&gt;MUST&lt;/strong&gt; be the same). &lt;/p&gt;

&lt;p&gt;Then attach that action (function) again, only with higher priority, so it will be after the &lt;code&gt;'woocommerce_template_single_excerpt'&lt;/code&gt; action. &lt;/p&gt;

&lt;p&gt;Here’s the code to place into &lt;code&gt;functions.php&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;    function prefix_move_price_after_excerpt(){
      remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price', 10 );
      add_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_price', 25 );
    }
    add_action( 'init', 'prefix_move_price_after_excerpt', 10);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the product page looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B7wjq90k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2032/0%2ANBjXGCxtuYBtZPbA" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B7wjq90k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/2032/0%2ANBjXGCxtuYBtZPbA" alt="" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn more about actions and filters at the official WordPress developer resources:&lt;br&gt;
&lt;a href="https://developer.wordpress.org/plugins/hooks/actions/"&gt;https://developer.wordpress.org/plugins/hooks/actions/&lt;br&gt;
&lt;/a&gt;&lt;a href="https://developer.wordpress.org/plugins/hooks/filters/"&gt;https://developer.wordpress.org/plugins/hooks/filters/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  There will be more…
&lt;/h2&gt;

&lt;p&gt;Those were some basic tips for you as a WordPress developer, hope you find them useful and helpful.&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace be with you!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>wordpress</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>5 Should-Use WordPress Plugins as a Developer</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Tue, 27 Jun 2023 12:15:07 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/5-should-use-wordpress-plugins-as-a-developer-58mf</link>
      <guid>https://dev.to/bogdanfromkyiv/5-should-use-wordpress-plugins-as-a-developer-58mf</guid>
      <description>&lt;p&gt;Just wanna show you some of my most-using plugins in WordPress as a developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://wordpress.org/plugins/advanced-custom-fields/"&gt;Advanced Custom Fields&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The most essential plugin for a WordPress developer, IMHO. It adds to ANY post type or ANY taxonomy custom data fields, where you can provide almost any data you want. Those data fields can be text fields, textareas, datetime fields, colour pickers, radio buttons, checkboxes, images, videos etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--52yLMQt6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ATGMNY-FkJhWQUhdcNp73Zg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--52yLMQt6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2ATGMNY-FkJhWQUhdcNp73Zg.jpeg" alt="" width="800" height="548"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A demo of ACF plugin. Image from &lt;a href="https://www.advancedcustomfields.com/"&gt;https://www.advancedcustomfields.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So you or your editors can set tons of custom settings and then you as a developer can easily use them in your theme’s code.&lt;/p&gt;

&lt;p&gt;The PRO version of ACF plugin also provides some awesome possibilities. For example, repeater fields, so you can create a set of fields and then duplicate it as many times as you need. Also, the PRO version allows you to build custom Gutenberg blocks! I wrote an article about that, so check it out, you’ll love this feature:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/bogdanfromkyiv/how-and-why-to-build-custom-gutenberg-blocks-in-wordpress-23lm-temp-slug-9478592"&gt;How and Why to Build Custom Gutenberg Blocks in WordPress&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://facetwp.com/"&gt;FacetWP&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A super-powerful plugin for making complex posts’ filters in an easy way. And it’s not only for products in WooCommerce, you can filter any post types by any meta fields they have. This means it is fully compatible with ACF, which makes it even cooler.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Upod042N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/893/1%2AsqMErPRgbPFQgJts5bEbKg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Upod042N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/893/1%2AsqMErPRgbPFQgJts5bEbKg.png" alt="" width="800" height="670"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A demo of the FacetWP plugin&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It comes with awesome features like pager, for instance. It allows you to either create a pagination with the filtering results, or show them in an infinite scroll or with a load-more button. With extra addons, you can show results on a map and sort them in geographical order from the user’s location.&lt;/p&gt;

&lt;p&gt;Besides, all the filtering is happening with AJAX. And using plugin’s filters and hooks you can modify filter results in any way you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://wordpress.org/plugins/contact-form-7/"&gt;Contact Form 7&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I’m sure you already know about this plugin, but allow me to explain why I like it so much.&lt;/p&gt;

&lt;p&gt;I know, it’s not very user-friendly on the back-end. This is because the form building process is based on shortcodes and users cannot see the final result immediately.&lt;/p&gt;

&lt;p&gt;But it’s very powerful for developers! They can write their own HTML code for the forms, manipulate form data on submission (both on a server side and browser side), use dynamic data for the form fields (like current user’s name or variables from the $_GET array) etc. And let’s be honest, building contact forms is not a daily routine for your site’s administrators 😅. So the shortcodes-based back-end can be forgivable for all the features this plugin provides. And it’s 100% free!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.wpallimport.com/"&gt;WP All Import/Export&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I had this task from one of my clients recently to populate WooCommerce products from the Excel table. And not just simple products with name, description, image and price, but with tons of custom fields (hello ACF again).&lt;/p&gt;

&lt;p&gt;This is where WP All Import saved me! It allows you to import Excel, XML or CSV files, then create a template for your import and import all your posts at once. And yes, it works with any post types and taxonomies in WordPress.&lt;/p&gt;

&lt;p&gt;And the process of creating an import template is very easy — you can simply drag a field from your import file and then drop it into the place it has to be in your post.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V2xFOKHE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A4A0oxtjQa1L5njeJhZol5Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V2xFOKHE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A4A0oxtjQa1L5njeJhZol5Q.jpeg" alt="" width="800" height="625"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Very easy to create an import template. Image from &lt;a href="https://www.wpallimport.com/"&gt;https://www.wpallimport.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It’s fully compatible with WooCommerce and ACF (even with ACF repeater fields), although you need to purchase extra addons for them. BTW, they are planning to &lt;a href="https://www.wpallimport.com/your-last-chance-to-buy-unlimited-lifetime-licenses/"&gt;change away from lifetime licences&lt;/a&gt;, but you still have time to purchase it once and for all.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://uk.wordpress.org/plugins/duplicator/"&gt;Duplicator&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If you need to migrate your site from staging to production server, or from localhost to live — this is the right plugin! What I like the most of it — it migrates the site exactly how it was. That means you won’t need to fix widgets, menus etc.&lt;/p&gt;

&lt;p&gt;The PRO version allows you to migrate multisites and work with big site sizes (when you have lots of media for example). But you can simply exclude the uploads folder from the archive (built-in feature of the free version). Just copy your media files manually after migrating the site (a small hack 😉).&lt;/p&gt;

&lt;p&gt;What’re your favourite WordPress plugins?&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace to you!&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Roadmap for WordPress Developer</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Fri, 23 Jun 2023 15:43:31 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/roadmap-for-wordpress-developer-4fg8</link>
      <guid>https://dev.to/bogdanfromkyiv/roadmap-for-wordpress-developer-4fg8</guid>
      <description>&lt;p&gt;I started my way in development websites on WordPress around 10 years ago. To be honest, I just started to fix some bugs and maintain sites. I had no idea on which CMS they were built, I just dug into the code and tried to do what I had to 😅. But what I did know were basics of web development, like HTML, CSS, JS for front-end and PHP with MySQL for the back-end. So here’s in more detail what you should learn to develop professional websites based on CMS WordPress, IMHO.&lt;/p&gt;

&lt;h3&gt;
  
  
  DECIDE WHAT YOU WANT TO DO
&lt;/h3&gt;

&lt;p&gt;First of all, you should understand for yourself what exactly you’re planning to do with WordPress. You can be an &lt;a href="https://elementor.com/features/wordpress/" rel="noopener noreferrer"&gt;Elementor templates developer&lt;/a&gt;, for example. In this case, you should know only &lt;a href="https://wordpress.org/documentation/category/where-to-start/" rel="noopener noreferrer"&gt;WordPress basics&lt;/a&gt; like installing WordPress, adding/editing posts or pages, working with menus and widgets, and of course working with the Elementor builder itself. But if you want to develop custom plugins or themes on WordPress — then you need to learn some technical stuff too.&lt;/p&gt;

&lt;h3&gt;
  
  
  THE BASICS
&lt;/h3&gt;

&lt;p&gt;On the web, it all starts with &lt;strong&gt;HTML&lt;/strong&gt; (Hypertext Markup Language) and &lt;strong&gt;CSS&lt;/strong&gt; (Cascading Style Sheets*&lt;em&gt;&lt;em&gt;)&lt;/em&gt;&lt;/em&gt;*. Basically, even if you’re planning to work with WordPress using builders like Elementor or WPBakery — you still need to know at least basics of HTML and CSS. There are tons of different courses, videos on YouTube and articles on the Net about learning HTML and CSS, so it’s up to you which one to choose. As for me, I prefer MDN Web Docs: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/HTML&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you learn how to place elements on the page and style them, you can REALLY start to develop on WordPress.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;First you need to understand that you cannot be ONLY a front-end or ONLY a back-end WordPress developer. You have to be both — means you’re gonna be a full stack developer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course you can work with WordPress knowing only a front-end or a back-end (e.g React developer builds a front-end for some custom WordPress plugin). But to be a WordPress developer itself and be able to maintain all the path from markup to the final product (theme, plugin) requires being a full stack developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  THE FRONT-END
&lt;/h3&gt;

&lt;p&gt;Despite WordPress including jQuery (which is a library for JavaScript) &lt;strong&gt;I highly recommend learning JavaScript from scratch&lt;/strong&gt;. Because the more complex your WordPress sites become the more knowledge you need to maintain and improve them (both in front-end and back-end). And jQuery only helps you to work faster and more convenient with DOM and AJAX basically, but doesn’t replace all JavaScript’s syntax (like arrays, objects, functions etc). Besides, it becomes handy when you work with other developers’ code, which they can write in so-called vanilla JS (clean JavaScript without any extra libraries).&lt;/p&gt;

&lt;p&gt;Like with HTML and CSS, there are plenty of docs about JavaScript, so you can learn it in any way you want (Google helps you). I can suggest &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt; or &lt;a href="https://www.codecademy.com/learn/introduction-to-javascript" rel="noopener noreferrer"&gt;the Codecademy course for beginners&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you get comfortable with JavaScript itself, you can get into &lt;strong&gt;jQuery&lt;/strong&gt;. It’s a JS library that makes a lot easier to work with elements, handling events and AJAX requests etc.&lt;/p&gt;

&lt;p&gt;For example, here’s how to make an element to fade out and then remove it in plain JS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let s = document.getElementById('thing').style;
s.opacity = 1;
(function fade(){
  (s.opacity-=.1) &amp;lt; 0 ? s.display="none" : setTimeout(fade, 40)
})();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here’s how to make the same thing with jQuery:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$('#thing').fadeOut();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.jquery.com/" rel="noopener noreferrer"&gt;The official documentation&lt;/a&gt; is quite explicit, so you can start to learn jQuery there.&lt;/p&gt;

&lt;p&gt;I mentioned a couple of times something called &lt;strong&gt;AJAX&lt;/strong&gt;. It stands for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX" rel="noopener noreferrer"&gt;Asynchronous JavaScript and XML&lt;/a&gt;. It’s a method that allows websites to make quick, incremental updates to the user interface without reloading the entire browser page. For example, when a user clicks on a submit button to post some form with a quiz, the result of the quiz appears on the page immediately so the user doesn’t need to reload the page to see it. This makes the website faster and more responsive to user actions.&lt;/p&gt;

&lt;p&gt;In addition, when you become an advanced WordPress developer you can start learning &lt;a href="https://react.dev/learn" rel="noopener noreferrer"&gt;&lt;strong&gt;React&lt;/strong&gt;&lt;/a&gt;. This is another JS library to build complex web applications. It can be useful when developing builders (like Gutenberg or Divi) or their components for WordPress. Also, with &lt;a href="https://developer.wordpress.org/rest-api/" rel="noopener noreferrer"&gt;WordPress REST API&lt;/a&gt; you can build complex web apps, where React will be responsible for the front-end and WordPress itself for a back-end. Until you get into React, you can build custom Gutenberg blocks in much easier way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/bogdanfromkyiv/how-and-why-to-build-custom-gutenberg-blocks-in-wordpress-2ne9"&gt;How and Why to Build Custom Gutenberg Blocks in WordPress&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  THE BACK-END
&lt;/h3&gt;

&lt;p&gt;WordPress works on &lt;strong&gt;PHP&lt;/strong&gt;. It’s an open source general-purpose scripting language that is especially suited for web development and can be embedded into HTML. WordPress core expands it with its own functions, but the start is PHP. Learn basic syntax, read about functions and classes, error handling and namespaces.&lt;/p&gt;

&lt;p&gt;The explicit documentation is available on &lt;a href="https://www.php.net/manual/en/" rel="noopener noreferrer"&gt;the official website&lt;/a&gt;, but as usual — learn in the most convenient way for yourself. Watch YouTube videos, read different manuals, pass online courses, go to some code classes — it’s your life 🙂&lt;/p&gt;

&lt;p&gt;After PHP itself, start to dive into WordPress core functions, coding standards etc. It all can be found at the &lt;a href="https://developer.wordpress.org/" rel="noopener noreferrer"&gt;WordPress Developer Resources&lt;/a&gt;. The most basics are &lt;a href="https://developer.wordpress.org/plugins/hooks/actions/" rel="noopener noreferrer"&gt;actions&lt;/a&gt; and &lt;a href="https://developer.wordpress.org/plugins/hooks/filters/" rel="noopener noreferrer"&gt;filters&lt;/a&gt;. I’ve also wrote about them in this post about useful tips for WordPress developer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@bogdanfromkyiv/5-useful-tips-for-wordpress-developers-47caec7914ef" rel="noopener noreferrer"&gt;5 useful tips for Wordpress developers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can also recommend studying at least some basics of MySQL. This is a free, open-source and widely used relational database management system (RDBMS). In simple words, this is a database (DB) where all your WordPress data is located. And to manipulate that data (adding/updating/removing) you need to use SQL (Structured query language). The &lt;a href="https://dev.mysql.com/doc/" rel="noopener noreferrer"&gt;official documentation of MySQL&lt;/a&gt; is quite messy and may be hard for beginners, so I recommend studying MySQL on &lt;a href="https://www.w3schools.com/MySQL/default.asp" rel="noopener noreferrer"&gt;W3Scools.com&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  THE CONCLUSION
&lt;/h3&gt;

&lt;p&gt;To sum up all that stuff you just read, I prepared this illustration about roadmap for WordPress developer:&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AWVxXKx9ZwnfyVt_riwcwQA.jpeg" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AWVxXKx9ZwnfyVt_riwcwQA.jpeg" alt="Roadmap for WordPress developer"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Roadmap for WordPress developer&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here’re some links where you can study about all those languages and technologies to become a master WordPress developer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HTML + CSS:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.w3schools.com/html/default.asp" rel="noopener noreferrer"&gt;W3Schools tutorial for HTML&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.w3schools.com/css/default.asp" rel="noopener noreferrer"&gt;W3Schools tutorial for CSS&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML" rel="noopener noreferrer"&gt;MDN Web Docs for HTML&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS" rel="noopener noreferrer"&gt;MDN Web Docs for CSS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript + jQuery + React:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.codecademy.com/learn/introduction-to-javascript" rel="noopener noreferrer"&gt;Codecademy course&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.w3schools.com/js/default.asp" rel="noopener noreferrer"&gt;W3Schools tutorial&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.jquery.com/" rel="noopener noreferrer"&gt;jQuery official docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX" rel="noopener noreferrer"&gt;MDN Web Docs for AJAX&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://react.dev/learn" rel="noopener noreferrer"&gt;Official React docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3schools.com/react/default.asp" rel="noopener noreferrer"&gt;W3Schools tutorial for React&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP + WordPress core:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.php.net/manual/en/" rel="noopener noreferrer"&gt;PHP official manulas&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.w3schools.com/php/default.asp" rel="noopener noreferrer"&gt;W3Schools tutorial for PHP&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.wordpress.org/" rel="noopener noreferrer"&gt;WordPress developer resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3schools.com/MySQL/default.asp" rel="noopener noreferrer"&gt;W3Schools tutorial&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;strong&gt;What was your way in WordPress development? Don’t hesitate to write your stories in the comments below.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv" rel="noopener noreferrer"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace be with you!&lt;/p&gt;

</description>
      <category>development</category>
      <category>wordpress</category>
      <category>roadmap</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How and Why to Build Custom Gutenberg Blocks in WordPress</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Mon, 19 Jun 2023 13:33:03 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/how-and-why-to-build-custom-gutenberg-blocks-in-wordpress-2ne9</link>
      <guid>https://dev.to/bogdanfromkyiv/how-and-why-to-build-custom-gutenberg-blocks-in-wordpress-2ne9</guid>
      <description>&lt;p&gt;So you’re a WordPress developer and you get this awesome website design in Figma (or Zeplin or whatever works for you) and you need to make no less awesome WordPress website. How to achieve this?&lt;/p&gt;

&lt;p&gt;If you have a pretty simple design with a simple grid — you can use Elementor. It’s easy to edit for users, has plenty of options so it won’t be hard for you to reproduce simple designs there.&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AC4TR-d6Fx3V0TEYl" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AC4TR-d6Fx3V0TEYl" alt="A pretty simple block"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A pretty simple block&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But what if you have a complex design with uncommon elements like custom sliders, calculators, scrollytelling blocks? “I can do all this stuff with Elementor too”, — some say. And they will be kinda right. But can you imagine how tricky it will be with Elementor? To override all its styles, scripts and layouts? I’ve tried that, and that was a nightmare! You will get a total mess with CSS and HTML, so it will be very hard to maintain such a site. This is not what Elementor is for! Besides, you need to get through all the developer docs about how to make a custom widget in Elementor and dig into the code each time you want to add/remove some field or change its type or whatever else.&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AK4O1cZdnP0pQIZIH" 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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AK4O1cZdnP0pQIZIH"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Kinda hard to build such a block in Elementor&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let me show how it can be achieved with native WordPress Gutenberg editor and Advanced Custom Fields Pro plugin!&lt;/p&gt;

&lt;p&gt;Wait, what? The PRO version? Should I buy it?&lt;/p&gt;

&lt;p&gt;It’s worth it! It provides such amazing features, I cannot imagine myself developing a WordPress site without that plugin now! Besides, you can &lt;a href="https://github.com/wp-premium/advanced-custom-fields-pro" rel="noopener noreferrer"&gt;download the PRO version from GitHub&lt;/a&gt; (uploaded by plugin’s owner Elliot Condon, so it’s legal 😎). Of course, it’s not the latest version, but it’s a good demo so you can take a closer look at this plugin.&lt;/p&gt;

&lt;p&gt;So what’s this plugin for? As you may guess from its name — it adds custom fields of any type to any post type. That means you can add a text input, number input, image, video, embed object, color picker, datetime picker and many more. Basically, all that HTML can provide (correct me if I’m wrong in the comments 😁). Plus, you can create repeater fields and provide conditional logic to each field. Really, it’s a very powerful and useful plugin for WordPress developers.&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2Abg6BiklXzJYRF4uT" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2Abg6BiklXzJYRF4uT"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This is only part of the field types ACF provides&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AKm3lcfjyUToWM27Q" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2AKm3lcfjyUToWM27Q"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;And here’re some more field types&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But let’s get back to the custom blocks. You can add those too! And manipulate custom fields from the WordPress admin panel. Your users will get a user-friendly back-end for the custom blocks, so they can easily set the data they need. And what’s more important, they won’t mess things up, because they will be allowed to change only the data you provide them to change. So it will be very hard to break anything on the page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Register a new block
&lt;/h3&gt;

&lt;p&gt;Let’s get to business! I’m gonna show you a bit of an old-school way of adding new custom blocks. But don’t worry, it has only slight changes compared to the modern way. Basically, the main difference is where to define parameters of a new block. So first, create a new subfolder in your theme’s folder to place your block’s files there. Something like this:&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%2Fcdn-images-1.medium.com%2Fmax%2F393%2F0%2ApzYPvTlBgey4ugOf" 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%2Fcdn-images-1.medium.com%2Fmax%2F393%2F0%2ApzYPvTlBgey4ugOf"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A folder tree of the new block&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Then we need to register a new block and provide settings for it. I do that in &lt;code&gt;functions.php&lt;/code&gt; file using &lt;a href="https://www.advancedcustomfields.com/resources/acf_register_block_type/" rel="noopener noreferrer"&gt;&lt;code&gt;acf_register_block_type&lt;/code&gt;&lt;/a&gt; function 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;if( function_exists('acf_register_block_type') ) {
  add_action('acf/init', 'custom_prefix_register_acf_blocks');
  function custom_prefix_register_acf_blocks() {
    acf_register_block_type(array(
      'name' =&amp;gt; 'my-block', // a system name of the block
      'title' =&amp;gt; __('My Block'), 
      'description' =&amp;gt; __('Just another awesome block.'),
      'render_template' =&amp;gt; 'blocks/my-block/block.php',
      'category' =&amp;gt; 'common', // The core provided categories are [common | formatting | layout | widgets | embed].
      'icon' =&amp;gt; 'book-alt', // These can be any of WordPress’ Dashicons, or a custom svg element.
      'keywords' =&amp;gt; array( 'block', 'custom' ),
      'supports' =&amp;gt; array( 'anchor' =&amp;gt; true )
    ));
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t forget to change &lt;code&gt;custom_prefix_&lt;/code&gt; to your theme’s unique prefix. I wrote a bit more about why you should use prefixes here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@bogdanfromkyiv/do-not-do-this-as-a-wordpress-developer-da9230cb690a" rel="noopener noreferrer"&gt;DO NOT do this as a WordPress developer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since ACF version 6.0 and WordPress 5.8+ it is recommended to register new blocks with a new &lt;a href="https://www.advancedcustomfields.com/resources/acf-blocks-with-block-json/" rel="noopener noreferrer"&gt;block.json syntax&lt;/a&gt;, which requires you to create a file named &lt;code&gt;block.json&lt;/code&gt; (exactly that name) in the folder where your new block will be located (&lt;code&gt;blocks/my-block&lt;/code&gt; in our example). Then add your block’s parameters to that 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;{
  "name": "acf/my-block",
  "title": "My Block",
  "description": "Just another awesome block.",
  "style": ["file:./block.css"],
  "category": "common",
  "icon": "book-alt",
  "keywords": ["block", "custom"],
  "acf": {
    "mode": "preview",
    "renderTemplate": "block.php"
  },
  "align": "full"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note, that we need to provide a namespace called acf to the block’s name parameter (&lt;code&gt;“name”: “ **acf/** my-block”&lt;/code&gt;). Then you need to register your block in your theme’s functions.php file using a &lt;a href="https://developer.wordpress.org/reference/functions/register_block_type/" rel="noopener noreferrer"&gt;&lt;code&gt;register_block_type&lt;/code&gt;&lt;/a&gt; function (not &lt;code&gt;acf_register_block_type&lt;/code&gt;) 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;if( function_exists('acf_register_block_type') ) {
  add_action( 'init', 'custom_prefix_register_acf_blocks' );
  function custom_prefix_register_acf_blocks() {
    register_block_type( __DIR__. '/blocks/my-block' );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if you’re using a GitHub version of ACF Pro plugin (which is 5.9.1 version) you should use the legacy way of registering custom blocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add a new block
&lt;/h3&gt;

&lt;p&gt;To add a new custom Gutenberg block follow this steps:&lt;/p&gt;

&lt;p&gt;1.In your &lt;code&gt;wp-admin&lt;/code&gt; go to Custom Fields -&amp;gt; Add new:&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%2Fcdn-images-1.medium.com%2Fmax%2F405%2F0%2A3gncyxpfw5cjI_o9" 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%2Fcdn-images-1.medium.com%2Fmax%2F405%2F0%2A3gncyxpfw5cjI_o9"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Provide a new field group title, select the group’s location to Block and choose your block’s name from the list (&lt;code&gt;My Block&lt;/code&gt; in our example).&lt;/li&gt;
&lt;/ol&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2ApbRWak2akSwO10e-" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2ApbRWak2akSwO10e-"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add fields to your block (+ Add Field button). For example, if you want your user to have the ability to change some text in your block — add a text field.&lt;/li&gt;
&lt;/ol&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2Ao-HbPYC_sYsxRLKr" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2Ao-HbPYC_sYsxRLKr"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add as many fields as you need. If you new with ACF plugin — take a look of detailed documentation about each of field types here: &lt;a href="https://www.advancedcustomfields.com/resources/#field-types" rel="noopener noreferrer"&gt;https://www.advancedcustomfields.com/resources/#field-types&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2An-dimwEErK5vBf70" 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%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F0%2An-dimwEErK5vBf70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;As soon as you’re done — click on the Publish button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a template file for your block. Add a new file in your block’s folder (&lt;code&gt;blocks/my-block&lt;/code&gt;) that matches the &lt;code&gt;render_template&lt;/code&gt; setting used when registering the block (&lt;code&gt;block.php&lt;/code&gt; in this example).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a PHP and HTML code of your block in that file. For example:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
/**
* Custom Block Template.
*
* @param array $block The block settings and attributes.
* @param string $content The block inner HTML (empty).
* @param bool $is_preview True during backend preview render.
* @param int $post_id The post ID the block is rendering content against.
* This is either the post ID currently being displayed inside a query loop,
* or the post ID of the post hosting this block.
* @param array $context The context provided to the block by the post or it's parent block.
*/

// Support custom "anchor" values.
$anchor = '';
if ( ! empty( $block['anchor'] ) ) {
  $anchor = 'id="' . esc_attr( $block['anchor'] ) . '" ';
}

// Create a class attribute allowing for custom "className" and "align" values.
$class_name = 'custom-block';
if ( ! empty( $block['className'] ) ) {
  $class_name .= ' ' . $block['className'];
}
if ( ! empty( $block['align'] ) ) {
  $class_name .= ' align' . $block['align'];
}

// Load values and assign defaults.
$text_to_change = get_field( 'text_to_change' ) ?: __('Default text', 'theme-slug');
$image_to_change = get_field( 'image_to_change' ) ?: 112; // returns image ID
?&amp;gt;

&amp;lt;div &amp;lt;?php echo $anchor; ?&amp;gt;class="&amp;lt;?php echo esc_attr( $class_name ); ?&amp;gt;"&amp;gt;
  &amp;lt;div class="custom-block"&amp;gt;
    &amp;lt;h3 class="custom-block__text"&amp;gt;
      &amp;lt;?php echo esc_html( $text_to_change ); ?&amp;gt;
    &amp;lt;/h3&amp;gt;
    &amp;lt;div class="custom-block__image"&amp;gt;
      &amp;lt;?php echo wp_get_attachment_image( $image_to_change['ID'], 'full' ); ?&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only function here that comes from the ACF plugin is &lt;a href="https://www.advancedcustomfields.com/resources/get_field/" rel="noopener noreferrer"&gt;&lt;code&gt;get_field&lt;/code&gt;&lt;/a&gt;. It returns a value of the field you added in the field group called My Block (step 2).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Don’t forget to add CSS styles to your block. You can do that either in your theme’s stylesheet file or in the file &lt;code&gt;block.css&lt;/code&gt; if you use a new &lt;code&gt;block.json&lt;/code&gt; syntax (that file goes to the folder with your &lt;code&gt;block.php&lt;/code&gt; file). To apply custom block styles to the Gutenberg editor use this code:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function custom_prefix_theme_setup() {
  // Add support for editor styles.
  add_theme_support( 'editor-styles' );
  // Enqueue editor styles.
  add_editor_style( 'blocks/my-block/block.css' ); // relative path from your theme's directory
}
add_action( 'admin_init', 'custom_prefix_theme_setup' );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;All we have to do now is to add our new custom block in the WordPress page or post:&lt;/li&gt;
&lt;/ol&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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AMYvY1qxoMt-4bmyw" 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%2Fcdn-images-1.medium.com%2Fmax%2F800%2F0%2AMYvY1qxoMt-4bmyw"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An example of a custom Gutenberg block made with ACF Pro plugin&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I’ve made a very simple block just to demonstrate how easy it can be done, you can learn more about custom blocks from the ACF docs: &lt;a href="https://www.advancedcustomfields.com/resources/blocks/" rel="noopener noreferrer"&gt;https://www.advancedcustomfields.com/resources/blocks/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do I need to use custom Gutenberg blocks again?
&lt;/h3&gt;

&lt;p&gt;As you may have noticed, the HTML markup I use in the example is completely arbitrary. No need to deal with extra elements like in Elementor or other WordPress builders. As well as CSS — your block only inherits basic styles from your site (like from &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; tags). And it’s super easy for users to change any data you provide them to change. So you can create a very complex custom blocks with lots of settings and your users can easily maintain them from the admin without disturbing you each time they want “to change that button text” 😄.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you think about this bundle Gutenberg + ACF? Don’t hesitate to write your thoughts in the comments below.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv" rel="noopener noreferrer"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace be with you!&lt;/p&gt;

</description>
      <category>gutenberg</category>
      <category>wordpress</category>
      <category>advancedcustomfields</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dynamic font-size using only CSS3</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Sun, 18 Jun 2023 09:16:58 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/dynamic-font-size-using-only-css3-31kj</link>
      <guid>https://dev.to/bogdanfromkyiv/dynamic-font-size-using-only-css3-31kj</guid>
      <description>&lt;p&gt;It’s 2023 already, time flies! So using CSS media queries for different screen resolutions just to increase/decrease your text sizes for a couple pixels or rems is kinda annoying. Why don’t you use dynamic font-size instead?&lt;/p&gt;

&lt;h2&gt;
  
  
  What is dynamic font-size?
&lt;/h2&gt;

&lt;p&gt;Dynamic font-size means your text sizes will be changed proportionally to the user’s viewport width. You just need to provide the minimum value of font-size, the maximum value of it, and two breakpoints, between which font will scale.&lt;/p&gt;

&lt;p&gt;For example: let’s say you want your text to have a font size of 20px at viewport width of 768pxand maximum size of 36px when viewport width is 1920px. But if the viewport width is less than 768px, you don’t want your font-size to get lower than 16px and if the viewport width is more than 1920px you want your font size to stop scaling at 48px.&lt;/p&gt;

&lt;p&gt;Lots of words, better check the pictures below.&lt;/p&gt;

&lt;p&gt;When viewport’s width is 768pxthe font-size is equal to 20px:&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%2Fdsd2j282o18dq4m0x58r.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%2Fdsd2j282o18dq4m0x58r.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When viewport width is larger than 768pxthe font-size scales proportionally:&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%2F8gecde2uanjr9i0td3j7.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%2F8gecde2uanjr9i0td3j7.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At 1920pxit becomes 36px:&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%2F7smeongylxslqmbc2oyp.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%2F7smeongylxslqmbc2oyp.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the screens smaller than 768px, the font-size won’t be smaller than 16px:&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%2Fhnnwh3o4f0kwotdckulv.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%2Fhnnwh3o4f0kwotdckulv.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the screens larger than 1920px, the font-size won’t be bigger than 48px:&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%2Fo4ishwdjokynkxzzo082.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%2Fo4ishwdjokynkxzzo082.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty cool, huh 😁?&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s the trick?
&lt;/h2&gt;

&lt;p&gt;To achieve that we only need one line of CSS:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;font-size: clamp(16px, calc(20px + (36–20) * (100vw - 768px)/(1920–768)), 48px);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we use native CSS3 function &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clamp" rel="noopener noreferrer"&gt;clamp()&lt;/a&gt;, which accepts three arguments:&lt;br&gt;
the minimum value, the desirable value and the maximum value. For the minimum value we use 16px in the example above, the maximum value is 48px.&lt;/p&gt;

&lt;p&gt;The desirable value is the most tricky part — we use the CSS function &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/calc" rel="noopener noreferrer"&gt;calc()&lt;/a&gt; for it, which performs calculations between different CSS values.&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%2Fq0cwj0q29afcoyiz49ex.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%2Fq0cwj0q29afcoyiz49ex.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;20px (as well as 20) — is our minimum value for the font-size;&lt;/p&gt;

&lt;p&gt;36 — is the maximum value of the font-size;&lt;/p&gt;

&lt;p&gt;768px (768) — is the lower breakpoint;&lt;/p&gt;

&lt;p&gt;1920 — the higher breakpoint;&lt;/p&gt;

&lt;p&gt;100vw returns the full width of the user’s viewport (1vw = 1% of the viewport’s width).&lt;/p&gt;

&lt;p&gt;To make sure this formula works let’s test it for our breakpoint values (which are 768px and 1920px).&lt;/p&gt;

&lt;p&gt;So when the user’s viewport width becomes 768px, then 100vw = 768px. Just replace 100vw to 768px and you will get exactly 20px (the right part of the formula equals 0px).&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%2Fznto2faxdc64nbpv1hkj.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%2Fznto2faxdc64nbpv1hkj.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do the same with 1920px viewport width and you will get exactly 36px as the value for the font-size (the right part of the formula equals 16px).&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%2Fc1u8h1bo13nbunxd78tu.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%2Fc1u8h1bo13nbunxd78tu.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/bogdanfromkyiv/embed/KKBaMeB?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;br&gt;
As you can see, dynamic font-size can be possible with only CSS3, thanks to clamp() and calc() functions. If you want to dive deeper into different solutions for dynamic font-size, I highly recommend this article to read:&lt;br&gt;
&lt;a href="https://css-tricks.com/linearly-scale-font-size-with-css-clamp-based-on-the-viewport/" rel="noopener noreferrer"&gt;https://css-tricks.com/linearly-scale-font-size-with-css-clamp-based-on-the-viewport/&lt;/a&gt; .&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv" rel="noopener noreferrer"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading! &lt;br&gt;
Stay safe and peace be with you!&lt;/p&gt;

</description>
      <category>css</category>
      <category>font</category>
      <category>tutorial</category>
      <category>html</category>
    </item>
    <item>
      <title>Gutenberg vs Elementor</title>
      <dc:creator>Bogdan Bendziukov</dc:creator>
      <pubDate>Thu, 25 May 2023 13:59:19 +0000</pubDate>
      <link>https://dev.to/bogdanfromkyiv/gutenberg-vs-elementor-2k83</link>
      <guid>https://dev.to/bogdanfromkyiv/gutenberg-vs-elementor-2k83</guid>
      <description>&lt;p&gt;Elementor is the most popular builder on WordPress and one of the most downloadable plugins in the whole WordPress plugins repository. It has over 5 million active instals!&lt;/p&gt;

&lt;p&gt;I remember how I enjoyed working with it when it became popular. You can change anything you want and immediately see the effect — you’re working on your actual page, not with some weird blocks, hello Visual Composer! And yes, I know they improved it quite a lot, but still I have those blocks in front of my eyes and how I was presented it to my clients 🙄.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--13rFMHGX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A32AdhJLvucANhBuLkmmbqA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--13rFMHGX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A32AdhJLvucANhBuLkmmbqA.png" alt="" width="800" height="396"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Back-end on Visual Composer looks scary for non-prepared users&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GUiiz1be--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AaMMKfSMCB9PPfjZn9f2WNw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GUiiz1be--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AaMMKfSMCB9PPfjZn9f2WNw.png" alt="" width="800" height="466"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;So much easier to build websites with Elementor. Source: &lt;a href="https://elementor.com"&gt;https://elementor.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But Elementor is a whole different thing.. Or is it?&lt;/p&gt;

&lt;p&gt;Until Gutenberg was introduced in WordPress 5.0 (November 2018), users dealt with TinyMCE WISYWIG Editor — an open source rich text editor. Lot of users still keep using it, that’s why the &lt;a href="https://wordpress.org/plugins/classic-editor/"&gt;Classic Editor plugin&lt;/a&gt; is one of the most installed plugins in the WordPress plugin repo.&lt;/p&gt;

&lt;p&gt;It was very good for quite a long time, but nowadays people often need much more than inserting an image or making a word in a bold font. Users want to have an opportunity to create complex pages with different blocks, reorder them, and all that without programming knowledge. Fair enough, I’d say! When you’re using your smartphone you don’t need an iOS or Android developer next to you, are you? Even if you don’t know how to change some option, the first google link will help you.&lt;/p&gt;

&lt;p&gt;Ok, so what’s wrong with Elementor? Seems like it has all you need for the full site editing. Why should you use Gutenberg, which is obviously less powerful?&lt;/p&gt;
&lt;h3&gt;
  
  
  First of all — Gutenberg is a part of WordPress!
&lt;/h3&gt;

&lt;p&gt;And it keeps evolving. It was quite buggy on its first release, I agree. And I also was one of those people who installed the Classic Editor plugin first thing after starting developing a new WordPress site. But times change and now it’s so much easier and more convenient in use. And as it’s already part of WordPress means themes and plugins developers will provide Gutenberg support in their products more and more.&lt;/p&gt;
&lt;h3&gt;
  
  
  Gutenberg produces only tags you need!
&lt;/h3&gt;

&lt;p&gt;One of my most favourite things about Gutenberg Editor as a developer — it is so easy to maintain. Ever checked your console using Elementor?&lt;/p&gt;

&lt;p&gt;Here’s an example of what you get with placing only ONE paragraph:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--syDyPTGE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A9VZqcTlZW8W4nqD18a8OIA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--syDyPTGE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2A9VZqcTlZW8W4nqD18a8OIA.jpeg" alt="" width="800" height="371"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A screenshot with Elementor’s code mess&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And here’s a comparison with the Gutenberg Editor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cYsnyWur--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AYobviXOwn3JhQcB42GKJQw.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cYsnyWur--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn-images-1.medium.com/max/1024/1%2AYobviXOwn3JhQcB42GKJQw.jpeg" alt="" width="800" height="343"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A screenshot with Gutenberg cleaness&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Big difference, right? It’s also a big difference for SEO, since the less your page has elements, the lesser size it will have, which means the faster it will load.&lt;/p&gt;

&lt;p&gt;It’s also much easier to maintain for developers — you don’t need to use a giant CSS chain to set new styles for elements, your page structure keeps simple and logical.&lt;/p&gt;
&lt;h3&gt;
  
  
  Users won’t mess it up — they can only change what they NEED to change.
&lt;/h3&gt;

&lt;p&gt;Elementor has too many tools. It scares unprepared users. On one hand they can change via Elementor tools anything I’d say. Not only basic stuff like paddings, margins, borders etc. But also create animations, add pseudo elements (&lt;code&gt;:before&lt;/code&gt; and &lt;code&gt;:after&lt;/code&gt;), even append custom CSS for elements.&lt;/p&gt;

&lt;p&gt;But do they actually need it? If you’re owning some cafe and just want to change a couple of pictures on your site — do you really need those animation options?&lt;/p&gt;

&lt;p&gt;There’s a new type of “developers” already — the ones who can build sites only using Elementor (or other builders). So it looks like Elementor was designed specifically for them. Because once a customer wants some extra functionality which isn’t covered by Elementor’s blocks — such “developers” will be helpless.&lt;/p&gt;

&lt;p&gt;Imagine such a picture — you have 20 pages on your site and want to change border-radius from &lt;code&gt;20px&lt;/code&gt; to &lt;code&gt;0px&lt;/code&gt; (make them square). If you are a regular user and don’t use global styles (which are only available in the Elementor PRO, btw), you’re gonna spend some time to adjust all your buttons. And what if you have 50 pages? A hundred pages?&lt;/p&gt;

&lt;p&gt;Don’t get me wrong, Elementor is not completely useless. It’s very good for quickly building landing pages, for example. But most of the options it provides are quite complex for regular users and for developers it causes extra problems with maintaining markup and styles.&lt;/p&gt;
&lt;h3&gt;
  
  
  Easy to extend existing blocks!
&lt;/h3&gt;

&lt;p&gt;Want to add a new style for a Gutenberg block? It’s so easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
* Register custom block styles
*/
add_action('init', 'theme_prefix_register_block_styles');
function theme_prefix_register_block_styles() {
  register_block_style('core/heading', [
    'name' =&amp;gt; 'title-small',
    'label' =&amp;gt; __('Small title', 'theme-slug'),
  ]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;a href="https://developer.wordpress.org/reference/hooks/render_block/"&gt;&lt;code&gt;render_block&lt;/code&gt;&lt;/a&gt; hook you can customise content of any block in Gutenberg.&lt;/p&gt;

&lt;p&gt;For instance, let’s add &lt;code&gt;lazy-load&lt;/code&gt; class to all images from Gutenberg:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/**
* Add custom class for gutenberg images
*/
add_filter( 'render_block', 'theme_prefix_custom_image_class', 10, 2 );
function theme_prefix_custom_image_class( $block_content, $block ) {
  if ( 'core/image' === $block['blockName'] ) {
    $block_content = preg_replace('/wp-image-/', 'lazy-load wp-image-', $block_content);
  }

  return $block_content;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I highly recommend &lt;a href="https://css-tricks.com/a-crash-course-in-wordpress-block-filters/"&gt;this article&lt;/a&gt; from CSS tricks about block filters in Gutenberg — there’s so much stuff you can achieve and extend blocks however you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy to create new blocks!
&lt;/h3&gt;

&lt;p&gt;Either with ACF PRO or using a traditional method. I wrote about how to build a custom Gutenberg Block using Advanced Custom Fields Pro plugin here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/bogdanfromkyiv/how-and-why-to-build-custom-gutenberg-blocks-in-wordpress-23lm-temp-slug-9478592"&gt;How and Why to Build Custom Gutenberg Blocks in WordPress&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The “traditional” way might seem a bit complex — you need to set up a dev environment with Node, use React to build the settings panel of your block. But luckily, there’s a package for quick installation and preparing all you need to create a custom Gutenberg block called &lt;code&gt;@wordpress/create-block&lt;/code&gt;. So all you have to do is run couple commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx @wordpress/create-block your-block-name
$ cd your-block-name
$ npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Voila! You are all setted up and ready to build yet another Gutenberg block 😄.&lt;/p&gt;

&lt;p&gt;Learn more about building Gutenberg blocks via &lt;code&gt;@wordpress/create-block&lt;/code&gt; here: &lt;a href="https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/"&gt;https://developer.wordpress.org/block-editor/reference-guides/packages/packages-create-block/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Nothing is perfect in this world. So is Gutenberg. I can write another article about the cons of the Gutenberg Editor. But the fact is that Gutenberg is a part of WordPress. It evolves and will keep evolving. I don’t know if it will kick out Elementor from the market (I doubt it), but it will become a valuable player for sure. So digging into the Gutenberg ecosystem is one of the needful tasks of modern WordPress developers.&lt;/p&gt;




&lt;p&gt;If you find this article helpful — don’t hesitate to like, subscribe and leave your thoughts in the comments 😊&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/@bogdanfromkyiv"&gt;Read more posts on my Medium blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading!&lt;br&gt;&lt;br&gt;
Stay safe and peace to you!&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>elementor</category>
      <category>gutenberg</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
