Insights

How to Make a Custom Linear Gradient Field for Sitecore Content Editor Utilizing Color Pickers

Enable Authors To Generate Custom Linear Gradient Backgrounds For Their Components

Unlocking Creative Potential With Custom CSS Gradients in Sitecore

Building a site and creating bold and feature rich components is something we all love to do. Sometimes I feel like the backend of Sitecore, like Content Editor, gets missed in the opportunity of what’s possible. It’s one of the reasons I wanted to see what else could be done when it comes to custom fields in Sitecore. When we build components, a lot of the time if there is a need for a graphic we offer the ability to insert an image. But what if we could offer clients something truly unique and enable them to build their own background using a nice CSS gradient.

Screenshot of Sitecore's interface showing the configuration of a LinearGradient custom field including assembly, class, and control settings.

Below you’ll see in action a custom field, made up of a number of other purely visual fields including multiple color pickers, and a range input, used to generate the CSS required to show a linear gradient. I also set it up so you could see what generated and thus know what to expect.

When the CSS is generated it is stored in essentially a Single-Line Text field that can be accessed from a front-end component.

Animated demonstration of a custom CSS gradient interface in Sitecore, showing color selection and degree adjustment.

Curious how to do it? Let’s break it down.

Create New Custom Field in Core Database

First thing to do is to create the new custom field in the core database. So, while in Content Editor, switch over to the core database and then navigate to /system/Field Types and create a new folder called Custom Types.

Next, navigate to /sitecore/system/Field types/Simple Types/Single-Line Text and clone that item to create a new Field Type in that folder you created, let’s call it LinearGradient.

Screenshot of a Sitecore customization interface for creating a CSS gradient background, showing color pickers and a degree slider.

Let’s fill in the fields on the right.

  • Assembly - this is the dll file where your field type class will be found
  • Class - the name of your class. In our case we called it LinearGradientField.
  • Control - this is how the field will be referenced via our configuration file.

Setup Sitecore Patch File

While in Visual Studio, create a Sitecore patch file with the following configuration. You will need to change it so your assembly and class file matches.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <controlSources>
      <source mode="on" namespace="XmCloudSXAStarter.Fields" assembly="XmCloudSXAStarter" prefix="custom" singleInstance="true" />       
    </controlSources>
    <fieldTypes>
      <fieldType name="LinearGradientField" type="XmCloudSXAStarter.Fields.LinearGradientField, XmCloudSXAStarter" />
    </fieldTypes>
  </sitecore>
</configuration>

Because all we’re saving for the field is essentially a string thus we will be able to base our field on the Single-Line Text field. The other input fields, like the color pickers and rotation selector are merely there for improved user experience.

Setup Class for Field

Let’s now create the .Next class file to render the field as we need. We’ll start with the following.

using System.Web.UI;

namespace XmCloudSXAStarter.Fields
{
    public class LinearGradientField : Sitecore.Shell.Applications.ContentEditor.Text
    {
        protected override void DoRender(HtmlTextWriter output)
        {
        }
    }
}

The important thing to note here is that we’re utilizing Sitecore.Shell.Applications.ContentEditor.Text as our base class. This is what Single-Line Text utilizes so essentially we’ll be able to use all the same functionality we just will alter the DoRender function to change the visual output.

Breaking Down the DoRender Function

So when it comes to the actual DoRender function it can be a bit crazy and there are certainly ways of improving it. Such as where CSS is pulled from, where JS might be pulled from, but I’ve kept it all here for simplicity.

Before I show the entire function, I’m just going to breakdown the individual bits. First let’s have a look at the HTML which is broken into a couple parts. We have our gradientdisplay which will be the visualizer for what the linear gradient looks like.

<div id=""gradientdisplay"" style=""width:100%;height:200px;border-radius:16px;margin-bottom:16px;background:linear-gradient(0deg, #fff, #000);""></div>";

Then we have all the fields that allow us to choose the colors and angle. And if you look at

<div id=""containerColor1"">
    <div id=""colorColumn1"">
        <label for=""colorPicker1"">Color #1:</label>
        <input type=""color"" id=""colorPicker1"" name=""color1"" value=""#ffffff"">
        <br/><label for=""selectedColor1"">Selected first color:</label>
        <input type=""text"" id=""selectedColor1"" name=""selectedColor1"" value=""#ffffff"" readonly>
    </div>
    <div id=""colorColumn2"">
        <label for=""colorPicker2"">Color #2:</label>
        <input type=""color"" id=""colorPicker2"" name=""color2"" value=""#000000"">
        <br/>                    
        <label for=""selectedColor2"">Selected second color:</label>
        <input type=""text"" id=""selectedColor2"" name=""selectedColor2"" value=""#000000"" readonly>
    </div> 
    <div class=""clockColumn"">
        <div class=""innerClockColumn"">
            <div class=""clock-container"">
                <div class=""clock-arm"" id=""clockArm""></div>
            </div>
            <div>
                <input type=""range"" min=""0"" max=""360"" value=""0"" class=""degree-input"" id=""degreeInput"" />
                <p style=""text-align: center;"">Selected Degree: <span id=""degreeDisplay"">0</span>°</p>
            </div>
        </div>
    </div>

</div>

And of course we need the field for which the resulting css that’s generated needs to be stored.

<div style=""padding-top:16px;"">
    <label for=""{this.ID}"">Chosen Gradient:</label>
    <input id=""{this.ID}"" class=""scContentControl"" type=""text"" value=""{Value}"" />
</div>

Next up is we have some CSS to help lay out the fields appropriately. Note that the double brackets are because all the code like the HTML, CSS and JavaScript are written within a verbatim interpolated string literal. And thus for formatting purposes we need to use double brackets otherwise it will be expecting a token variable.

<style>
  #containerColor1 {{
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
      gap: 16px;
  }}
  .innerClockColumn {{
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
      gap: 8px;
  }}
  .clock-container {{
      position: relative;
      width: 60px;
      height: 60px;
      border: 1px solid #000;
      border-radius: 50%;

      display: flex;
      justify-content: center;
      align-items: center;
  }}
  .clock-arm {{
      position: absolute;
      width: 50%;
      height: 2px;
      left: 0px;
      background-color: red;
      transform-origin: 100%;
      transform: rotate(0deg);
  }}
  .degree-input {{
      display: block;
      margin: 20px auto;
  }}
</style>

Lastly we have our JavaScript. There is one important piece to this. Due to the fact we’re saving essentially a Single-Line Text, each time we load an item that displays a LinearGradient field, we have to determine the colors and angle all from that single text line. Thus, we need to utilize Regex to identify the three values. This gets performed immediately after the form fields are displayed. Following that we have our event listeners for the both the color pickers as well as the input range field that captures the angle.

<script>
  var regex = /linear-gradient\((\d{{1,3}}deg),\s*(#[0-9a-fA-F]{{3,6}}),\s*(#[0-9a-fA-F]{{3,6}})\)/

  const degreeInput = document.getElementById('degreeInput');
  const clockArm = document.getElementById('clockArm');
  const degreeDisplay = document.getElementById('degreeDisplay');
  const colorpicker1 = document.getElementById('colorpicker1');
  const colorpicker2 = document.getElementById('colorpicker2');
  const selectedColor1 = document.getElementById('selectedColor1');
  const selectedColor2 = document.getElementById('selectedColor2');
  const gradientdisplay = document.getElementById('gradientdisplay');
  const lgString = document.getElementById('{this.ID}').value;
  var matches = lgString.match(regex);

  if (matches) {{
      var degree = matches[1];
      var color1 = matches[2];
      var color2 = matches[3];
      colorpicker1.value = color1;
      selectedColor1.value = color1;
      colorpicker2.value = color2;
      selectedColor2.value = color2;
      degree = degree.replace('deg', '');
      degreeInput.value = degree;
      clockArm.style.transform = `rotate(${{degree}}deg)`;
      gradientdisplay.style.background = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
  }}

  colorpicker1.addEventListener('input', function() {{
      selectedColor1.value = colorpicker1.value; 
      gradientdisplay.style.background = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
      document.getElementById('{this.ID}').value = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
  }});

  colorpicker2.addEventListener('input', function() {{
      selectedColor2.value = colorpicker2.value;
      gradientdisplay.style.background = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
      document.getElementById('{this.ID}').value = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
  }}); 

  degreeInput.addEventListener('input', function() {{
      const degree = degreeInput.value;
      clockArm.style.transform = `rotate(${{degree}}deg)`;
      degreeDisplay.textContent = degree;
      gradientdisplay.style.background = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
      document.getElementById('{this.ID}').value = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
   }});
</script>

Completed DoRender Function

protected override void DoRender(HtmlTextWriter output)
{
    this.ServerProperties["ID"] = this.ID;
    string loader = $@"<div id=""{this.ID}-linergradientfield"">";
    output.Write(loader);
    string gradient = $@"
    <div id=""gradientdisplay"" style=""width:100%;height:200px;border-radius:16px;margin-bottom:16px;background:linear-gradient(0deg, #fff, #000);""></div>";
    output.Write(gradient);

    string fields = $@"
    <div id=""containerColor1"">
        <div id=""colorColumn1"">
            <label for=""colorPicker1"">Color #1:</label>
            <input type=""color"" id=""colorPicker1"" name=""color1"" value=""#ffffff"">
            <br/><label for=""selectedColor1"">Selected first color:</label>
            <input type=""text"" id=""selectedColor1"" name=""selectedColor1"" value=""#ffffff"" readonly>
        </div>
        <div id=""colorColumn2"">
            <label for=""colorPicker2"">Color #2:</label>
            <input type=""color"" id=""colorPicker2"" name=""color2"" value=""#000000"">
            <br/>                    
            <label for=""selectedColor2"">Selected second color:</label>
            <input type=""text"" id=""selectedColor2"" name=""selectedColor2"" value=""#000000"" readonly>
        </div> 
        <div class=""clockColumn"">
            <div class=""innerClockColumn"">
                <div class=""clock-container"">
                    <div class=""clock-arm"" id=""clockArm""></div>
                </div>
                <div>
                    <input type=""range"" min=""0"" max=""360"" value=""0"" class=""degree-input"" id=""degreeInput"" />
                    <p style=""text-align: center;"">Selected Degree: <span id=""degreeDisplay"">0</span>°</p>
                </div>
            </div>
        </div>

    </div>
    <style>
        #containerColor1 {{
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            gap: 16px;
        }}
        .innerClockColumn {{
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            gap: 8px;
        }}
        .clock-container {{
            position: relative;
            width: 60px;
            height: 60px;
            border: 1px solid #000;
            border-radius: 50%;

            display: flex;
            justify-content: center;
            align-items: center;
        }}
        .clock-arm {{
            position: absolute;
            width: 50%;
            height: 2px;
            left: 0px;
            background-color: red;
            transform-origin: 100%;
            transform: rotate(0deg);
        }}
        .degree-input {{
            display: block;
            margin: 20px auto;
        }}
    </style>";
    output.Write(fields);
    string main = $@"<div style=""padding-top:16px;"">
        <label for=""{this.ID}"">Chosen Gradient:</label>
        <input id=""{this.ID}"" class=""scContentControl"" type=""text"" value=""{Value}"" />
        </div>
        <script>
            var regex = /linear-gradient\((\d{{1,3}}deg),\s*(#[0-9a-fA-F]{{3,6}}),\s*(#[0-9a-fA-F]{{3,6}})\)/

            const degreeInput = document.getElementById('degreeInput');
            const clockArm = document.getElementById('clockArm');
            const degreeDisplay = document.getElementById('degreeDisplay');
            const colorPicker1 = document.getElementById('colorPicker1');
            const colorPicker2 = document.getElementById('colorPicker2');
            const selectedColor1 = document.getElementById('selectedColor1');
            const selectedColor2 = document.getElementById('selectedColor2');
            const gradientdisplay = document.getElementById('gradientdisplay');
            const lgString = document.getElementById('{this.ID}').value;
            var matches = lgString?.match(regex);

            if (matches) {{
                var degree = matches[1];
                var color1 = matches[2];
                var color2 = matches[3];
                colorPicker1.value = color1;
                selectedColor1.value = color1;
                colorPicker2.value = color2;
                selectedColor2.value = color2;
                degree = degree.replace('deg', '');
                degreeInput.value = degree;
                degreeDisplay.textContent = degree;
                clockArm.style.transform = `rotate(${{degree}}deg)`;
                gradientdisplay.style.background = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
            }}

            colorPicker1.addEventListener('input', function() {{
                selectedColor1.value = colorPicker1.value; 
                gradientdisplay.style.background = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
                document.getElementById('{this.ID}').value = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
            }});

            colorPicker2.addEventListener('input', function() {{
                selectedColor2.value = colorPicker2.value;
                gradientdisplay.style.background = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
                document.getElementById('{this.ID}').value = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
            }}); 

            degreeInput.addEventListener('input', function() {{
                const degree = degreeInput.value;
                clockArm.style.transform = `rotate(${{degree}}deg)`;
                degreeDisplay.textContent = degree;
                gradientdisplay.style.background = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
                document.getElementById('{this.ID}').value = 'linear-gradient(' + degreeInput.value + 'deg,' + selectedColor1.value + ',' + selectedColor2.value + ')';
                }});
            </script>";

    output.Write(main);
    output.Write("</div>");
}

One you put it all together you can then add the LinearGradient field just like you do any other field to your component templates and you’ve now really given your content authors a powerful experience.



Meet David Austin

Development Team Lead | Sitecore Technology MVP x 3

📷🕹️👪

David is a decorated Development Team Lead with Sitecore Technology MVP and Coveo MVP awards, as well as Sitecore CDP & Personalize Certified. He's worked in IT for 25 years; everything ranging from Developer to Business Analyst to Group Lead helping manage everything from Intranet and Internet sites to facility management and application support. David is a dedicated family man who loves to spend time with his girls. He's also an avid photographer and loves to explore new places.

Connect with David