<main>
<header>
<slider min="1" max="4" step="1" label="Segments" v-model.number="segments"></slider>
<slider min="1" max="5" step="0.05" label="Size" v-model.number="size"></slider>
</header>
<section>
<div>
<h3>CSS</h3>
<div :style="'--fontsize: calc('+size+' * 1vmin)'">
<template v-for="n in segments">
<css-arrow :index="n-1" :segments="segments"></css-arrow>
</template>
</div>
<ul>
<li>template: simple</li>
<li>styles: more</li>
<li>resize: glitchy</li>
</ul>
</div>
<div class="vs">vs.</div>
<div>
<h3>SVG</h3>
<div :style="'--fontsize: calc('+size+' * 1vmin)'">
<template v-for="n in segments">
<svg-arrow :index="n-1" :segments="segments"></svg-arrow>
</template>
</div>
<ul>
<li>template: complex</li>
<li>styles: less</li>
<li>resize: buttery</li>
</ul>
</div>
</section>
</main>
:root {
--color: #08a6e6;
--accent: #9c63c7;
--fg: #573659;
--bg: #f0f2f0;
--fontsize: 2vmin; // example size, set appropriately
}
@media (orientation: portrait) {
:root {
--fontsize: 2vmax; // example size, set appropriately
}
}
.css-arrow {
position: relative;
display: flex;
align-items: center;
height: 1em;
padding: .2em 0;
font-size: calc(var(--fontsize) * 2);
transition: font-size 300ms linear;
span + span {
margin-left: .15em;
}
&__leg,
&__stop {
box-sizing: border-box;
position: relative;
display: block;
border-radius: 1em;
opacity: .4;
}
&__stop {
width: .75em;
height: .75em;
border: .2em solid var(--color);
+ .css-arrow__leg--last {
width: 1em;
}
}
&__leg {
width: .85em;
height: .2em;
background: var(--color);
&--last {
width: 3em;
&::before,
&::after {
content: '';
position: absolute;
right: 0;
background: var(--color);
width: .7em;
height: .2em;
border-radius: 1em;
}
&::before {
top: 50%;
transform: translate(0, -.15em) rotate(-45deg);
transform-origin: 100% 50%;
}
&::after {
top: 50%;
transform: translate(0, -.05em) rotate(45deg);
transform-origin: 100% 50%;
}
}
}
&--active {
opacity: 1;
}
}
.svg-arrow {
display: flex;
padding: .2em 0;
font-size: calc(var(--fontsize) * 2);
transition: font-size 300ms linear;
svg {
height: 1em;
opacity: .4;
&.svg-arrow--active {
opacity: 1;
}
> * {
stroke: var(--color);
stroke-width: 10;
stroke-linecap: round;
stroke-linejoin: round;
fill: transparent;
}
}
&--summary {
font-size: .7em;
svg {
opacity: 1;
> * {
stroke: var(--color);
}
}
}
}
// layout & stuff - has nothing to do with the arrows
body {
margin: 0;
min-height: 100vh;
display: flex;
font-family: sans-serif;
font-size: var(--fontsize);
background: var(--bg);
color: var(--fg);
}
main {
flex: 1;
display: flex;
flex-direction: column;
}
header {
display: flex;
justify-content: space-around;
padding: 2em;
font-size: calc(var(--fontsize) * 2);
text-align: center;
background: var(--accent);
color: #fff;
}
section {
flex: 1;
padding: 2em;
display: flex;
justify-content: space-around;
align-items: center;
> div {
padding: 2em;
}
}
ul, ol {
margin: 0;
padding: 0 0 0 2em;
}
li {
padding: .3em 0;
white-space: nowrap;
}
h3 {
border-bottom: .2em solid;
margin: 0 0 .5em 0;
}
h4 {
margin: 0;
}
.vs {
flex: none;
background: #fff;
border-radius: 50%;
}
input {
font-size: inherit;
}
// range slider
input[type="range"] {
appearance: none;
outline: none;
padding: 0;
border: 0;
width: 25vmin;
height: .25em;
border-radius: 1em;
cursor: pointer;
// MOZILLA
&::range-track {
appearance: none;
background: #fff;
outline: none;
}
&::focus-outer {
border: 0;
}
&::range-thumb {
appearance: none;
width: 1em;
height: 1em;
border: none;
box-shadow: 0 0 0 .2em var(--accent);
border-radius: 1em;
background: #fff;
transform: scale(.7);
transition: .3s ease-out;
}
&::range-thumb:focus,
&::range-thumb:active {
appearance: none;
transform: scale(1.5);
}
// BLINK/WEBKIT
&::slider-thumb {
appearance: none;
width: 1em;
height: 1em;
border: none;
box-shadow: 0 0 0 .2em var(--accent);
border-radius: 1em;
background: #fff;
transform: scale(.8);
transition: .3s ease-out;
}
&::slider-thumb:focus,
&::slider-thumb:active {
appearance: none;
transform: scale(1.1);
}
}
View Compiled
Vue.component('css-arrow', {
template: `
<span class="css-arrow">
<template v-for="n in segments">
<span class="css-arrow__leg" :class="segmentClasses(n)"></span>
<span class="css-arrow__stop" v-if="showStop(n)"></span>
</template>
</span>
`,
props: [
'index',
'segments'
],
methods: {
isActive: function(n){
return n-1 === this.index
},
isLast: function(n){
return n === this.segments
},
showStop: function(n) {
return n < this.segments
},
segmentClasses: function(n) {
let classes = []
if(this.isActive(n)) {
classes.push('css-arrow--active')
}
if(this.isLast(n)) {
classes.push('css-arrow__leg--last')
}
return classes.join(' ')
}
}
})
Vue.component('svg-arrow', {
template: `
<span class="svg-arrow">
<template v-if="segments == 1">
<svg viewBox="0 0 150 50" :class="segmentClasses(1)">
<line x1="5" y1="25" x2="140" y2="25"></line>
<polyline points="125 7 143 25 125 43"></polyline>
</svg>
</template>
<template v-else v-for="n in segments">
<svg viewBox="0 0 50 50" :class="segmentClasses(n)">
<line x1="7" y1="25" x2="43" y2="25"></line>
<polyline points="28 7 43 25 28 43" v-if="isLast(n)"></polyline>
</svg>
<svg viewBox="0 0 50 50" v-if="showStop(n)">
<circle cx="25" cy="25" r="16"></circle>
</svg>
</template>
</span>
`,
props: [
'index',
'segments'
],
methods: {
isActive: function(n){
return n-1 === this.index
},
isLast: function(n){
return n === this.segments
},
showStop: function(n) {
return n < this.segments
},
segmentClasses: function(n) {
if(this.isActive(n)) {
return 'svg-arrow--active'
}
}
}
})
Vue.component('slider', {
template: `
<h4>
{{label}} <br />
<input type="range" :min="min" :max="max" :step="step" :value="value"
@input="update($event.target.value)" />
</h4>
`,
props: [
'label',
'min',
'max',
'step',
'value'
],
methods: {
update: function (value) {
this.$emit('input', value)
}
}
})
let vm = new Vue({
el: 'main',
data: {
segments: 3,
size: 3
}
})
This Pen doesn't use any external CSS resources.