Skip to content

dot

Animated
typevaluesdefault
Number | String | ObjectAccepts size, color and other styles as Number, descriptive string "size [color]" or object {size [, backgroundColor, widht, borderRadius ...]}0

The dot property lets you define a point indicator at the end of the progress line. You have a lot of freedom to customize the dot using a Number, descriptive String, or an Object to inject any CSS styles.

Number: :dot="10" - specifies a round dot with 10px width and height and default #713dfd color.

Descriptive string: dot="size [color]" - size can be just a Number or a percent value like 5%. The calculation for percent values is similar to thickness and depends on the size. color is optional and lets you quickly define the color of the dot. The order of properties is important for parsing the String, and you can set the color only if the size is defined.

Object: :dot="{ size: Number | String [, any CSS inline style with Vue syntax] }" - to customize the point, you can define the prop as an Object. size is required and can be just a Number or a String to define a percent value. Only by defining the prop as an Object, you have the possibility to add any styles to the dot you want to, using Vue syntax for defining inline styles. You can even completely break the positioning of the dot if you need to. You cannot override the height of the dot since it is important for internal calculation and must be controllable.

Usage 📜

vue
<ve-progress :dot="10" />
<ve-progress dot="5% blue" />
<ve-progress :dot="{ size: 10, backgroundColor: 'red'}" />

Examples

Let's start with simple cases

vue
<ve-progress :dot="15" :progress="50" />
<ve-progress dot="8 lightgray" :progress="50" />
<ve-progress dot="2% lightgray" :progress="50" />

Here the dot becomes anything you wish

vue
<ve-progress
  :progress="50"
  line-mode="out"
  :empty-thickness="2"
  color="Navy"
  :dot="{
    size: 10,
    backgroundColor: 'transparent',
    border: '2px DEEPSKYBLUE solid',
  }"
/>
<ve-progress
  :progress="50"
  line-mode="out"
  :thickness="50"
  :empty-thickness="2"
  line="butt"
  color="Navy"
  :dot="{
    size: 50,
    backgroundColor: 'DarkCyan',
    width: '4px',
  }"
/>
<ve-progress
  :progress="50"
  line-mode="in"
  :thickness="70"
  :empty-thickness="2"
  line="butt"
  :color="{
    radial: true,
    colors: [
      {
        color: 'blue',
        offset: 0,
      },
      {
        color: 'blue',
        offset: 47,
      },
      {
        color: 'transparent',
        offset: 48,
      },
      {
        color: 'transparent',
        offset: 100,
      },
    ],
  }"
  :dot="{
    size: 70,
    backgroundColor: 'DarkCyan',
    width: '2px',
  }"
/>

The examples below show how to dig into the dot internals and customize it with CSS styles. We can access the dot element with the .ep-circle--progress__dot selector and style it like any other element.

Note that the examples are using the :deep() selector to target the dot element inside the scoped styles.

vue
<template>
  <ve-progress
    class="my-circle"
    :progress="50"
    line-mode="in"
    empty-color="transparent"
    :determinate="determinate"
    dot="10 gray"
  />
  <ve-progress
    class="my-circle2"
    :progress="50"
    line-mode="in"
    :thickness="70"
    :empty-thickness="2"
    line="butt"
    color="Navy"
    :dot="{
      size: 70,
      backgroundColor: 'DarkCyan',
      width: '1px',
    }"
  />
</template>
<style scoped lang="scss">
.my-circle {
  :deep(.ep-circle--progress__dot) {
    &:after {
      content: "";
      display: block;
      position: absolute;
      width: 15px;
      height: 15px;
      background-color: transparent;
      border: 2px gray solid;
      border-radius: 50%;
      right: -50%;
      top: -48%;
    }
  }
}
.my-circle2 {
  :deep(.ep-circle--progress__dot) {
    &:after {
      content: "";
      display: block;
      position: absolute;
      width: 1px;
      height: 70%;
      background-color: DarkCyan;
      bottom: 15%;
      left: 5px;
    }
    &:before {
      content: "";
      display: block;
      position: absolute;
      width: 1px;
      height: 70%;
      background-color: DarkCyan;
      border-radius: 50%;
      bottom: 15%;
      right: 5px;
    }
  }
}
</style>

Conic gradient

You asked for it,, years later we still miss the ability to define conic gradients for SVG elements. Fortunately, modern CSS provides tricks to achieve lines with conic gradients.

Under the hood, the dot is created with a regular HTML element, and therefore we can apply any CSS tricks to it too. Totally unexpected, we can create a conic gradient by (mis)using the dot property.

Below examples are based on the CodePen, all credits go to the author. I have adapted the code to demonstrate how good enough conic gradients can be integrated within this plugin. To let the magic happen, we make the dot to have the height and width of the whole circle, make it round with border-radius and mimic the empty and the progress lines with before and after pseudo-elements. To make the conic gradient to move along the progress value, we bind the progress to the gradient color offset with v-bind.

vue
<template>
  <ve-progress
    :progress="p"
    class="circle-1"
    color="transparent"
    empty-color="rgba(63, 121, 255, 0.2)"
    :dot="{
      size: '5%',
    }"
  />
  <ve-progress
    :progress="p"
    class="circle-2"
    color="transparent"
    empty-color="rgba(63, 121, 255, 0.2)"
    :dot="{
      size: '5%',
    }"
  />
  <ve-progress
    :progress="p"
    class="circle-3"
    :color="{
      colors: [
        {
          color: 'LightCoral',
          offset: '0',
        },
        {
          color: 'Gold',
          offset: '33',
        },
        {
          color: 'MediumSpringGreen',
          offset: '100',
        },
      ],
    }"
    :dot="{
      size: '5%',
    }"
  />
</template>
<script setup lang="ts">
import { computed, ref } from "vue";

const p = ref(50);
const progress = computed(() => {
  return `${p.value}%`;
});
</script>
<style scoped lang="scss">
// internal stuff, disables the dot rotation following the progress line
:deep(.ep-circle--progress__dot-container) {
  transform: none !important;
}
:deep(.ep-circle--progress__dot) {
  transition: 0s !important;
  // we make the dot as big as the circle itself
  width: 200px !important;
  height: 200px !important;
  border-radius: 100% !important;
  background-color: transparent !important;
  border: 0 solid transparent;
  // some CSS magic to apply a conic gradient, creating a progress line
  &:after,
  &:before {
    transition: 0.3s;
    --startColor: rgba(63, 121, 255, 0.1);
    --endColor: #3f79ff;
    --from: -0deg;
    // we bind the computed progress value to reactively update the gradient
    --progress: v-bind(progress);
    content: "";
    border-radius: 100%;
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    background: conic-gradient(
      from var(--from),
      var(--startColor),
      var(--endColor) var(--progress),
      transparent var(--progress)
    );
    mask: radial-gradient(
      farthest-side,
      transparent calc(100% - var(--thickness)),
      #fff calc(100% - var(--thickness) + 1px)
    );
  }
  &:before {
    opacity: 0;
  }
}
.circle-1 {
  :deep(.ep-circle--progress__dot) {
    &:after {
      --thickness: 80px;
    }
  }
}
.circle-2 {
  :deep(.ep-circle--progress__dot) {
    &:after {
      --startColor: rgba(63, 121, 255, 0);
      --thickness: 10px;
    }
  }
}
.circle-3 {
  // circle-3 has a more complex hack. :after plays the empty gray line covering the circle progress - :before
  // as the progress goes up, the :before line is revealed, creating a progress line with a gradient
  :deep(.ep-circle--progress__dot) {
    &:after {
      --startColor: transparent;
      --endColor: #e6e9f0;
      --thickness: 10px;
      background: conic-gradient(
        from var(--from),
        var(--startColor),
        var(--startColor) var(--progress),
        var(--endColor) var(--progress),
        var(--endColor) 100%,
        transparent var(--progress)
      );
      mask: radial-gradient(
        farthest-side,
        transparent calc(100% - var(--thickness)),
        #fff calc(100% - var(--thickness) + 1px)
      );
    }
    &:before {
      opacity: 1;
      --progress: v-bind(progress);
      --startColor: LightCoral;
      --middleCollor: Gold;
      --endColor: MediumSpringGreen;
      --thickness: 10px;
      background: conic-gradient(
        from var(--from),
        var(--startColor) 33%,
        var(--middleCollor) 50%,
        var(--endColor) 100%,
        transparent var(--progress)
      );
      mask: radial-gradient(
        farthest-side,
        transparent calc(100% - var(--thickness)),
        #fff calc(100% - var(--thickness) + 1px)
      );
    }
  }
}
</style>

By far, this is not the most perfect conic gradient solution, it loses some smoothness in contrast to other circle parts, and you have to hack into the plugin internals.

Actually, you can achieve the same effect without the plugin. On the other hand, it demonstrates the plugin's flexibility, and you still can profit from other features. Take this as a workaround and an inspiration for your experiments.

Released under the MIT License.