Recreation of https://dribbble.com/shots/3959132-Todo-List-Swipe-To-Check
Original built with JS but adapted to be CSS-only since JS wasn't necessary.
Recreation of https://dribbble.com/shots/3959132-Todo-List-Swipe-To-Check
Original built with JS but adapted to be CSS-only since JS wasn't necessary.
<svg viewBox="0 0 0 0" style="position: absolute; z-index: -1; opacity: 0;"> | |
<defs> | |
<linearGradient id="boxGradient" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="25" y2="25"> | |
<stop offset="0%" stop-color="#27FDC7"/> | |
<stop offset="100%" stop-color="#0FC0F5"/> | |
</linearGradient> | |
<linearGradient id="lineGradient"> | |
<stop offset="0%" stop-color="#0FC0F5"/> | |
<stop offset="100%" stop-color="#27FDC7"/> | |
</linearGradient> | |
<path id="todo__line" stroke="url(#lineGradient)" d="M21 12.3h168v0.1z"></path> | |
<path id="todo__box" stroke="url(#boxGradient)" d="M21 12.7v5c0 1.3-1 2.3-2.3 2.3H8.3C7 20 6 19 6 17.7V7.3C6 6 7 5 8.3 5h10.4C20 5 21 6 21 7.3v5.4"></path> | |
<path id="todo__check" stroke="url(#boxGradient)" d="M10 13l2 2 5-5"></path> | |
<circle id="todo__circle" cx="13.5" cy="12.5" r="10"></circle> | |
</defs> | |
</svg> | |
<div class="todo-list"> | |
<label class="todo"> | |
<input class="todo__state" type="checkbox" /> | |
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 25" class="todo__icon"> | |
<use xlink:href="#todo__line" class="todo__line"></use> | |
<use xlink:href="#todo__box" class="todo__box"></use> | |
<use xlink:href="#todo__check" class="todo__check"></use> | |
<use xlink:href="#todo__circle" class="todo__circle"></use> | |
</svg> | |
<div class="todo__text">Do a very important task</div> | |
</label> | |
<label class="todo"> | |
<input class="todo__state" type="checkbox" /> | |
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 25" class="todo__icon"> | |
<use xlink:href="#todo__line" class="todo__line"></use> | |
<use xlink:href="#todo__box" class="todo__box"></use> | |
<use xlink:href="#todo__check" class="todo__check"></use> | |
<use xlink:href="#todo__circle" class="todo__circle"></use> | |
</svg> | |
<div class="todo__text">Another important task</div> | |
</label> | |
<label class="todo"> | |
<input class="todo__state" type="checkbox" /> | |
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 200 25" class="todo__icon"> | |
<use xlink:href="#todo__line" class="todo__line"></use> | |
<use xlink:href="#todo__box" class="todo__box"></use> | |
<use xlink:href="#todo__check" class="todo__check"></use> | |
<use xlink:href="#todo__circle" class="todo__circle"></use> | |
</svg> | |
<div class="todo__text">Not so important task</div> | |
</label> | |
</div> |
console.clear(); | |
setTimeout(function(){ | |
document.querySelector('input[type="checkbox"]').setAttribute('checked',true); | |
},100); | |
/* | |
// Javascript was initially used, but wasn't really necessary. Javascript droppped, but left here for archival purposes. | |
var todoTemplate = function(id){ | |
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 25" class="todo__icon"> | |
<defs> | |
<mask id="myMask${id}" maskUnits="userSpaceOnUse" x="0" y="0"> | |
<g stroke="#FFF" fill="none"> | |
<path class="todo__line" d="M21 12.3h168"/> | |
<path class="todo__box" d="M21 12.7v5c0 1.3-1 2.3-2.3 2.3H8.3C7 20 6 19 6 17.7V7.3C6 6 7 5 8.3 5h10.4C20 5 21 6 21 7.3v5.4"/> | |
<path class="todo__check" d="M10 13l2 2 5-5"/> | |
</g> | |
</mask> | |
</defs> | |
<rect fill="url(#todoGradient)" mask="url(#myMask${id})" width="100%" height="100%" /> | |
<circle class="todo__circle" cx="13.5" cy="12.5" r="10" /> | |
</svg>` | |
}; | |
var todos = [ ...document.querySelectorAll('.todo') ].forEach(activateTodo); | |
function offsetPath(path, offset){ | |
var length = path.getTotalLength(); | |
path.style.transition = 'none'; | |
path.style.strokeDasharray = length + ' ' + length + offset; | |
path.style.strokeDashoffset = length; | |
setTimeout(function(){ path.style.transition = null; },20); | |
return length; | |
} | |
function togglePath(path, toggle, offset = 0){ | |
var length = offsetPath(path, offset); | |
function updatePath(toggle){ | |
return path.style.strokeDashoffset = ( toggle ? -offset : length ); | |
} | |
updatePath(toggle); | |
return updatePath; | |
} | |
function activateTodo(todo, i){ | |
todo.insertAdjacentHTML('afterbegin', todoTemplate(i)); | |
var line = todo.querySelector('.todo__line'), | |
updateLine = togglePath(line, false, 4); | |
var box = todo.querySelector('.todo__box'), | |
updateBox = togglePath(box, true); | |
var check = todo.querySelector('.todo__check'), | |
updateCheck = togglePath(check); | |
var toggle = false; | |
todo.addEventListener('click', function(){ | |
toggle = !toggle; | |
updateLine(toggle); | |
updateBox(!toggle); | |
updateCheck(toggle); | |
if ( todo.classList ) { todo.classList[ toggle ? 'add' : 'remove' ]('todo--checked'); } | |
}); | |
} | |
*/ |
<script src="https://codepen.io/shshaw/pen/epmrgO"></script> |
@duration: 0.8s; | |
.todo-list { | |
background: #FFF; | |
font-size: 20px; | |
max-width: 15em; | |
margin: auto; | |
padding: 0.5em 1em; | |
box-shadow: 0 5px 30px rgba(0, 0, 0, 0.2); | |
} | |
.todo { | |
display: block; | |
position: relative; | |
padding: 1em 1em 1em 16%; | |
margin: 0 auto; | |
cursor: pointer; | |
border-bottom: solid 1px #ddd; | |
&:last-child { border-bottom: none; } | |
} | |
.todo__state { | |
position: absolute; | |
top: 0; | |
left: 0; | |
opacity: 0; | |
} | |
.todo__text { | |
color: saturate(#1B4A4E,15%); | |
transition: all @duration/2 linear @duration/2; | |
} | |
.todo__icon { | |
position: absolute; | |
top: 0; | |
bottom: 0; | |
left: 0; | |
width: 100%; | |
height: auto; | |
margin: auto; | |
fill: none; | |
stroke: #27FDC7; | |
stroke-width: 2; | |
stroke-linejoin: round; | |
stroke-linecap: round; | |
} | |
.todo__line, | |
.todo__box, | |
.todo__check { | |
transition: stroke-dashoffset @duration cubic-bezier(.9,.0,.5,1); | |
} | |
.todo__circle { | |
stroke: #27FDC7; | |
stroke-dasharray: 1 6; | |
stroke-width: 0; | |
transform-origin: 13.5px 12.5px; | |
transform: scale(0.4) rotate(0deg); | |
animation: none @duration linear; //cubic-bezier(.08,.56,.04,.98); | |
@keyframes explode { | |
//0% { stroke-width: 0; transform: scale(0.5) rotate(0deg); } | |
30% { | |
stroke-width: 3; | |
stroke-opacity: 1; | |
transform: scale(0.8) rotate(40deg); | |
//animation-timing-function: cubic-bezier(.89,.01,.95,.51); | |
} | |
100% { | |
stroke-width: 0; | |
stroke-opacity: 0; | |
transform: scale(1.1) rotate(60deg); | |
//animation-timing-function: cubic-bezier(.08,.56,.04,.98); | |
} | |
} | |
} | |
.todo__box { | |
stroke-dasharray: 56.1053, 56.1053; stroke-dashoffset: 0; | |
transition-delay: @duration * 0.2; | |
} | |
.todo__check { | |
stroke: #27FDC7; | |
stroke-dasharray: 9.8995, 9.8995; stroke-dashoffset: 9.8995; | |
transition-duration: @duration * 0.4; | |
} | |
.todo__line { | |
stroke-dasharray: 168, 1684; | |
stroke-dashoffset: 168; | |
} | |
.todo__circle { | |
animation-delay: @duration * 0.7; | |
animation-duration: @duration * 0.7; | |
} | |
.todo__state:checked { | |
~ .todo__text { transition-delay: 0s; color: #5EBEC1; opacity: 0.6; } | |
~ .todo__icon .todo__box { stroke-dashoffset: 56.1053; transition-delay: 0s; } | |
~ .todo__icon .todo__line { stroke-dashoffset: -8; } | |
~ .todo__icon .todo__check { stroke-dashoffset: 0; transition-delay: @duration * 0.6; } | |
~ .todo__icon .todo__circle { animation-name: explode; } | |
} | |
html { background: #ddd; } | |
html { height: 100%; display: flex; } | |
body { width: 100%; margin: auto; } |