0

I made a fancy custom checkbox that animates a gradient in/out when (un)checked. How can I make the checkbox border a gradient, as well? The border gradient should match the fill gradient just like the solid-colored fancy checkboxes.

I tried both the wrapping and pseudo-element methods described here: https://css-tricks.com/gradient-borders-in-css. However these methods:

  • Don't seem to work well with checkbox inputs (like integrating with the input focus outline.)
  • Interfere with the pseudo-element used to animate the checkbox gradient.

/*Based on: https://moderncss.dev/pure-css-custom-checkbox-style/ */

input[type='checkbox'] {
  appearance: none;
  background-color: #fff !important;

  display: inline-grid;
  place-content: center;

  color: purple;
  border-color: purple;
}

input[type='checkbox']::before {
  content: '';
  width: inherit;
  height: inherit;
  border-radius: inherit;

  transform: scale(0);
  transition: 500ms transform ease-in-out;

  background: purple;
}

input[type='checkbox']:checked::before {
  transform: scale(1);
  transition: 500ms transform ease-in-out;
  background: purple;
}

input[type='checkbox'].gradient-bg::before {
  background: linear-gradient(45deg, blue 20%, red 80%);
}
<div class=container>
  <input type="checkbox" class="gradient-bg" checked />
  <input type="checkbox" class="gradient-bg" />

  <input type="checkbox" checked />
  <input type="checkbox" />
</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">

2 Answers 2

2

Here is an idea using mask on a pseudo element. I kept only the relevant CSS for the gradient checkbox.

input[type='checkbox'] {
  display: inline-grid;
  width: 3em; /* size */
  border-radius: .5em; /* radius */
  aspect-ratio: 1;
  appearance: none;
}

input[type='checkbox']::before {
  content: '';
  border-radius: inherit;
  background: border-box linear-gradient(45deg, blue 20%, red 80%); /* gradient */
  border: .3em /* border width */ solid #0000;
  mask:
    conic-gradient(#000 0 0) 50%/var(--s,0% 0%) content-box no-repeat,
    conic-gradient(#000 0 0) padding-box exclude,
    conic-gradient(#000 0 0);
  transition: .3s;
}

input[type='checkbox']:checked::before {
  --s: 100% 100%;
}
input[type='checkbox']:focus {
  box-shadow: 0 0 .3em .2em lightblue;
}
<input type="checkbox" checked >
<input type="checkbox" >

Sign up to request clarification or add additional context in comments.

2 Comments

This seems to work fine, but is it 'legal' CSS to have a before pseudo element on an input element?
as long as you have appearance: none; many native elements (such as checkbox and radio) can be styled like common elements.
0

Adding the following code achieves the gradient border with a combination of transparent border, background-image, and background-clip:

input[type='checkbox'].gradient-bg {
  border: calc(var(--pico-border-radius) / 2) solid transparent;
  border-radius: var(--pico-border-radius);
  background-image: linear-gradient(white, white), linear-gradient(45deg, blue 20%, red 80%);
  background-origin: padding-box, border-box;
  background-clip: padding-box, border-box;
}

I originally didn't even try the border-image method because the article said "it’s just incompatible with border-radius, unfortunately." However, MDN docs for border-image has an example for rounded borders:

border-radius has no effect on the border image. This is because border-image-outset is able to place the image outside the border box, so it doesn't make sense for the border image to be clipped by the border area. To create rounded borders when using a border image, you should create the image itself with rounded corners, or, in the case of a gradient, draw it as the background instead...


This YouTube video explains this double background-image technique in more detail. This technique doesn't work if the content inside the gradient border needs to be semi-transparent. So the video goes on to extend the method with masks so it works with transparent backgrounds.

/*Based on: https://moderncss.dev/pure-css-custom-checkbox-style/ */

input[type='checkbox'] {
  appearance: none;
  background-color: #fff !important;

  display: inline-grid;
  place-content: center;

  color: purple;
  border-color: purple;
}

input[type='checkbox']::before {
  content: '';
  width: inherit;
  height: inherit;
  border-radius: inherit;

  transform: scale(0);
  transition: 500ms transform ease-in-out;

  background: purple;
}

input[type='checkbox']:checked::before {
  transform: scale(1);
  transition: 500ms transform ease-in-out;
  background: purple;
}

input[type='checkbox'].gradient-bg::before {
  background: linear-gradient(45deg, blue 20%, red 80%);
}

input[type='checkbox'].gradient-bg {
  border: calc(var(--pico-border-radius) / 2) solid transparent;
  border-radius: var(--pico-border-radius);
  background-image: linear-gradient(white, white), linear-gradient(45deg, blue 20%, red 80%);
  background-origin: padding-box, border-box;
  background-clip: padding-box, border-box;
}
<div class=container>
  <input type="checkbox" class="gradient-bg" checked />
  <input type="checkbox" class="gradient-bg" />

  <input type="checkbox" checked />
  <input type="checkbox" />
</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.