Hot Line:

+1 (917) 730-2010

AI-Powered Web Development, SEO, Shopify & WordPress — Get a Free Consultation Today. Get Started

Tutorials

How To Add Event Calendar in Shopify [2025 – Without APP]

  • 05 Nov, 2025
  • 1 Comments
  • By WebSensePro
How To Add Event Calendar in Shopify [2025 – Without APP]

Create a new snippets file called events_calendar.liquid and add the following code:


{% assign section_id = section.id | replace: '+', '' | replace: '=', '' %}

{%- comment -%} Assign: detect if dynamic metadata content is present in section {%- endcomment -%}
{%- assign enable_metafield_detect = section.settings.enable_metafield_detect -%}

{%- comment -%} Images: all placeholders {%- endcomment -%}
{%- assign placeholder_image_id = 'oRWRlTgBrPo' -%}

{%- comment -%} Images: check image crop aspect ratio {%- endcomment -%}
{%- if section.settings.image_crop == 'none' or section.settings.image_crop == 'match_text_box' -%}
  {%- assign image_crop_width = section.settings.image.width | default: '1000' -%}
  {%- assign image_crop_height = section.settings.image.height | default: '1200' -%}
{%- else -%}
  {%- assign image_crop_width = section.settings.image_crop | split: ':' | first | times: 1000 -%}
  {%- assign image_crop_height = section.settings.image_crop | split: ':' | last | times: 1000 -%}
{%- endif -%}

{%- assign mobile_image_crop_width = section.settings.mobile_background_image.width | default: image_crop_width -%}
{%- assign mobile_image_crop_height = section.settings.mobile_background_image.height | default: image_crop_height -%}

{% comment %} Images: responsive image widths {% endcomment %}
{%- assign widths = '180, 360, 540, 720, 900, 1080, 1296, 1512, 1728, 1950, 2100, 2260, 2450, 2700, 3000, 3350, 3750, 4100' -%}

{%- comment -%} Layout: has user entered measurement value into max-width field? {%- endcomment -%}
{%- if section.settings.max_width contains 'px' or section.settings.max_width contains '%' -%}
  {%- assign max_width = section.settings.max_width -%}
{%- else -%}
  {%- assign max_width = section.settings.max_width | append: 'px' -%}
{%- endif -%}

{% comment %} Metafields: hide section if there is no content {% endcomment %}
{%- if section.settings.image == blank and enable_metafield_detect == true -%}
  {%- break -%}
{%- endif -%}

{%- comment -%} Text: custom font_picker values {%- endcomment -%}
{%- capture font_settings_list -%}
{{ section.settings.main_font | font_face: font_display: 'swap' }}~
{{ section.settings.main_font | font_modify: 'weight', 'bolder' | font_face: font_display: 'swap' }}~
{{ section.settings.main_font | font_modify: 'style', 'italic' | font_face: font_display: 'swap' }}~
{{ section.settings.main_font | font_modify: 'style', 'italic' | font_modify: 'weight', 'bolder' | font_face: font_display: 'swap' }}~
{{ section.settings.heading_font | font_face: font_display: 'swap' }}~
{{ section.settings.heading_font | font_modify: 'weight', 'bolder' | font_face: font_display: 'swap' }}~
{{ section.settings.heading_font | font_modify: 'style', 'italic' | font_face: font_display: 'swap' }}~
{{ section.settings.heading_font | font_modify: 'style', 'italic' | font_modify: 'weight', 'bolder' | font_face: font_display: 'swap' }}
{%- endcapture -%}
{%- assign font_array = font_settings_list | split: '~' -%}

{%- comment -%} CSS {%- endcomment -%}
<style>
  /*** Global styles ***/

[id^="DP--"] {
  margin-left: auto;
  margin-right: auto;
  position: relative;
  text-transform: unset;
  letter-spacing: unset;
  margin: unset;
  padding: unset;
  z-index: 1;
}

.wbs-pro__section * {
  box-sizing: border-box !important;
}

.wbs-pro__section img {
  max-width: 100%;
}

[class^="wbs_pro_-_fixed"] {
  display: none;
}

.wbs-pro__section h1,
.wbs-pro__section h2,
.wbs-pro__section h3,
.wbs-pro__section h4,
.wbs-pro__section h5,
.wbs-pro__section h6 {
  color: inherit;
  text-transform: none;
  letter-spacing: 0;
  margin: 0;
  padding: 0;
}

.wbs-pro__rte,
.wbs-pro__rte p,
.wbs-pro__rte a,
.wbs-pro__rte a:hover,
.wbs-pro__rte a:visited,
.wbs-pro__rte a:focus {
  color: inherit;
  text-transform: none;
  letter-spacing: 0;
  margin: 0;
  padding: 0;
}

.wbs-pro__rte a {
  text-decoration: underline;
}

.wbs-pro__rte p {
  margin-bottom: 0.8em;
}

.wbs-pro__rte p:last-of-type {
  margin-bottom: 0;
}

.wbs-pro__rte ul {
  margin: 1em 0;
  padding-left: 40px;
}

.wbs-pro__rte li {
  list-style: unset;
}

.wbs-pro__absolute-link {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  opacity: 0;
  z-index: 2;
}

.wbs-pro__relative {
  position: relative;
}

h1.wbs-pro__heading,
h2.wbs-pro__heading,
h3.wbs-pro__heading,
h4.wbs-pro__heading,
h5.wbs-pro__heading,
h6.wbs-pro__heading {
  margin-bottom: 1.2rem;
}

.wbs-pro__text {
  margin: 0;
  color: inherit;
}

.wbs-pro__button {
  font-family: inherit;
  cursor: pointer;
  text-transform: unset;
  appearance: none;
  -webkit-appearance: none;
}

.wbs-pro__button::after {
  display: none;
}

.wbs-pro__section button,
.wbs-pro__section input[type="text"],
.wbs-pro__section input[type="email"] {
  appearance: none;
  -webkit-appearance: none;
}

.wbs-pro__sizer {
  margin-left: auto;
  margin-right: auto;
}

.wbs-pro__height-sizer {
  display: table;
}

.wbs-pro__height--x-small {
  height: 125px;
}

.wbs-pro__height--small {
  height: 300px;
}

.wbs-pro__height--medium {
  height: 475px;
}

.wbs-pro__height--large {
  height: 650px;
}

.wbs-pro__height--x-large {
  height: 775px;
}

@media only screen and (max-width: 767px) {
  .wbs-pro__height--x-small {
    height: 94px;
  }

  .wbs-pro__height--small {
    height: 225px;
  }

  .wbs-pro__height--medium {
    height: 357px;
  }

  .wbs-pro__height--large {
    height: 488px;
  }

  .wbs-pro__height--x-large {
    height: 582px;
  }
}

/*** Videos ***/

.wbs-pro__video__wrapper video[loading=lazy],
.wbs-pro__background-video[loading=lazy] {
  opacity: 1;
}

/*** Images ***/
.wbs-pro__image__wrapper {
  display: grid;
  position: relative;
  margin: 0;
}

.wbs-pro__image__wrapper svg {
  display: block;
}

.wbs-pro__image__wrapper:not(.wbs-pro__image__wrapper--contain) svg {
  width: inherit;
  height: inherit;
}

.wbs-pro__image__wrapper > * {
  grid-area: 1 / 1 / 2 / 2;
}

.wbs-pro__image__wrapper img,
.wbs-pro__image__wrapper .wbs-pro__placeholder {
  object-fit: cover;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 100%;
  position: absolute;
}

.wbs-pro__image__wrapper--contain img {
  object-fit: contain;
}

.wbs_pro__image-link {
  display: block;
  overflow: hidden;
}

/*** Widths ***/

div.DP__widths {
  display: inline-block !important;
  vertical-align: top;
  font-size: 0;
  margin-left: auto;
  margin-right: auto;
}

div.DP__widths * {
  font-size: initial;
}

@media (max-width: 767px) {
  div.DP__widths {
    width: 100% !important;
  }
}

/*** Flex ***/
.wbs-pro__flex {
  display: flex;
}

.wbs-pro__flex-wrap {
  flex-wrap: wrap;
}

.wbs-pro__flex--1-per-row .wbs-pro__flex-item {
  flex-basis: 100%;
}

.wbs-pro__flex--2-per-row .wbs-pro__flex-item {
  flex-basis: 50%;
}

.wbs-pro__flex--3-per-row .wbs-pro__flex-item {
  flex-basis: 33.3333%;
}

.wbs-pro__flex--4-per-row .wbs-pro__flex-item {
  flex-basis: 25%;
}

.wbs-pro__flex--5-per-row .wbs-pro__flex-item {
  flex-basis: 20%;
}

.wbs-pro__flex--6-per-row .wbs-pro__flex-item {
  flex-basis: 16.6666%;
}

.wbs-pro__flex--7-per-row .wbs-pro__flex-item {
  flex-basis: 14.3%;
}

.wbs-pro__flex--8-per-row .wbs-pro__flex-item {
  flex-basis: 12.5%;
}

.wbs-pro__flex-row-reverse {
  flex-direction: row-reverse;
}

.wbs-pro__grid-row-reverse {
  direction: rtl;
}

.wbs-pro__grid-row-reverse * {
  direction: ltr;
}

.wbs-pro__justify-left {
  justify-content: flex-start;
  text-align: left;
}

.wbs-pro__justify-center {
  justify-content: center;
  text-align: center;
}

.wbs-pro__justify-right {
  justify-content: flex-end;
  text-align: right;
}

.wbs-pro__justify-justify {
  justify-content: space-between;
  text-align: justify;
}

.wbs-pro__align-top {
  align-items: flex-start;
}

.wbs-pro__align-center {
  align-items: center;
}

.wbs-pro__align-bottom {
  align-items: flex-end;
}

/** Text alignment **/

.wbs-pro__text-alignment-left {
  text-align: left;
}

.wbs-pro__text-alignment-center {
  text-align: center;
}

.wbs-pro__text-alignment-right {
  text-align: right;
}

.wbs-pro__text-alignment-justify {
  text-align: justify;
}

/*** Grid ***/
.wbs-pro__grid {
  display: grid;
}

.wbs-pro__grid--1-per-row {
  grid-template-columns: 1fr;
}

.wbs-pro__grid--2-per-row {
  grid-template-columns: 1fr 1fr;
}

.wbs-pro__grid--3-per-row {
  grid-template-columns: 1fr 1fr 1fr;
}

.wbs-pro__grid--4-per-row {
  grid-template-columns: 1fr 1fr 1fr 1fr;
}

.wbs-pro__grid--5-per-row {
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
}

.wbs-pro__grid--6-per-row {
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
}

.wbs-pro__grid--7-per-row {
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

.wbs-pro__grid--8-per-row {
  grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
}

.wbs-pro__grid-reverse {
  direction: rtl;
}

.wbs-pro__grid-reverse * {
  direction: ltr;
}

/*** Helpers ***/
.wbs-pro__unset {
  font-family: unset;
  font-size: unset;
  letter-spacing: unset;
  line-height: unset;
  margin: unset;
  padding: unset;
  list-style: none;
}

.wbs-pro__force-full-width {
  width: 100vw;
  position: relative;
  left: 50%;
  right: 50%;
  margin-left: -50vw;
  margin-right: -50vw;
}

.wbs-pro__visually-hidden {
  position: absolute !important;
  height: 1px;
  width: 1px;
  overflow: hidden;
  clip: rect(1px, 1px, 1px, 1px);
  white-space: nowrap;
}

.wbs-pro__recaptcha-message {
  margin-top: 0.8em;
  font-size: 0.8em;
}

.wbs-pro__recaptcha-message a,
.wbs-pro__recaptcha-message a:hover,
.wbs-pro__recaptcha-message a:focus {
  font-style: italic;
  color: inherit;
  font-size: inherit;
}

/** Pages **/
.wbs-pro__page-intro {
  width: 100%;
}

/** Theme editor warnings **/

.wbs-pro__no-app-warning {
  position: relative;
  overflow: hidden;
  padding: 1em;
}

.wbs-pro__no-app-warning-text {
  position: relative;
  background: #ff0;
  font-weight: bold;
  text-transform: uppercase;
  padding: 10px;
  max-width: 50%;
  margin: 0 auto;
}

.wbs-pro__no-app-warning-background {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 130%;
  background: repeating-linear-gradient(-45deg, #ff0, #ff0 20px, #000 20px, #000 40px);
  animation: wbs-pro__warning-background 1s linear infinite;
}

@keyframes wbs-pro__warning-background {
  to {
    transform: translateX(-56px);
  }
}

/** Animations **/

[style*="--wbs-pro-animate"] {
  will-change: transform;
  transform: translateZ(0);
  opacity: 0;
}

.wbs-pro__animation-applied {
  overflow: hidden;
}

.wbs-pro__animation-applied [style*="--wbs-pro-animate"] {
  --wbs-pro-animation-multiplier: 0.3s;
  animation-duration: 0.5s;
  animation-timing-function: ease-out;
  animation-fill-mode: forwards;
  animation-delay: calc(var(--wbs-pro-animation-multiplier) * var(--wbs-pro-animate));
}

@keyframes wbs-pro__animation--fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

@keyframes wbs-pro__animation--fade-in-left {
  0% {
    opacity: 0;
    transform: translateX(10%);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes wbs-pro__animation--fade-in-right {
  0% {
    opacity: 0;
    transform: translateX(-10%);
  }
  100% {
    opacity: 1;
    transform: translateX(0);
  }
}

@keyframes wbs-pro__animation--fade-in-down {
  0% {
    opacity: 0;
    transform: translateY(-10%);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes wbs-pro__animation--fade-in-up {
  0% {
    opacity: 0;
    transform: translateY(10%);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

/** Parallax **/
.wbs-pro__parallax {
  position: relative;
  z-index: 1;
}

.wbs-pro__parallax > .wbs-pro__parallax-img {
  position: absolute;
  object-fit: cover;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: -1;
}

/** Responsive helpers **/

.wbs-pro__desktop--visible {
  display: inherit;
}

.wbs-pro__desktop--hidden {
  display: none !important;
}

/* Landscape phone to portrait tablet */
@media (max-width: 767px) {
  .wbs-pro__tablet--visible {
    display: block !important;
  }

  .wbs-pro__tablet--hidden {
    display: none !important;
  }
}

/* Landscape phones and down */
@media (max-width: 480px) {
  .wbs-pro__mobile--visible {
    display: block !important;
  }

  .wbs-pro__mobile--hidden {
    display: none !important;
  }
}

/** Theme specific **/

/* Dawn */
[class*="wbs-pro"]:empty {
  display: inherit;
}

/* Debut - offset margin from the header */
/* .main-content[id="MainContent"] .shopify-section[class*="DP__"]:first-child,
.main-content[id="MainContent"] [id*="DP__"] {
  margin-top: -35px;
}

@media only screen and (min-width: 750px) {
  .main-content[id="MainContent"] .shopify-section[class*="DP__"]:first-child,
  .main-content[id="MainContent"] .DP__widths.DP__width--half:first-child,
  .main-content[id="MainContent"] .DP__widths.DP__width--half:first-child + .DP__widths.DP__width--half,
  .main-content[id="MainContent"] [id*="DP__"] {
    margin-top: -55px;
  }
} */

/* Brooklyn - prevent default margins (but leave them on theme sections) */
.index-sections [class*="DP__"] {
  margin-top: 0;
}

.index-sections [class*="DP__"].shopify-section:first-child:not(.shopify-section--full-width) {
  margin-top: 0;
}

/* Impulse - prevent default margins (but leave them on theme sections) */
.main-content [class*="DP__"] {
  margin-top: 0;
  margin-bottom: 0;
}

/* Cornerstone - override overflow: hidden */
[class*="DP__"].shopify-section {
  overflow: unset;
}

  {%- if section.settings.override_theme_font != blank -%}
    {%- for font in font_array -%}
      {%- unless font contains 'error' -%}{{ font }}{%- endunless -%}
    {%- endfor -%}
    .DP--{{ section_id }} {
      --main-font: {{ section.settings.main_font.family }}, {{ section.settings.main_font.fallback_families }};
      --main-font-weight: {{ section.settings.main_font.weight }};
      --main-font-style: {{ section.settings.main_font.style }};
      --heading-font: {{ section.settings.heading_font.family }}, {{ section.settings.heading_font.fallback_families }};
      --heading-font-weight: {{ section.settings.heading_font.weight }};
      --heading-font-style: {{ section.settings.heading_font.style }};
    }
  {%- endif -%}

  .DP--{{ section_id }} {
    margin-top: {{ section.settings.outer_margin }}px;
    margin-bottom: {{ section.settings.outer_margin }}px;
    {%- if section.settings.override_theme_font != blank -%}
      font-family: var(--main-font);
      font-weight: var(--main-font-weight);
      font-style: var(--main-font-style);
    {%- endif -%}

  }

  .DP--{{ section_id }} .wbs-pro__sizer {
    padding-top: {{ section.settings.inner_padding }}px;
    padding-bottom: {{ section.settings.inner_padding }}px;
    width: {{ section.settings.base_width }}%;
    max-width: {{ max_width }};
  }

  .DP--{{ section_id }} .wbs-pro__background {
    background: {{ section.settings.background_solid }};
    background: {{ section.settings.background_color }};
  }

  {%- comment -%} Full design control CSS variables {%- endcomment -%}
  {%- if section.settings.enable_full_design_control == true -%}
    .DP--{{ section_id }} {
      {%- if section.settings.button_background_color != blank and section.settings.button_background_color.alpha > 0 -%}
        --dp-button-background-color: {{ section.settings.button_background_color }};
      {%- endif -%}
      {%- if section.settings.button_border_color != blank and section.settings.button_border_color.alpha > 0 -%}
        --dp-button-border-color: {{ section.settings.button_border_color }};
      {%- endif -%}
      {%- if section.settings.button_hover_background_color != blank and section.settings.button_hover_background_color.alpha > 0 -%}
        --dp-button-hover-background-color: {{ section.settings.button_hover_background_color }};
      {%- endif -%}
      {%- if section.settings.button_text_color != blank and section.settings.button_text_color.alpha > 0 -%}
        --dp-button-text-color: {{ section.settings.button_text_color }};
      {%- endif -%}
      {%- if section.settings.weekdays_background_color != blank and section.settings.weekdays_background_color.alpha > 0 -%}
        --dp-weekdays-background-color: {{ section.settings.weekdays_background_color }};
      {%- endif -%}
      {%- if section.settings.weekdays_text_color != blank and section.settings.weekdays_text_color.alpha > 0 -%}
        --dp-weekdays-text-color: {{ section.settings.weekdays_text_color }};
      {%- endif -%}
      {%- if section.settings.grid_border_color != blank and section.settings.grid_border_color.alpha > 0 -%}
        --dp-grid-border-color: {{ section.settings.grid_border_color }};
      {%- endif -%}
      {%- if section.settings.day_border_color != blank and section.settings.day_border_color.alpha > 0 -%}
        --dp-day-border-color: {{ section.settings.day_border_color }};
      {%- endif -%}
      {%- if section.settings.day_background_color != blank and section.settings.day_background_color.alpha > 0 -%}
        --dp-day-background-color: {{ section.settings.day_background_color }};
      {%- endif -%}
      {%- if section.settings.day_number_text_color != blank and section.settings.day_number_text_color.alpha > 0 -%}
        --dp-day-number-text-color: {{ section.settings.day_number_text_color }};
      {%- endif -%}
      {%- if section.settings.today_background_color != blank and section.settings.today_background_color.alpha > 0 -%}
        --dp-today-background-color: {{ section.settings.today_background_color }};
      {%- endif -%}
      {%- if section.settings.today_text_color != blank and section.settings.today_text_color.alpha > 0 -%}
        --dp-today-text-color: {{ section.settings.today_text_color }};
      {%- endif -%}
      {%- if section.settings.other_month_background_color != blank and section.settings.other_month_background_color.alpha > 0 -%}
        --dp-other-month-background-color: {{ section.settings.other_month_background_color }};
      {%- endif -%}
      {%- if section.settings.other_month_text_color != blank and section.settings.other_month_text_color.alpha > 0 -%}
        --dp-other-month-text-color: {{ section.settings.other_month_text_color }};
      {%- endif -%}
      {%- if section.settings.calendar_title_text_color != blank and section.settings.calendar_title_text_color.alpha > 0 -%}
        --dp-calendar-title-text-color: {{ section.settings.calendar_title_text_color | default: '#121212' }};
      {%- endif -%}
      {%- if section.settings.navigation_arrow_color != blank and section.settings.navigation_arrow_color.alpha > 0 -%}
        --dp-navigation-arrow-color: {{ section.settings.navigation_arrow_color }};
      {%- endif -%}
      {%- if section.settings.modal_event_text_color != blank and section.settings.modal_event_text_color.alpha > 0 -%}
        --dp-modal-event-text-color: {{ section.settings.modal_event_text_color }};
      {%- endif -%}
      {%- if section.settings.modal_background_color != blank and section.settings.modal_background_color.alpha > 0 -%}
        --dp-modal-background-color: {{ section.settings.modal_background_color }};
      {%- endif -%}
      {%- if section.settings.modal_content_background_color != blank and section.settings.modal_content_background_color.alpha > 0 -%}
        --dp-modal-content-background-color: {{ section.settings.modal_content_background_color }};
      {%- endif -%}
      {%- if section.settings.event_details_border_color != blank and section.settings.event_details_border_color.alpha > 0 -%}
        --dp-event-details-border-color: {{ section.settings.event_details_border_color }};
      {%- endif -%}
    }
  {%- endif -%}

  .DP--{{ section_id }} .wbs-pro__background-image {
    position: absolute;
    object-fit: cover;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: -1;
  }

  .DP--{{ section_id }} .wbs-pro__calendar-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
    flex-wrap: wrap;
  }

  .DP--{{ section_id }} .wbs-pro__calendar-navigation {
    display: flex;
    align-items: center;
  }

  .DP--{{ section_id }} .wbs-pro__calendar-navigation button {
    background: none;
    border: none;
    font-size: calc(var(--dp-g-body-size, var(--dp-body-size, 18px)) * {{ section.settings.text_size | times: 0.01 }});
    cursor: pointer;
    padding: 0 15px;
    color: var(--dp-navigation-arrow-color, #333333);
    fill: var(--dp-navigation-arrow-color, #333333);
  }

  .DP--{{ section_id }} .wbs-pro__calendar-title {
    margin: 0 15px;
    min-width: 200px;
    text-align: center;
    color: var(--dp-calendar-title-text-color, #333333);
    font-size: calc(var(--dp-g-heading-size, var(--dp-heading-size, 35px)) * {{ section.settings.text_size | times: 0.01 }});
    {%- if section.settings.override_theme_font != blank -%}
      font-family: var(--heading-font);
      font-weight: var(--heading-font-weight);
      font-style: var(--heading-font-style);
    {%- endif -%}
  }

  .DP--{{ section_id }} .wbs-pro__calendar-controls {
    display: flex;
    align-items: center;
    margin: 0 10px;
  }

  .DP--{{ section_id }} .wbs-pro__calendar-button-today {
    padding: 8px 16px;
    background: var(--dp-button-background-color, #f0f0f0);
    border: 1px solid var(--dp-button-border-color, #ddd);
    border-radius: 4px;
    cursor: pointer;
    color: var(--dp-button-text-color, #333333);
    {%- if section.settings.override_theme_font != blank -%}
      font-family: var(--main-font);
      font-weight: var(--main-font-weight);
      font-style: var(--main-font-style);
    {%- endif -%}
  }

  .DP--{{ section_id }} .wbs-pro__calendar-button-today:hover {
    background: var(--dp-button-hover-background-color, #e0e0e0);
  }

  .DP--{{ section_id }} .wbs-pro__calendar-container {
    width: 100%;
    overflow-x: hidden;
  }

  .DP--{{ section_id }} .wbs-pro__weekdays {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    text-align: center;
    font-weight: bold;
    background: var(--dp-weekdays-background-color, #f0f0f0);
    color: var(--dp-weekdays-text-color, #333333);
    padding: 10px 0;
    border-radius: 4px 4px 0 0;
    width: 100%;
  }

  .DP--{{ section_id }} .wbs-pro__weekdays div,
  .DP--{{ section_id }} .wbs-pro__weekday {
    width: 100%;
    box-sizing: border-box;
  }

  .DP--{{ section_id }} .wbs-pro__calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    grid-auto-rows: minmax(120px, auto);
    border: 1px solid var(--dp-grid-border-color, #ddd);
    border-radius: 0 0 4px 4px;
    width: 100%;
  }

  .DP--{{ section_id }} .wbs-pro__calendar-day {
    border: 1px solid var(--dp-day-border-color, #ddd);
    padding: 10px;
    min-height: 120px;
    position: relative;
    width: 100%;
    box-sizing: border-box;
    background-color: var(--dp-day-background-color);
    min-width: 0; /* Prevents content from expanding the cell */
  }

  .DP--{{ section_id }} .wbs-pro__day-number {
    position: absolute;
    top: 5px;
    right: 5px;
    width: 25px;
    height: 25px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    color: var(--dp-day-number-text-color, #333333);
  }

  .DP--{{ section_id }} .wbs-pro__today .wbs-pro__day-number {
    background-color: var(--dp-today-background-color, #4e73df);
    color: var(--dp-today-text-color, white);
  }

  .DP--{{ section_id }} .wbs-pro__other-month {
    background-color: var(--dp-other-month-background-color, #f9f9f9);
    color: var(--dp-other-month-text-color, #aaa);
  }

  .DP--{{ section_id }} .wbs-pro__event-container {
    margin-top: 30px;
    overflow-y: auto;
    max-height: 80px;
    width: 100%;
  }

  .DP--{{ section_id }} .wbs-pro__event {
    margin-bottom: 5px;
    padding: 5px 8px;
    border-radius: 3px;
    font-size: calc(var(--dp-g-body-size, var(--dp-body-size, 18px)) * {{ section.settings.text_size | times: 0.008 }});
    color: var(--dp-event-text-color, white);
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    box-sizing: border-box;
    width: 100%;
    border-left: 4px solid;
  }

  .DP--{{ section_id }} .wbs-pro__event-time {
    font-weight: bold;
    margin-right: 4px;
    font-size: calc(var(--dp-g-body-size, var(--dp-body-size, 18px)) * {{ section.settings.text_size | times: 0.01 }});
    opacity: 0.9;
  }

  .DP--{{ section_id }} .wbs-pro__event-title {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
    display: inline-block;
    vertical-align: bottom;
  }

  .DP--{{ section_id }} .wbs-pro__event-modal {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: var(--dp-modal-background-color, rgba(0, 0, 0, 0.5));
    z-index: 1000;
    justify-content: center;
    align-items: center;
  }

  .DP--{{ section_id }} .wbs-pro__modal-content {
    background-color: var(--dp-modal-content-background-color, white);
    padding: 25px;
    border-radius: 10px;
    max-width: 500px;
    width: 95%;
    max-height: 80vh;
    overflow-y: auto;
    position: relative;
    color: var(--dp-modal-event-text-color, #333333);
  }

  .DP--{{ section_id }} .wbs-pro__modal-title {
    font-size: calc(var(--dp-g-small-heading-size, var(--dp-small-heading-size, 24px)) * {{ section.settings.text_size | times: 0.01 }});
    {%- if section.settings.override_theme_font != blank -%}
      font-family: var(--heading-font);
      font-weight: var(--heading-font-weight);
      font-style: var(--heading-font-style);
    {%- endif -%}
  }

  .DP--{{ section_id }} .wbs-pro__close-modal {
    position: absolute;
    top: 0px;
    right: 010px;
    font-size: 24px;
    cursor: pointer;
    font-family: system-ui;
  }

  .DP--{{ section_id }} .wbs-pro__event-details {
    margin-top: 20px;
    padding-top: 20px;
    border-top: 1px solid var(--dp-event-details-border-color, #eee);
  }

  {%- if section.settings.custom_css != blank -%}
    {%- assign custom_section_declarations = section.settings.custom_css | split: '}' -%}
    {%- for declaration in custom_section_declarations -%}
      {%- if declaration contains '{' -%}
        #DP--{{ section_id }} {{ declaration }} }
      {%- endif -%}
    {%- endfor -%}
  {%- endif -%}

  /* Smaller mobile devices */
  @media (max-width: 480px) {
    .DP--{{ section_id }} {
      --dp-heading-size: 24px;
      --dp-body-size: 14px;
      --dp-small-heading-size: 16px;
    }

    .DP--{{ section_id }} .wbs-pro__sizer {
      min-width: 95%;
    }

    .DP--{{ section_id }} .wbs-pro__calendar-navigation {
      margin: auto;
    }

    .DP--{{ section_id }} .wbs-pro__calendar-title {
      flex: 1;
    }

    .DP--{{ section_id }} .wbs-pro__calendar-controls {
      justify-content: center;
      margin: 0 auto;
      margin-top: 10px;
    }

    .DP--{{ section_id }} .wbs-pro__calendar-controls button,
    .DP--{{ section_id }} .wbs-pro__view-button {
      padding: 6px 10px;
      font-size: calc(var(--dp-g-body-size, var(--dp-body-size, 18px)) * {{ section.settings.text_size | times: 0.008 }});
    }

    .DP--{{ section_id }} .wbs-pro__weekdays {
      grid-template-columns: repeat(7, minmax(0, 1fr));
    }

    .DP--{{ section_id }} .wbs-pro__calendar-grid {
      grid-template-columns: repeat(7, minmax(0, 1fr));
      grid-auto-rows: minmax(80px, auto);
    }

    .DP--{{ section_id }} .wbs-pro__weekdays div,
    .DP--{{ section_id }} .wbs-pro__weekday {
      font-size: calc(var(--dp-g-body-size, var(--dp-body-size, 18px)) * {{ section.settings.text_size | times: 0.01 }});
      padding: 5px 0;
    }

    .DP--{{ section_id }} .wbs-pro__day-number {
      width: 20px;
      height: 20px;
      font-size: calc(var(--dp-g-body-size, var(--dp-body-size, 18px)) * {{ section.settings.text_size | times: 0.01 }});
    }

    .DP--{{ section_id }} .wbs-pro__calendar-day {
      min-height: 60px;
      padding: 3px 2px;
    }

    .DP--{{ section_id }} .wbs-pro__event-container {
      max-height: 40px;
      margin-top: 20px;
    }

    .DP--{{ section_id }} .wbs-pro__event {
      padding: 1px 3px;
      font-size: calc(var(--dp-g-body-size, var(--dp-body-size, 18px)) * {{ section.settings.text_size | times: 0.01 }});
      border-radius: 2px;
      margin-bottom: 1px;
    }

    .DP--{{ section_id }} .wbs-pro__event-time {
      min-width: 30px;
      font-size: calc(var(--dp-g-body-size, var(--dp-body-size, 18px)) * {{ section.settings.text_size | times: 0.01 }});
    }

    .DP--{{ section_id }} .wbs-pro__modal-content {
      padding: 15px;
    }

    {%- if section.settings.mobile_custom_css != blank -%}
      {%- assign mobile_custom_declarations = section.settings.mobile_custom_css | split: '}' -%}
      {%- for declaration in mobile_custom_declarations -%}
        {%- if declaration contains '{' -%}
          #DP--{{ section_id }} {{ declaration }} }
        {%- endif -%}
      {%- endfor -%}
    {%- endif -%}
  }

</style>

<section id="DP--{{ section_id }}" class="DP--{{ section_id }} wbs-pro__main wbs-pro__section {{ section.settings.custom_classes }} {% if section.settings.custom_style == 'scheme' -%}{{ section.settings.style_scheme }}{% elsif section.settings.custom_style == 'global' -%}wbs-pro__global-style{% else -%}wbs-pro__section-style{% endif %}">
  {%- if section.settings.anchor_id != blank -%}
    <div id="{{ section.settings.anchor_id | remove: '#' | handleize }}" class="wbs-pro__anchor-id" style="height: 0; overflow: hidden;"></div>
  {%- endif -%}
  <div class="wbs-pro__background">
    {%- if section.settings.show_background_image -%}
      {%- if section.settings.background_image != blank -%}
        {{ section.settings.background_image | image_url: width: section.settings.background_image.width | image_tag:  widths: widths, sizes: '100vw', class: 'wbs-pro__background-image' }}
      {%- elsif enable_metafield_detect == false -%}
        <img src="https://cdn.shopify.com/s/files/1/0577/7673/4361/files/{{ placeholder_image_id }}_1600x.jpg"
            class="wbs-pro__background-image"
            alt=""
            loading="lazy"
            class="wbs-pro__background-image"
            width="1600"
            height="1000">
      {%- endif -%}
    {%- endif -%}
    <div class="wbs-pro__sizer">
      <div class="wbs-pro__calendar-header">
        <div class="wbs-pro__calendar-navigation">
          <button class="wbs-pro__calendar-button wbs-pro__calendar-button-prev" data-calendar="prev-month"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M16.67 0l2.83 2.829-9.339 9.175 9.339 9.167-2.83 2.829-12.17-11.996z"/></svg></button>
          <h2 class="wbs-pro__calendar-title wbs-pro__heading" data-calendar="current-month"></h2>
          <button class="wbs-pro__calendar-button wbs-pro__calendar-button-next" data-calendar="next-month"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M7.33 24l-2.83-2.829 9.339-9.175-9.339-9.167 2.83-2.829 12.17 11.996z"/></svg></button>
        </div>

        <div class="wbs-pro__calendar-controls">
          <button class="wbs-pro__calendar-button-today" data-calendar="today-btn">{{ 'today' | t }}</button>
        </div>
      </div>

      <div class="wbs-pro__calendar-container">
        <div class="wbs-pro__weekdays" data-calendar="month-weekdays">
          <div class="wbs-pro__weekday wbs-pro__weekday-sun">Sun</div>
          <div class="wbs-pro__weekday wbs-pro__weekday-mon">Mon</div>
          <div class="wbs-pro__weekday wbs-pro__weekday-tue">Tue</div>
          <div class="wbs-pro__weekday wbs-pro__weekday-wed">Wed</div>
          <div class="wbs-pro__weekday wbs-pro__weekday-thu">Thu</div>
          <div class="wbs-pro__weekday wbs-pro__weekday-fri">Fri</div>
          <div class="wbs-pro__weekday wbs-pro__weekday-sat">Sat</div>
        </div>
        <div data-calendar="calendar-grid" class="wbs-pro__calendar-grid"></div>
      </div>

      <div data-calendar="event-modal" class="wbs-pro__event-modal">
        <div class="wbs-pro__modal-content">
          <span class="wbs-pro__close-modal">×</span>
          <h3 class="wbs-pro__modal-title wbs-pro__small-heading" data-calendar="modal-title"></h3>
          <div class="wbs-pro__modal-description wbs-pro__text wbs-pro__rte" data-calendar="modal-description"></div>
          <div class="wbs-pro__event-details">
            <p class="wbs-pro__modal-date"><strong>Date:</strong> <span data-calendar="modal-date"></span></p>
            <p class="wbs-pro__modal-time"><strong>Time:</strong> <span data-calendar="modal-time"></span></p>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

{% schema %}
{
  "name": "Events calendar",
  "class": "events-calendar-section",
  "settings": [
    {
      "type": "header",
      "content": "Design"
    },
    {
  "type": "header",
  "content": "Settings"
},
{
  "type": "text",
  "id": "timezone",
  "label": "Timezone",
  "default": "America/New_York",
  "info": "Enter IANA timezone (e.g., America/New_York, Europe/London, Asia/Tokyo, UTC)"
},
{
  "type": "checkbox",
  "id": "show_timezone",
  "label": "Show timezone abbreviation",
  "default": true,
  "info": "Show timezone (like EST, PST) next to event times"
},
    {
      "type": "color",
      "id": "background_solid",
      "label": "Background",
      "alpha": true
    },
    {
      "type": "color_background",
      "id": "background_color",
      "label": "Background gradient",
      "info": "Background gradient replaces background where possible."
    },
    {
      "type": "checkbox",
      "id": "show_background_image",
      "label": "Show background image",
      "default": false
    },
    {
      "type": "image_picker",
      "id": "background_image",
      "label": "Background image"
    },
    {
      "type": "checkbox",
      "id": "enable_full_design_control",
      "label": "Enable full design control",
      "default": false
    },
    {
      "type": "range",
      "id": "text_size",
      "label": "Text size",
      "min": 80,
      "max": 150,
      "step": 5,
      "default": 100,
      "unit": "%"
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "header",
      "content": "Full design control"
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "header",
      "content": "Button Colors"
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "button_background_color",
      "label": "Button background",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "button_border_color",
      "label": "Button border",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "button_hover_background_color",
      "label": "Button hover background",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "button_text_color",
      "label": "Button text",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "header",
      "content": "Calendar Grid Colors"
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "weekdays_background_color",
      "label": "Weekdays background",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "weekdays_text_color",
      "label": "Weekdays text",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "grid_border_color",
      "label": "Grid border",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "day_border_color",
      "label": "Day border",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "day_background_color",
      "label": "Day background",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "day_number_text_color",
      "label": "Day number text",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "header",
      "content": "Today/Current Day Colors"
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "today_background_color",
      "label": "Today background",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "today_text_color",
      "label": "Today text",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "header",
      "content": "Other Month Colors"
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "other_month_background_color",
      "label": "Other month background",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "other_month_text_color",
      "label": "Other month text",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "header",
      "content": "Navigation & Heading Colors"
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "calendar_title_text_color",
      "label": "Calendar title text",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "navigation_arrow_color",
      "label": "Navigation arrows",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "header",
      "content": "Modal Colors"
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "modal_event_text_color",
      "label": "Modal event text",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "modal_background_color",
      "label": "Modal background",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "modal_content_background_color",
      "label": "Modal content background",
      "alpha": true
    },
    {
      "visible_if": "{{ section.settings.enable_full_design_control == true }}",
      "type": "color",
      "id": "event_details_border_color",
      "label": "Event details border",
      "alpha": true
    },
    {
      "type": "header",
      "content": "Layout"
    },
    {
      "type": "range",
      "id": "base_width",
      "label": "Size",
      "min": 60,
      "max": 100,
      "default": 100,
      "unit": "%"
    },
    {
      "type": "text",
      "id": "max_width",
      "label": "Maximum width",
      "placeholder": "eg. 1200px",
      "info": "Sets width constraint for content."
    },
    {
      "type": "range",
      "id": "inner_padding",
      "label": "Inner padding",
      "info": "Only applies to top and bottom.",
      "min": 0,
      "max": 100,
      "default": 60,
      "step": 5,
      "unit": "px"
    },
    {
      "type": "range",
      "id": "outer_margin",
      "label": "Outer margin",
      "info": "Only applies to top and bottom.",
      "min": 0,
      "max": 100,
      "default": 0,
      "step": 5,
      "unit": "px"
    },
    {
      "type": "header",
      "content": "Advanced"
    },
    {
      "type": "liquid",
      "id": "custom_css",
      "label": "CSS"
    },
    {
      "type": "liquid",
      "id": "mobile_custom_css",
      "label": "Mobile CSS",
      "info": "Applied on screens less than 480px."
    },
  
    {
      "visible_if": "{{ false }}",
      "type": "select",
      "id": "custom_style",
      "label": "Custom style*",
      "info": "* Custom styles section is required.",
      "options": [
        {
          "value": "section",
          "label": "Default section settings"
          },
        {
          "value": "scheme",
          "label": "Apply scheme"
        },
        {
          "value": "global",
          "label": "Inherit global style"
        }
      ],
      "default": "global"
    },
    {
      "visible_if": "{{ section.settings.custom_style == 'scheme' }}",
      "type": "text",
      "id": "style_scheme",
      "label": "Style scheme name"
    },
    {
      "type": "checkbox",
      "id": "override_theme_font",
      "label": "Override default theme font",
      "default": false
    },
    {
      "visible_if": "{{ section.settings.override_theme_font == true }}",
      "type": "font_picker",
      "id": "heading_font",
      "label": "Heading",
      "default": "serif"
    },
    {
      "visible_if": "{{ section.settings.override_theme_font == true }}",
      "type": "font_picker",
      "id": "main_font",
      "label": "Text",
      "default": "sans-serif"
    },
    {
      "visible_if": "{{ false }}",
      "type": "checkbox",
      "id": "enable_metafield_detect",
      "label": "Enable metafield detection",
      "default": false,
      "info": "Hide section if content is not detected."
    }
  ],
  "blocks": [
    {
      "type": "event",
      "name": "Event",
      "settings": [
        {
          "type": "text",
          "id": "title",
          "label": "Title",
          "default": "Event"
        },
        {
          "type": "richtext",
          "id": "description",
          "label": "Description"
        },
        {
          "type": "text",
          "id": "start_date",
          "label": "Date (YYYY-MM-DD)"
        },
        {
          "type": "checkbox",
          "id": "all_day",
          "label": "All day event",
          "default": true
        },
        {
          "visible_if": "{{ block.settings.all_day == false }}",
          "type": "text",
          "id": "start_time",
          "label": "Start time (HH:MM)"
        },
        {
          "visible_if": "{{ block.settings.all_day == false }}",
          "type": "text",
          "id": "end_date",
          "label": "End date (YYYY-MM-DD)"
        },
        {
          "visible_if": "{{ block.settings.all_day == false }}",
          "type": "text",
          "id": "end_time",
          "label": "End time (HH:MM)"
        },
        {
          "type": "header",
          "content": "Recurrence"
        },
        {
          "type": "checkbox",
          "id": "recurring",
          "label": "Event repeats",
          "default": false
        },
        {
          "visible_if": "{{ block.settings.recurring == true }}",
          "type": "select",
          "id": "frequency",
          "label": "Frequency",
          "options": [
            { "value": "weekly", "label": "Weekly" },
            { "value": "monthly", "label": "Monthly" }
          ],
          "default": "weekly"
        },
        {
          "visible_if": "{{ block.settings.recurring == true }}",
          "type": "text",
          "id": "repeat_until_date",
          "label": "Repeat until (YYYY-MM-DD)",
          "info": "Leave blank to repeat for one year"
        },
        {
          "type": "header",
          "content": "Design"
        },

        {
          "type": "color",
          "id": "event_color",
          "label": "Event background",
          "default": "#ccc"
        },
        {
          "type": "color",
          "id": "event_border_color",
          "label": "Event side border",
          "default": "#121212"
        },
        {
          "type": "color",
          "id": "event_text_color",
          "label": "Event text",
          "default": "#121212"
        }
      ]
    }
  ],
  "presets": [
    {
      "name": "Events calendar",
      "category": "Events"
    }
  ],
   "locales": {
     "en": {
       "january": "January",
       "february": "February",
       "march": "March",
       "april": "April",
       "may": "May",
       "june": "June",
       "july": "July",
       "august": "August",
       "september": "September",
       "october": "October",
       "november": "November",
       "december": "December",
       "today": "Today"
     }
   }
}
{% endschema %}

{%- comment -%} External scripts {%- endcomment -%}
<script data-wbs-pro-external-js="{{ section_id }}" src="https://unpkg.com/[email protected]/dist/ical.es5.min.cjs" defer></script>
<script data-wbs-pro-external-js="{{ section_id }}" src="https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js" defer></script>
{%- comment -%} JavaScript {%- endcomment -%}
<script type="application/json" data-wbs-pro="{{ section_id }}">
  {
    "id": {{ section_id | json }},
    "timezone": {{ section.settings.timezone | json }},
    "show_timezone": {{ section.settings.show_timezone | json }},
    "ical_url": {{ section.settings.ical_url | json }},
    "ical_event_color": {{ section.settings.ical_event_color | json }},
    "ical_event_border_color": {{ section.settings.ical_event_border_color | json }},
    "ical_event_text_color": {{ section.settings.ical_event_text_color | json }},
    "months": {
      "0": {{ 'january' | t | json }},
      "1": {{ 'february' | t | json }},
      "2": {{ 'march' | t | json }},
      "3": {{ 'april' | t | json }},
      "4": {{ 'may' | t | json }},
      "5": {{ 'june' | t | json }},
      "6": {{ 'july' | t | json }},
      "7": {{ 'august' | t | json }},
      "8": {{ 'september' | t | json }},
      "9": {{ 'october' | t | json }},
      "10": {{ 'november' | t | json }},
      "11": {{ 'december' | t | json }}
    },
    "events": [
      {% for block in section.blocks %}
        {
          "id": {{ forloop.index }},
          "title": {{ block.settings.title | json }},
          "description": {{ block.settings.description | json }},
          "start_date": {{ block.settings.start_date | json }},
          "start_time": {{ block.settings.start_time | json }},
          "end_date": {{ block.settings.end_date | json }},
          "end_time": {{ block.settings.end_time | json }},
          "all_day": {{ block.settings.all_day | json }},
          "recurring": {{ block.settings.recurring | json }},
          "frequency": {{ block.settings.frequency | json }},
          "repeat_until_date": {{ block.settings.repeat_until_date | json }},
          "event_color": {{ block.settings.event_color | json }},
          "event_border_color": {{ block.settings.event_border_color | json }},
          "event_text_color": {{ block.settings.event_text_color | json }}
        }{% unless forloop.last %},{% endunless %}
      {% endfor %}
    ]
  }
</script>

<script data-wbs-pro-js="{{ section_id }}" type="module" defer>
(function(){
  const wbspro = {
    settings: {},
    // --- Calendar utility methods ---
    formatMonthYear(date) {
      const month = date.getMonth();
      const year = date.getFullYear();
      return `${this.settings.months[month]} ${year}`;
    },
    formatTime(date) {
      return new Intl.DateTimeFormat('en-US', {
        hour: '2-digit',
        minute: '2-digit',
        timeZone: this.settings.timezone
      }).format(date);
    },
    formatDate(date) {
      return new Intl.DateTimeFormat('en-US', {
        month: 'long',
        day: 'numeric',
        year: 'numeric',
        timeZone: this.settings.timezone
      }).format(date);
    },
    formatShortDate(date) {
      return new Intl.DateTimeFormat('en-US', {
        month: 'short',
        day: 'numeric',
        timeZone: this.settings.timezone
      }).format(date);
    },
    formatWeekday(date) {
      return new Intl.DateTimeFormat('en-US', {
        weekday: 'short',
        timeZone: this.settings.timezone
      }).format(date);
    },
    getTimezoneAbbr() {
      const timezoneParts = new Intl.DateTimeFormat('en-US', {
        timeZoneName: 'short',
        timeZone: this.settings.timezone
      }).formatToParts(new Date());
      const timezone = timezoneParts.find(part => part.type === 'timeZoneName');
      return timezone ? timezone.value : '';
    },
    getFirstDayOfMonth(year, month) {
      return new Date(year, month, 1).getDay();
    },
    getLastDayOfMonth(year, month) {
      return new Date(year, month + 1, 0).getDate();
    },
    isToday(year, month, day) {
      const today = new Date();
      return year === today.getFullYear() && month === today.getMonth() && day === today.getDate();
    },

    load: function(section) {
      // DOM Elements (scoped to section)
      const calendarGrid = section.querySelector('[data-calendar="calendar-grid"]');
      const currentMonthEl = section.querySelector('[data-calendar="current-month"]');
      const prevBtn = section.querySelector('[data-calendar="prev-month"]');
      const nextBtn = section.querySelector('[data-calendar="next-month"]');
      const todayBtn = section.querySelector('[data-calendar="today-btn"]');
      const modal = section.querySelector('[data-calendar="event-modal"]');
      const modalTitle = section.querySelector('[data-calendar="modal-title"]');
      const modalDescription = section.querySelector('[data-calendar="modal-description"]');
      const modalDate = section.querySelector('[data-calendar="modal-date"]');
      const modalTime = section.querySelector('[data-calendar="modal-time"]');
      const closeModal = section.querySelector('.wbs-pro__close-modal');

      // Calendar settings
      const calendarTimezone = wbspro.settings.timezone;
      const showTimezone = wbspro.settings.show_timezone;
      const icalUrl = wbspro.settings.ical_url;

      // Current date
      let currentDate = new Date();
      const today = new Date();
      let currentMonth = currentDate.getMonth();
      let currentYear = currentDate.getFullYear();

      // --- Centralized Event Store ---
      let allEvents = [];

      // Helper: parse and expand manual block events
      function parseManualEvents(rangeStart, rangeEnd) {
        const DateTime = window.luxon.DateTime;
        const timezone = wbspro.settings.timezone;
        const expandedEvents = [];

        wbspro.settings.events.forEach(block => {
          if (!block.title) return; // Skip blocks without titles

          // Use today's date if start_date is blank
          const startDate = block.start_date || DateTime.now().setZone(timezone).toISODate();
          // For end_date, use the provided value only if it's not empty and not the schema default
          const endDate = (block.end_date && block.end_date.trim() && block.end_date !== "2025-04-11") ? block.end_date : startDate;

          const startTime = block.all_day ? '00:00:00' : (block.start_time || '00:00');
          const endTime = block.all_day ? '23:59:59' : (block.end_time || '23:59');

          const firstStart = DateTime.fromISO(`${startDate}T${startTime}`, { zone: timezone });
          const firstEnd = DateTime.fromISO(`${endDate}T${endTime}`, { zone: timezone });
          const duration = firstEnd.diff(firstStart);

          // If not recurring, just add the single event if it's in range
          if (!block.recurring) {
            if (firstEnd >= rangeStart && firstStart <= rangeEnd) {
              expandedEvents.push({
                id: `manual-${block.id}`,
                title: block.title,
                description: block.description,
                startDate: firstStart,
                endDate: firstEnd,
                all_day: block.all_day,
                event_color: block.event_color,
                event_border_color: block.event_border_color,
                event_text_color: block.event_text_color
              });
            }
            return;
          }

          // If recurring, expand all occurrences
          const until = block.repeat_until_date
            ? DateTime.fromISO(block.repeat_until_date, { zone: timezone }).endOf('day')
            : firstStart.plus({ years: 1 });

          let current = firstStart;
          while (current <= until) {
            const currentEnd = current.plus(duration);

            // Only add events that are within the visible range
            if (currentEnd >= rangeStart && current <= rangeEnd) {
              expandedEvents.push({
                id: `manual-${block.id}-${current.toISODate()}`,
                title: block.title,
                description: block.description,
                startDate: current,
                endDate: currentEnd,
                all_day: block.all_day,
                event_color: block.event_color,
                event_border_color: block.event_border_color,
                event_text_color: block.event_text_color
              });
            }

            // Stop expanding if we're past the visible range and the until date
            if (current > rangeEnd && current > until) break;

            if (block.frequency === 'weekly') {
              current = current.plus({ weeks: 1 });
            } else if (block.frequency === 'monthly') {
              current = current.plus({ months: 1 });
            } else {
              break;
            }
          }
        });
        return expandedEvents;
      }

      // Helper: parse iCal events using ical.js
      function parseIcalEvents(icsData, rangeStart, rangeEnd) {
        const DateTime = window.luxon.DateTime;
        const timezone = wbspro.settings.timezone;
        const jcalData = ICAL.parse(icsData);
        const comp = new ICAL.Component(jcalData);
        const vevents = comp.getAllSubcomponents('vevent');
        let expandedEvents = [];

        vevents.forEach(event => {
          const e = new ICAL.Event(event);

          if (e.isRecurring()) {
            const expand = e.iterator();
            let next;
            while ((next = expand.next())) {
              const occurrence = e.getOccurrenceDetails(next);
              const occStart = DateTime.fromJSDate(occurrence.startDate.toJSDate()).setZone(timezone);
              const occEnd = DateTime.fromJSDate(occurrence.endDate.toJSDate()).setZone(timezone);

              // Only include occurrences within the visible range
              if (occEnd < rangeStart) continue;
              if (occStart > rangeEnd) break;

              expandedEvents.push({
                id: e.uid,
                title: e.summary,
                description: e.description,
                startDate: occStart,
                endDate: occEnd,
                all_day: occurrence.startDate.isDate,
                event_color: wbspro.settings.ical_event_color,
                event_border_color: wbspro.settings.ical_event_border_color,
                event_text_color: wbspro.settings.ical_event_text_color
              });
            }
          } else {
            // Non-recurring event
            let start, end, all_day;
            if (e.startDate.isDate) {
              start = DateTime.fromISO(e.startDate.toString(), { zone: timezone });
              end = start.endOf('day');
              all_day = true;
            } else {
              start = DateTime.fromMillis(e.startDate.toJSDate().getTime(), { zone: 'utc' }).setZone(timezone);
              end = DateTime.fromMillis(e.endDate.toJSDate().getTime(), { zone: 'utc' }).setZone(timezone);
              all_day = false;
            }
            expandedEvents.push({
              id: e.uid,
              title: e.summary,
              description: e.description,
              startDate: start,
              endDate: end,
              all_day,
              event_color: wbspro.settings.ical_event_color,
              event_border_color: wbspro.settings.ical_event_border_color,
              event_text_color: wbspro.settings.ical_event_text_color
            });
          }
        });

        return expandedEvents;
      }

      // Main: Fetch, combine, and update the central event store
      async function fetchAndRenderEvents(year, month) {
        const DateTime = window.luxon.DateTime;
        const timezone = wbspro.settings.timezone;

        // Define the visible range for fetching/expanding
        const viewStart = DateTime.fromObject({ year, month: month + 1, day: 1 }, { zone: timezone });
        const rangeStart = viewStart.startOf('month').startOf('week').minus({ days: 1 });
        const rangeEnd = viewStart.endOf('month').endOf('week').plus({ days: 1 });

        // Get manual events
        const manualEvents = parseManualEvents(rangeStart, rangeEnd);

        // Get iCal events
        let icalEvents = [];
        if (icalUrl) {
          try {
            const proxiedUrl = `https://corsproxy.io/?${encodeURIComponent(icalUrl)}`;
            const response = await fetch(proxiedUrl);
            const icsData = await response.text();
            icalEvents = parseIcalEvents(icsData, rangeStart, rangeEnd);
          } catch (err) {
            console.error("iCal fetch failed", err);
          }
        }

        // Combine events and render the calendar
        allEvents = manualEvents.concat(icalEvents);
        renderCalendar();
      }

      // --- Core Calendar Rendering Logic ---
    function getEventsForDate(year, month, day) {
        const DateTime = window.luxon.DateTime;
        const timezone = wbspro.settings.timezone;
        const dayStart = DateTime.fromObject({ year, month: month + 1, day, hour: 0, minute: 0, second: 0 }, { zone: timezone });
        const dayEnd = dayStart.endOf('day');
        return allEvents.filter(event => {
          return event.startDate <= dayEnd && event.endDate > dayStart;
        });
      }

      function updateCurrentDateDisplay() {
        currentMonthEl.textContent = wbspro.formatMonthYear(new Date(currentYear, currentMonth));
      }

      function openEventModal(event) {
        modalTitle.textContent = event.title;
        modalDescription.innerHTML = event.description || '';

        let dateString;
        const start = event.startDate.toJSDate();
        const end = event.endDate.toJSDate();

        if (event.startDate.hasSame(event.endDate, 'day')) {
          dateString = wbspro.formatDate(start);
        } else {
          dateString = `${wbspro.formatDate(start)} - ${wbspro.formatDate(end)}`;
        }
        modalDate.textContent = dateString;

        let timeString;
        if (event.all_day) {
          timeString = 'All day';
        } else {
          const timezoneAbbr = showTimezone ? ` ${wbspro.getTimezoneAbbr()}` : '';
          timeString = `${wbspro.formatTime(start)} - ${wbspro.formatTime(end)}${timezoneAbbr}`;
        }
        modalTime.textContent = timeString;

        modal.style.display = 'flex';
      }

      function createDayElement(year, month, day, isOtherMonth) {
        if (month < 0) {
          month = 11;
          year--;
        } else if (month > 11) {
          month = 0;
          year++;
        }
        const dayEl = document.createElement('div');
        dayEl.className = 'wbs-pro__calendar-day';
        if (isOtherMonth) dayEl.classList.add('wbs-pro__other-month');
        if (wbspro.isToday(year, month, day)) dayEl.classList.add('wbs-pro__today');
        const dayNumber = document.createElement('div');
        dayNumber.className = 'wbs-pro__day-number';
        dayNumber.textContent = day;
        dayEl.appendChild(dayNumber);
        const eventContainer = document.createElement('div');
        eventContainer.className = 'wbs-pro__event-container';
        const dayEvents = getEventsForDate(year, month, day);
        dayEvents.forEach(event => {
          const eventEl = document.createElement('div');
          eventEl.className = `wbs-pro__event ${event.all_day ? 'wbs-pro__all-day' : 'wbs-pro__regular'}`;

          // Apply custom colors if available
          if (event.event_color) {
            eventEl.style.backgroundColor = event.event_color;
          }
          if (event.event_border_color) {
            eventEl.style.borderLeftColor = event.event_border_color;
          }
          if (event.event_text_color) {
            eventEl.style.color = event.event_text_color;
          }

          if (event.all_day) {
            eventEl.innerHTML = `<span class="wbs-pro__event-title">${event.title}</span>`;
          } else {
            const timeFormatted = event.startDate.toFormat('h:mm a');
            eventEl.innerHTML = `<span class="wbs-pro__event-time">${timeFormatted}</span> <span class="wbs-pro__event-title">${event.title}</span>`;
          }
          eventEl.dataset.eventId = event.id;
          eventEl.addEventListener('click', () => openEventModal(event));
          eventContainer.appendChild(eventEl);
        });
        dayEl.appendChild(eventContainer);
        return dayEl;
      }

    function renderMonthView() {
      updateCurrentDateDisplay();
      calendarGrid.innerHTML = '';
      calendarGrid.className = 'wbs-pro__calendar-grid';
        const firstDay = wbspro.getFirstDayOfMonth(currentYear, currentMonth);
        const lastDay = wbspro.getLastDayOfMonth(currentYear, currentMonth);
        const prevMonthLastDay = wbspro.getLastDayOfMonth(currentYear, currentMonth - 1);
      for (let i = firstDay - 1; i >= 0; i--) {
        const day = prevMonthLastDay - i;
        const dayEl = createDayElement(currentYear, currentMonth - 1, day, true);
        calendarGrid.appendChild(dayEl);
      }
      for (let day = 1; day <= lastDay; day++) {
        const dayEl = createDayElement(currentYear, currentMonth, day, false);
        calendarGrid.appendChild(dayEl);
      }
      const totalDaysShown = firstDay + lastDay;
      const rowsNeeded = Math.ceil(totalDaysShown / 7);
      if (rowsNeeded <= 5) {
        const remainingDays = 35 - (firstDay + lastDay);
        for (let day = 1; day <= remainingDays; day++) {
          const dayEl = createDayElement(currentYear, currentMonth + 1, day, true);
          calendarGrid.appendChild(dayEl);
        }
      }
    }

      function renderCalendar() {
        renderMonthView();
      }

      // --- Event Listeners (Setup once) ---
    closeModal.addEventListener('click', () => {
      modal.style.display = 'none';
    });
    window.addEventListener('click', (e) => {
      if (e.target === modal) {
        modal.style.display = 'none';
      }
    });
    prevBtn.addEventListener('click', () => {
      currentMonth--;
      if (currentMonth < 0) {
        currentMonth = 11;
        currentYear--;
      }
      fetchAndRenderEvents(currentYear, currentMonth);
    });
    nextBtn.addEventListener('click', () => {
      currentMonth++;
      if (currentMonth > 11) {
        currentMonth = 0;
        currentYear++;
      }
      fetchAndRenderEvents(currentYear, currentMonth);
    });
    todayBtn.addEventListener('click', () => {
      const today = new Date();
      currentMonth = today.getMonth();
      currentYear = today.getFullYear();
      fetchAndRenderEvents(currentYear, currentMonth);
    });

    // Initial fetch and render
    fetchAndRenderEvents(currentYear, currentMonth);
    },
    unload: function(section) {
      // Clean up if needed (remove event listeners, etc.)
    }
  };

    wbspro.settings = JSON.parse(document.querySelector('[data-wbs-pro="{{ section_id }}"]').innerHTML);
    // Fix timezone if null or invalid
if (!wbspro.settings.timezone || wbspro.settings.timezone === 'null') {
  wbspro.settings.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC';
}
    const sectionId = {{ section_id | json }};
    const section = document.querySelector(`#DP--${sectionId}`);
    if (sectionId == wbspro.settings.id) {
      wbspro.load(section);
    }
    window.addEventListener('shopify:section:unload', function (e) {
      const settings = document.querySelector(`[data-wbs-pro="${e.detail.sectionId}"]`);
      const sectionId = e.detail.sectionId;
      const section = document.querySelector(`#DP--${e.detail.sectionId}`);
      if (sectionId == wbspro.settings.id){
        wbspro.unload(section);
      }
    });
  })();
</script>
5/5 · 6 votes
Tags:

    Comments (1)

    Ilse

    2026-02-16

    Wow, amazing! Thank you so much for this. I was wondering: is there also a possibility to add a button in the event itself? So that people can click on the time/date of the event and get redirected to the page where they can sign up for that specific workshop? I’d love that! Thanks in advance, Ilse

    Background Pattern

    Want to work with us?

    Let’s build your website, grow your traffic, and automate your business with AI.

    Background Pattern