Add custom border shapes

We put together these tutorials to help developers build off of Whisk and are happy to answer any questions related to their implementation, but we cannot be responsible for troubleshooting or fixing any issues that come up through custom code modifications made following these instructions. Always make any custom code edits off of a duplicate/draft version of the theme.

The borders and image shapes are generated using SVG clip paths. When combined with CSS, it will crop the image inside of the SVG path shape. Here are some good articles about this technique in general:

The techniques we use in the theme are ones that we cross browser tested to display properly based on Shopify's browser support requirements for themes that are for sale in their theme store as of November 2022 when this theme originally debuted. We also chose to support Safari 13 and earlier as we had customers reach out to us for support for this browser. There may be other techniques that are mentioned in the linked articles that will work that we didn't choose to utilize because of previous issues with browser support. If you decide to try them and have success, let us know!

Adding a custom border shape for a banner section

A banner section is a section that contains a full width image or background video, like the "Image banner", "Collection banner", or "Background video".

1. Create the banner shape as a square

We'll need to create an SVG that is a square, as if everything inside of the square will be the image or video. The size ratio of the SVG doesn't matter, the image or video inside of it will determine the size ratio, so the SVG design will need to be something that looks good at all size ratios. Here's an example of what our SVG looks like in Illustrator for the Bottom Even Waves border style.

And here's what that shape looks like applied to an Image banner section on desktop...

...and on mobile.

We like to make the SVG as 100px x 100px, so we can easily do math around the coordinates. In order to keep the text boxes positioning inside the shape, we don't allow our border shape to go deeper than 10px on any side. Below is a screenshot from Illustrator where we have guides in place.


Once we have our shape, we'll convert it to an SVG clip path and add those coordinates in another file in the Whisk theme, "Snippets/svg-clip-paths.liquid". Here is how you can do that.

Make sure that your SVG shape is one path. The path coordinates are the contents the d="" attribute on the path element. Here's an example of the SVG code for the Bottom Even Waves SVG.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <path d="m100,100s-23-10-39-10-29,5-41,5C8,95,0,90,0,90V0s100,0,100,0v100Z"/>
</svg>

If you don't have an vector editing program like Adobe Illustrator that can edit and create SVGs, here's a link to a website where you can draw your own and grab the path coordinates: https://yqnn.github.io/svg-path-editor/

There's one important extra step to this technique — in order to use a path in CSS as a clip-path, we have to convert the path coordinates to a relative path within a viewbox of 0 0 1 1. Fortunately, there is a life saving website that converts SVG paths to CSS clip path ready coordinates so we don't have to do any math! đŸ˜… https://yoksel.github.io/relative-clip-path/

Using the above example, we'll paste the value from the d="" attribute into the first input field, "Insert clip-path coordinates for userSpaceOnUse" and the website will generate the updated path for us with a preview. Copy the coordinates that are generated in the second input titled "Take clip-path coordinates for objectBoundingBox".

We'll take these coordinates, and add them to the file "Snippets/svg-clip-paths.liquid". Create a new clipPath element inside of the <defs> element inside of the <svg>. Here's what our clipPath element for the Bottom Even Waves border style looks like:

<clipPath id="solid-even-waves-bottom-1" clipPathUnits="objectBoundingBox">
  <path d="M1,1 S0.77,0.091,0.61,0.091 S0.32,0.545,0.2,0.545 A0.407,3,0,0,1,0,0.091 V0 H1"/>
</clipPath>

The "id" will be the name of the SVG when it is referenced in the CSS, so it needs to be something unique that isn't used anywhere else. The clipPath also needs to have this attribute and value...

clipPathUnits="objectBoundingBox"

...in order to render properly.

Now we can add our unique element class to the "Assets/styles-utilities-element-object.css" file We'll add a url value with the id of the clipPath we just created. We include the "-webkit-clip-path" vendor prefixed version to support Safari versions 13 and older, and Samsung browsers. The vendor prefixed version should always be written above the regular version, so that the regular version can override it when supported. The class name should always start with "element—". The naming pattern we have given all banner shapes is to start with "edge-",

.element--edge-even-waves-bottom-1 {
  -webkit-clip-path: url(#even-waves-bottom-1);
  clip-path: url(#even-waves-bottom-1);
}

2. Use the banner shape in the theme

To use the shape in the theme, you need to add the shape name as an option in the schema portion in section files that offer an border shape option. Those are called "edge_style". The following example is from the "Image banner" section.

You'll want to add the shape as a new "value" with a "label". "Value" is the shape name from the CSS class name, everything after the double dashes (capitalization matters, so we like to keep things all lowercase to make it simple), and "label" is what the shape name will look like in the dropdown selector in the theme editor. The "group" helps keep the options organized in the theme editor, but it's not mandatory to have. You can reuse the values that are already there. The labels that are currently in the file are all locale translations, but if you aren't familiar with locales, you can just give your shape a name straight in this file. Below, I added a new shape titled "Top up angle alternate".

{
  "type": "select",
  "id": "edge_style",
  "options": [
   {
     "value": "edge-angle-top-up-alternate",
     "label": "Top up angle alternate",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-angle-top-up",
     "label": "t:borders.settings.border_style.option_angled_up_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-angle-top-down",
     "label": "t:borders.settings.border_style.option_angled_down_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-waves-angle-top-up",
     "label": "t:borders.settings.border_style.option_waves_angled_up_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-waves-angle-top-down",
     "label": "t:borders.settings.border_style.option_waves_angled_down_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-even-waves-top-1",
     "label": "t:borders.settings.border_style.option_even_waves_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-even-waves-top-1-reverse",
     "label": "t:borders.settings.border_style.option_even_waves_reverse_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-uneven-waves-top-1",
     "label": "t:borders.settings.border_style.option_uneven_waves_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-uneven-waves-top-1-reverse",
     "label": "t:borders.settings.border_style.option_uneven_waves_reverse_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-angle-bottom-up",
     "label": "t:borders.settings.border_style.option_angled_up_bottom",
    "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-angle-bottom-down",
     "label": "t:borders.settings.border_style.option_angled_down_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-waves-angle-bottom-up",
     "label": "t:borders.settings.border_style.option_waves_angled_up_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-waves-angle-bottom-down",
     "label": "t:borders.settings.border_style.option_waves_angled_down_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-even-waves-bottom-1",
     "label": "t:borders.settings.border_style.option_even_waves_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-even-waves-bottom-1-reverse",
     "label": "t:borders.settings.border_style.option_even_waves_reverse_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-uneven-waves-bottom-1",
     "label": "t:borders.settings.border_style.option_uneven_waves_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-uneven-waves-bottom-1-reverse",
     "label": "t:borders.settings.border_style.option_uneven_waves_reverse_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-center-point-bottom",
     "label": "t:borders.settings.border_style.option_center_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-center-pinch-bottom",
     "label": "t:borders.settings.border_style.option_pinch_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "none",
     "label": "t:borders.settings.border_style.option_none"
   }
  ],
 "default": "edge-angle-bottom-down",
 "label": "t:borders.settings.border_style.label"
},

You can learn more about Shopify's schema architecture in their Section schema documentation.

Adding a custom border shape for a section with a solid color scheme background

The technique is a little different to add a border shape to sections that have a full color background. The sections this technique applies to are: Multicolumn, Newsletter, Rich text, Icon list, Logo list, Contact form, Footer (note: only top shapes for the footer).

1. Create the shape that is 11px tall by 100px wide

We'll need to create an SVG with the above dimensions. Unlike the banner border shapes, these SVGs will attach themselves to the top or the bottom of the section. They don't need to be big enough to contain anything inside of them. The width of the browser window will determine the size ratio that the SVG renders at, so the SVG design will need to be something that looks good at all size ratios. Here's an example of what our SVG looks like in Illustrator for the Bottom Even Waves border style. This is in the same file that we created our banner border shape in, so you can visualize the difference.

And here's what that shape looks like applied to a Rich text section on desktop...

...and on mobile.

Once we have our shape, we'll convert it to an SVG clip path and add those coordinates in another file in the Whisk theme, "Snippets/svg-clip-paths.liquid". Here is how you can do that.

Make sure that your SVG shape is one path. The path coordinates are the contents the d="" attribute on the path element. Here's an example of the SVG code for the Bottom Even Waves SVG.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 11">
  <path d="m100,11S77,1,61,1s-29,5-41,5C8,6,0,1,0,1V0s100,0,100,0v11Z"/>
</svg>

If you don't have an vector editing program like Adobe Illustrator that can edit and create SVGs, here's a link to a website where you can draw your own and grab the path coordinates: https://yqnn.github.io/svg-path-editor/

There's one important extra step to this technique — in order to use a path in CSS as a clip-path, we have to convert the path coordinates to a relative path within a viewbox of 0 0 1 1. Fortunately, there is a life saving website that converts SVG paths to CSS clip path ready coordinates so we don't have to do any math! đŸ˜… https://yoksel.github.io/relative-clip-path/

Using the above example, we'll paste the value from the d="" attribute into the first input field, "Insert clip-path coordinates for userSpaceOnUse" and the website will generate the updated path for us with a preview. Copy the coordinates that are generated in the second input titled "Take clip-path coordinates for objectBoundingBox".

I know this looks a little crazy so tall, but don't worry. We'll add a height to it in CSS so it renders closer to our SVG.

We'll take these coordinates, and add them to the file "Snippets/svg-clip-paths.liquid". Create a new clipPath element inside of the <defs> element inside of the <svg>. Here's what our clipPath element for the Bottom Even Waves border style looks like:

<clipPath id="solid-even-waves-bottom-1" clipPathUnits="objectBoundingBox">
  <path d="m1,1 S0.77,0.091,0.61,0.091 s-0.29,0.455,-0.41,0.455 C0.08,0.545,0,0.091,0,0.091 V0 s1,0,1,0 v1"/>
</clipPath>

The "id" will be the name of the SVG when it is referenced in the CSS, so it needs to be something unique that isn't used anywhere else. The clipPath also needs to have this attribute and value...

clipPathUnits="objectBoundingBox"

...in order to render properly.

Now we can add our unique element class to the "Assets/styles-utilities-element-object.css" file We'll add a url value with the id of the clipPath we just created. We include the "-webkit-clip-path" vendor prefixed version to support Safari versions 13 and older, and Samsung browsers. The vendor prefixed version should always be written above the regular version, so that the regular version can override it when supported. The class name should always start with "element—". The naming pattern we have created for the theme is that for every solid border shape, we start the name with "edge-solid-" so that we can differentiate them from the banner shapes.

.element--edge-solid-even-waves-bottom-1 {
  -webkit-clip-path: url(#solid-even-waves-bottom-1);
  clip-path: url(#solid-even-waves-bottom-1);
  height: 9vh;
  width: 100%;
  position: relative;
  top: -1px;
  width: 100%;
}

If our border shape is on the top instead of the bottom, we can use this code with the positive value for "top". The positioning and the top declarations help make our shapes overlap the section they are a part of seamlessly, so there is never a small line between them.

.element--edge-solid-uneven-waves-top-1 {
  -webkit-clip-path: url(#solid-uneven-waves-top-1);
  clip-path: url(#solid-uneven-waves-top-1);
  height: 9vh;
  position: relative;
  top: 1px;
  width: 100%;
}

2. Use the shape in the theme

To use the shape in the theme, you need to add the shape name as an option in the schema portion in section files that offer an border shape option. Those are called "edge_style". The following example is from the "Image banner" section.

You'll want to add the shape as a new "value" with a "label". "Value" is the shape name from the CSS class name, everything after the double dashes (capitalization matters, so we like to keep things all lowercase to make it simple), and "label" is what the shape name will look like in the dropdown selector in the theme editor. The "group" helps keep the options organized in the theme editor, but it's not mandatory to have. You can reuse the values that are already there. The labels that are currently in the file are all locale translations, but if you aren't familiar with locales, you can just give your shape a name straight in this file. Below, I added a new shape titled "Top up angle alternate".

{
  "type": "select",
  "id": "edge_style",
  "options": [
   {
     "value": "edge-solid-angle-top-up-alternate",
     "label": "Top up angle alternate",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-angle-top-up",
     "label": "t:borders.settings.border_style.option_angled_up_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-angle-top-down",
     "label": "t:borders.settings.border_style.option_angled_down_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-waves-angle-top-up",
     "label": "t:borders.settings.border_style.option_waves_angled_up_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-waves-angle-top-down",
     "label": "t:borders.settings.border_style.option_waves_angled_down_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-even-waves-top-1",
     "label": "t:borders.settings.border_style.option_even_waves_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-even-waves-top-1-reverse",
     "label": "t:borders.settings.border_style.option_even_waves_reverse_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-uneven-waves-top-1",
     "label": "t:borders.settings.border_style.option_uneven_waves_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-uneven-waves-top-1-reverse",
     "label": "t:borders.settings.border_style.option_uneven_waves_reverse_top",
     "group": "t:borders.settings.border_style.group_top"
   },
   {
     "value": "edge-solid-angle-bottom-up",
     "label": "t:borders.settings.border_style.option_angled_up_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-angle-bottom-down",
     "label": "t:borders.settings.border_style.option_angled_down_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-waves-angle-bottom-up",
     "label": "t:borders.settings.border_style.option_waves_angled_up_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-waves-angle-bottom-down",
     "label": "t:borders.settings.border_style.option_waves_angled_down_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-even-waves-bottom-1",
     "label": "t:borders.settings.border_style.option_even_waves_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-even-waves-bottom-1-reverse",
     "label": "t:borders.settings.border_style.option_even_waves_reverse_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-uneven-waves-bottom-1",
     "label": "t:borders.settings.border_style.option_uneven_waves_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-uneven-waves-bottom-1-reverse",
     "label": "t:borders.settings.border_style.option_uneven_waves_reverse_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-center-point-bottom",
     "label": "t:borders.settings.border_style.option_center_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "edge-solid-center-pinch-bottom",
     "label": "t:borders.settings.border_style.option_pinch_bottom",
     "group": "t:borders.settings.border_style.group_bottom"
   },
   {
     "value": "none",
     "label": "t:borders.settings.border_style.option_none"
   }
 ],
 "default": "none",
 "label": "t:borders.settings.border_style.label"
},

You can learn more about Shopify's schema architecture in their Section schema documentation.

Still need help? Contact Us Contact Us