Dynamic Theme Blocks in Shopify


Dynamic Theme Blocks in Shopify revolutionize the way merchants and developers enhance their storefronts by offering a flexible and customizable approach to website design. These blocks allow for seamless integration of content and functionality, providing users with the ability to dynamically add, remove, and configure elements within across their entire theme. Let’s get started creating our first Dynamic Theme Block!

Code: https://github.com/ndrishinski/blogs/commit/201667c20489003d86669f6db7473d89ec31fc19 (Design Inspo here)

Prefer Video?

Enjoy, otherwise read on!

Create a Developer Preview Store

Currently, these theme blocks are in developer preview so we need to create a store that allows this feature. Navigate here to create a store!

Create our Theme Block

Now we are going to start editing our theme code! Navigate to the ‘Blocks’ directory, and create a new file called bio.liquid. Paste the following code into the file:

{{ 'bio-block.css' | asset_url | stylesheet_tag }}
<div class="popup" id="about">
  <div class="popup-container">
    <div class="popup-header">
      <div class="button-container">
        <button class="close-btn circle-btn red">
          <i class="fa-solid fa-xmark"></i>
        </button>
        <button class="close-btn circle-btn yellow">
          <i class="fa-solid fa-window-minimize"></i>
        </button>
        <button class="maximize-btn circle-btn green">
          <i class="fa-solid fa-up-right-and-down-left-from-center"></i>
        </button>
      </div>
    </div>
    <div class="popup-body about-container">
      <div class="img-frame">
        {{ block.settings.image-bio | image_url: width:250 | image_tag }}
      </div>
      <div class="hero-content">
        <h1>{{ block.settings.name-bio }}</h1>
        <p>
          {{ block.settings.bio-bio }}
        </p>
      </div>
    </div>
  </div>
</div

{% content_for 'blocks' %}
  
{% schema %}
  {
    "name": "Bio",
    "blocks": [{"type": "@app"}, {"type": "@theme"}],
    "settings": [
      {
        "type": "image_picker",
        "id": "image-bio",
        "label": "Image"
      },
      {
        "type": "text",
        "id": "name-bio",
        "label": "Name",
        "default": "Ruth Thompson"
      },
      {
        "type": "textarea",
        "id": "bio-bio",
        "label": "Bio",
        "default": "I'm a front-end developer. I have dedicated myself to creating visually appealing and user-friendly websites. I'm always open to using new skill sets and I believe in the power of collaboration, working closely with designers and back-end developers to deliver cohesive and effective solutions."
      },
    ],
    "presets": [{"name": "bio"}]
  }
{% endschema %}

Let’s unpack a few items here. First we are importing a CSS file we will create in the next step. Then we have our HTML/Liquid that provides the visual structure and dynamically renders our values that are defined in the schema at the bottom of the file. The schema is what defines what we will see in our theme customizer.

Create our stylesheet

Now we can create our stylesheet we are referencing in bio.liquid. Under the ‘Assets’ directory create a file called bio-block.css and paste the following CSS:

@import url("https://fonts.googleapis.com/css2?family=Marhey:wght@400;500;600;700&family=Quicksand:wght@300;400;500;600;700&display=swap");

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: "Quicksand", sans-serif;
}

#about img {
  width: 100%;
  pointer-events: none;
  user-select: none;
}

#about ul {
  list-style-type: none;
}

#about h1 {
  font-family: "Marhey", sans-serif;
  font-size: clamp(1.9rem, 2vw, 3rem);
  /* opacity: 0; */
  animation: title 1.1s ease 0.3s 1 normal forwards;
}

#about @keyframes title {
  0% {
    opacity: 0;
    letter-spacing: 10px;
    color: #ebeeff;
  }

  100% {
    opacity: 1;
    letter-spacing: 0;
    color: #fff;
  }
}

#about .hidden {
  display: none;
}

html,
#about body {
  position: relative;
  height: 100vh;
  width: 100%;
  background-image: url(https://github.com/ecemgo/mini-samples-great-tricks/assets/13468728/773ac512-567c-4316-81c9-511db0c8a094);
  background-repeat: no-repeat;
  background-size: cover;
}

#about section {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100vh;
}

/* ------------ */
/* ICON STYLES */

#about .icon-container {
  position: absolute;
  bottom: 18px;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 14px;
  padding: 10px 14px;
  background: rgba(255, 255, 255, 0.4);
  box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.24);
  border: 1px solid rgba(255, 255, 255, 0.6);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border-radius: 12px;
}

#about .icon-box {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 50px;
  aspect-ratio: 1/1;
  border-radius: 12px;
  color: #fff;
  font-size: 1.5rem;
  padding: 5px;
  cursor: pointer;
  transition: all 0.4s;
}

#about .icon-box:hover {
  transform: scale(1.1);
}

#about .about {
  background: rgb(182, 164, 248);
  background: linear-gradient(
    30deg,
    rgb(182, 164, 248) 0%,
    rgb(75, 52, 159) 70%
  );
}

#about .testimonial {
  background-color: #f6c66e;
  background-image: linear-gradient(30deg, #f6c66e 0%, rgb(168, 78, 22) 100%);
}

#about .projects {
  background: rgb(169, 207, 124);
  background: linear-gradient(
    310deg,
    rgb(169, 207, 124) 0%,
    rgb(77, 120, 31) 60%
  );
}

#about .contact {
  background-color: rgb(224, 129, 157);
  background-image: linear-gradient(
    340deg,
    rgb(243, 151, 178) 0%,
    rgb(147, 20, 58) 100%
  );
}

/* ------- */
/* POPUP */

#about {
  display: block;
  background: linear-gradient(45deg, #3e3fa8, transparent);
}


#about .popup-container {
  /* position: absolute; */
  /* top: 45%;
  left: 50%;
  transform: translate(-50%, -50%); */
  /* -ms-transform: translate(-50%, -50%); */
  /* width: min(900px, 90%); */
  background: rgba(133, 133, 133, 0.2);
  box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.6);
  border-radius: 12px;
  color: #fff;
  z-index: 30;
  transition: all 0.5s ease-in-out;
}

#about .popup-header {
  position: relative;
  display: flex;
  gap: 8px;
  font-weight: 600;
  padding: 15px 20px 10px;
  font-size: 1.2rem;
}

#about .popup-body {
  color: #f7f7f7;
  height: 70vh;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 30px 50px;
}

#about .popup.maximized .popup-body {
  height: 100vh;
}

#about .button-container {
  display: flex;
  align-items: center;
  column-gap: 8px;
}

#about .circle-btn {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 16px;
  aspect-ratio: 1/1;
  border-radius: 50%;
  border: 0;
  outline: 0;
  cursor: pointer;
  transform: scale(1);
  transition: all 0.2s;
}

#about .circle-btn i {
  opacity: 0;
  transition: all 0.2s;
  font-size: 0.5rem;
  color: rgb(65, 66, 67);
}

#about .circle-btn:hover {
  opacity: 1;
  transform: scale(1.03);
}

#about .button-container:hover i {
  opacity: 1;
}

/* .button-container:hover i:hover,
.button-container:hover i:hover:nth-child(2),
#about .button-container:hover i:hover:nth-child(3) {
  opacity: 1;
} */

#about .red {
  background-color: rgb(255, 96, 92);
}

#about .red i {
  font-size: 0.8rem;
}

#about .yellow {
  background-color: rgb(255, 189, 68);
}

#about .yellow i {
  transform: translateY(-3px);
}

#about .green {
  background-color: rgb(0, 202, 78);
}

#about .green i {
  transform: rotate(90deg);
}

/* --------- */
/* SCROLLBAR */

#about .popup-body::-webkit-scrollbar {
  width: 0.7rem;
}

#about .popup-body::-webkit-scrollbar-track {
  box-shadow: inset 0 0 0.375rem rgb(79, 78, 78);
  border-radius: 0.8rem;
}

#about .popup-body::-webkit-scrollbar-thumb {
  box-shadow: inset 0 0 0.375rem rgba(238, 238, 238, 0.9);
  outline: none;
  border-radius: 0.8rem;
}

/* ------ */
/* ABOUT */

#about .about-container {
  display: grid;
  grid-template-columns: 40% 50%;
  gap: 30px;
  place-items: center;
}

@media only screen and (max-width: 750px) {
  #about .about-container {
    grid-template-columns: none;
  }
}

#about .about-container h1 {
  margin-bottom: 20px;
  line-height: 1.4;
}

#about .about-container p {
  line-height: 1.6;
  font-size: 1.1rem;
}

#about .about-container .img-frame {
  overflow: hidden;
  width: 250px;
  border-radius: 8px;
  box-shadow: rgba(62, 63, 168, 0.8) 0px 10px 30px 8px,
    rgba(62, 63, 168, 0.8) 0px 0px 0px 2px;
}

#about .about-container .img-frame img {
  aspect-ratio: 6/7;
  object-fit: cover;
  transition: transform 1s;
}

#about .about-container .img-frame:hover img {
  transform: rotate(4deg) scale(1.25);
}

/* ------- */
/* SKILLS */

#about .skill-list {
  display: flex;
  flex-direction: column;
  margin-bottom: 40px;
}

#about .skill-list h1 {
  text-align: center;
  margin-bottom: 30px;
}

#about .skill-list ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 12px;
  width: min(100%, 400px);
  margin: 0 auto;
}

#about .skill-list ul li {
  font-size: 1rem;
  font-weight: 500;
  border-radius: 5px;
  background: linear-gradient(
    130deg,
    rgba(162, 234, 55, 0.7) 0%,
    rgba(46, 152, 74, 0.5) 100%
  );
  backdrop-filter: blur(30px);
  box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
  padding: 8px 12px;
}

Add Theme Block to Sections

Now we can save the files we have created thus far, but unforunately we won’t see this new block in the theme customizer yet. Why is that? Well we have to add a couple of lines of code to our theme sections for them to show up. In this Dawn theme, I have a section called ‘custom-liquid.liquid’ that I am going to edit. Paste the following code in:

{%- style -%}
  .section-{{ section.id }}-padding {
    padding-top: calc({{ section.settings.padding_top }}px * 0.75);
    padding-bottom: calc({{ section.settings.padding_bottom }}px  * 0.75);
  }

  @media screen and (min-width: 750px) {
    .section-{{ section.id }}-padding {
      padding-top: {{ section.settings.padding_top }}px;
      padding-bottom: {{ section.settings.padding_bottom }}px;
    }
  }
{%- endstyle -%}
<div class="color-{{ section.settings.color_scheme }} gradient">
  <div class="section-{{ section.id }}-padding">
    {{ section.settings.custom_liquid }}
  </div>

  {% content_for 'blocks' %}
  
</div>

{% schema %}
{
  "name": "t:sections.custom-liquid.name",
  "tag": "section",
  "class": "section",
  "settings": [
    {
      "type": "liquid",
      "id": "custom_liquid",
      "label": "t:sections.custom-liquid.settings.custom_liquid.label",
      "info": "t:sections.custom-liquid.settings.custom_liquid.info"
    },
    {
      "type": "select",
      "id": "color_scheme",
      "options": [
        {
          "value": "accent-1",
          "label": "t:sections.all.colors.accent_1.label"
        },
        {
          "value": "accent-2",
          "label": "t:sections.all.colors.accent_2.label"
        },
        {
          "value": "background-1",
          "label": "t:sections.all.colors.background_1.label"
        },
        {
          "value": "background-2",
          "label": "t:sections.all.colors.background_2.label"
        },
        {
          "value": "inverse",
          "label": "t:sections.all.colors.inverse.label"
        }
      ],
      "default": "background-1",
      "label": "t:sections.all.colors.label"
    },
    {
      "type": "header",
      "content": "t:sections.all.padding.section_padding_heading"
    },
    {
      "type": "range",
      "id": "padding_top",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_top",
      "default": 40
    },
    {
      "type": "range",
      "id": "padding_bottom",
      "min": 0,
      "max": 100,
      "step": 4,
      "unit": "px",
      "label": "t:sections.all.padding.padding_bottom",
      "default": 52
    }
  ],
  "blocks": [{"type": "@theme"}],
  "presets": [
    {
      "name": "t:sections.custom-liquid.presets.name"
    }
  ]
}
{% endschema %}

The two important things that we are adding here is first {% content_for 'blocks' %}. This is the liquid code that enabled our theme blocks to actually show in the UI. It is a requirement.

The second thing, is this line: ‘”blocks”: [{“type”: “@theme”}],’ because this tells the schema to allow all available theme blocks to be added to this section.

Conclusion

After saving these files, we should see a beautiful designed example of a theme block. The important thing to remember is that after the blocks are created, they need to be added to the section via the schema and liquid ‘{% content_for ‘blocks’ %}’ tag. Thanks for following along, and stay tuned for more Shopify related content.