Search code examples
javascripthtmlcssreactjsborder

Making border-image work with gradients


I'm working on a webapp that uses react.js and sass for styles (so all my style files are .scss). I have a textbox with the current style:

input[type=text] {
  text-align: center;
  font: inherit;
  border: 6px solid #999999;
  padding: 5px 5px;
  font-size: 15px;
  box-shadow: 0 1px 1px #DDD;
  width: 223px;
  outline: none;
  display: block;
  color: #7B8585;
  margin: 0 auto 20px;
}

At some point, my app wants to change the border colour. This is what I have for that:

var borderStyle;
if (gradient) {
  borderStyle = {
    'borderImage': '-webkit-linear-gradient(left, #0083c5 0%, #0083c5 33%, #ec4a26 66%, #ec4a26 100%)',
  };
}

Later, the input component:

<input type="text" style={borderStyle} onChange={this.handleChange} />

Currently what I see is a white border with a tiny image of the red-blue gradient in each corner of the border. I've tried using borderColor, which doesn't work with gradients at all, apparently. Am I missing something obvious, or is it not possible to do a simple border gradient?

The desired result is a left-to-right gradient (so the left border is entirely blue, the right is entirely red, and the top and bottom borders feature the blue-to-red transition).


In response to Harry's answer, I changed to the following code:

if (gradient) {
  borderStyle = {
    borderImage: 'linear-gradient(to right, #0083c5 0%, #0083c5 33%, #ec4a26 66%, #ec4a26 100%)',
    borderImageSlice: 1
  };
}

as specified in the react docs for inline styles. However when I inspect the element, the borderImageSlice property I've defined is missing; only the borderImage one is there, and I still only have tiny gradients in the corners of the border.


Solution

  • You need to add a border-image-slice property also while applying the border. Doing this would give the exact output as you need.

    I have added it via CSS itself in the below snippet (without the JS) but you should be able to adapt it :)

    input[type=text] {
      text-align: center;
      font: inherit;
      border: 6px solid #999999;
      padding: 5px 5px;
      font-size: 15px;
      box-shadow: 0 1px 1px #DDD;
      width: 223px;
      outline: none;
      display: block;
      color: #7B8585;
      margin: 0 auto 20px;
      border-image: linear-gradient(to right, #0083c5 0%, #0083c5 33%, #ec4a26 66%, #ec4a26 100%);
      border-image-slice: 1;
    }
    <input type="text" />

    Note: I have also modified the gradient syntax to use the standard one so that it works in all browsers that support border-image property.


    Below is a snippet which applies the border image when the text in the input box is changed.

    var ip = document.getElementById("inp");
    
    ip.addEventListener("change", function() {
      this.style.borderImage = 'linear-gradient(to right, #0083c5 0%, #0083c5 33%, #ec4a26 66%, #ec4a26 100%)';
      this.style.borderImageSlice = '1';
    });
    input[type=text] {
      text-align: center;
      font: inherit;
      border: 6px solid #999999;
      padding: 5px 5px;
      font-size: 15px;
      box-shadow: 0 1px 1px #DDD;
      width: 223px;
      outline: none;
      display: block;
      color: #7B8585;
      margin: 0 auto 20px;
    }
    <input type="text" id="inp" />


    It seems like ReactJS by default adds px as units to all numbers that are passed for inline styles and because of this the border-image-slice: 1 is wrongly getting set as border-image-slice: 1px. As this property is a unitless property in CSS, it is not getting applied properly. The solution is to wrap this value within quotes and also add a semi-colon within the quotes (like in the below code sample):

    var borderStyle = {
        borderImage: 'linear-gradient(to right, #0083c5 0%, #0083c5 33%, #ec4a26 66%, #ec4a26 100%)',
          borderImageSlice: '1;' // note the quotes and the semi-colon.
      };
    

    Big credits for finding out this problem goes to Henrik Andersson.

    JSBin Demo with ReactJS