TikTok Tutorial #84 - How to create a CSS-only Calendar App Concept
Learn with us how to create an awesome CSS-only Calendar App Concept!
If you found us on TikTok on the following post, check out this article and copy-paste the full code!
Happy coding! 😻
@creative.tim ♬ Hurts Me (Alone At Prom) - Tory Lanez
Get your code ⬇️
1. HTML Code
<main>
<div class="device">
<header>
<div class="header-bar"></div>
<div class="header-title">
<h1>Calendar</h1>
</div>
<div class="header-weekdays">
<span>sun</span>
<span>mon</span>
<span>tue</span>
<span>wed</span>
<span>thu</span>
<span>fri</span>
<span>sat</span>
</div>
</header>
<section>
<h2 class="month">December</h2>
<form>
<input type="radio" name="calendar-label-bg" class="calendar-label-bg" id="vacation" />
<input type="radio" name="calendar-label-bg" class="calendar-label-bg" id="walk" />
<input type="radio" name="calendar-label-bg" class="calendar-label-bg" id="fishing" />
<input type="radio" name="calendar-label-bg" class="calendar-label-bg" id="weekend" />
<input type="radio" name="calendar-label-bg" id="new" class="calendar-label-bg">
<div class="calendar">
<input type="checkbox" name="" id="" class="day previous-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day current-month" />
<input type="checkbox" name="" id="" class="day next-month" />
<input type="checkbox" name="" id="" class="day next-month" />
<input type="checkbox" name="" id="" class="day next-month" />
<div class="calendar-labels">
<label for="vacation"><span>vacation</span></label>
<label for="walk"><span>walk</span></label>
<label for="fishing"><span>fishing</span></label>
<label for="weekend"><span>weekend</span></label>
<label for="new" class="full"><input type="text" placeholder="Add new task"></label>
<label class="reset">
<input type="reset" value="Reset"/>
</label>
</div>
</div>
</form>
<h2 class="month">January</h2>
<div class="calendar inactive">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
<input type="checkbox" name="" id="" class="day next-month">
</div>
</section>
</div>
</main>
<aside>
<div class="meta">
<h1>CSS-only Calendar App Concept</h1>
<p>
Creative Tim<br>
</p>
<p>
Click two dates and the labels to see the effect.<br>
Works <strike>best</strike> only in Chrome and Safari.
</p>
</div>
</aside>
2. CSS Code
@import "compass/css3";
@import url(https://fonts.googleapis.com/css?family=Lato:300,400,700);
$device-width: 264px * 1.5;
$device-height: 544px * 1.5;
$color-bg: #4075b7;
$color-app: white;
$color-header: #58b2a4;
$color-device: #dcdfe6;
$color-device-border: white;
$color-device-part: #a1a5b3;
$color-text-dark: #333;
$color-text-light: white;
$color-noncurrent-month: rgba($color-text-dark, 0.3);
$color-check: rgba(white, 0.7);
$color-vacation: #d3738b;
$color-fishing: #8f658a;
$color-walk: #7186db;
$color-weekend: #f7c187;
$color-new: $color-header;
$colors: (
vacation: $color-vacation,
fishing: $color-fishing,
walk: $color-walk,
weekend: $color-weekend,
new: $color-new
);
$day-height: 3rem;
$day-labels: (vacation, fishing, walk, weekend, new);
$animation-easing: cubic-bezier(0.77, 0, 0.175, 1);
$label-speed: 1s;
$day-speed: 0.6s;
$bg-speed: 1s;
$header-height: $day-height * 2;
@mixin range($min, $max) {
&:nth-child(n + #{$min}):nth-child(-n + #{$max}) {
@content;
}
}
@mixin clearfix {
&:before, &:after {
content: " ";
display: table;
}
&:after { clear: both; }
}
@function day-span($days) {
@return percentage($days / 7);
}
* {
box-sizing: border-box;
position: relative;
}
html, body {
font-family: Lato, sans-serif;
font-weight: 300;
line-height: 1;
background-color: $color-bg;
width: 100%;
height: 100%;
counter-reset: previous-month 29 current-month next-month;
margin: 0;
}
aside, main {
width: 50%;
height: $device-height;
float: left;
padding: 0 2rem;
margin: 2rem 0;
}
header {
color: $color-text-light;
}
.header-bar {
height: 1rem;
}
.header-title {
padding: 1rem;
h1 {
font-weight: 300;
font-size: 1rem;
margin: 0;
padding: 0;
text-align: center;
text-transform: uppercase;
letter-spacing: 3px;
}
}
.header-weekdays {
height: 2rem;
span {
font-size: 0.8rem;
display: block;
float: left;
width: percentage(1/7);
padding: 0.5rem 0;
text-align: center;
text-transform: uppercase;
}
}
.calendar {
@include clearfix();
&.inactive {
pointer-events: none;
}
}
form {
@include clearfix();
background: rgba($color-header, 0.9);
}
.month {
padding: 1rem;
font-size: 1rem;
color: $color-text-dark;
margin: 0;
}
.day {
float: left;
width: percentage(1/7);
text-align: center;
padding: 1rem 0;
margin: 0;
font-size: 1rem;
line-height: 1;
-webkit-appearance: none;
appearance: none;
outline: none;
transition-property: background, color;
transition-duration: 0.3s;
transition-timing-function: ease-in-out;
transition-delay: 0s;
.device:not(:hover) & { transition-duration: 0; }
&:after {
display: block;
transition-property: transform, opacity;
transition-duration: $day-speed;
transition-timing-function: $animation-easing;
}
&.current-month:after {
content: counter(current-month);
counter-increment: current-month;
}
&.next-month:after {
content: counter(next-month);
counter-increment: next-month;
}
&.previous-month:after {
content: counter(previous-month);
counter-increment: previous-month;
}
&.previous-month, &.next-month {
color: $color-noncurrent-month;
}
&:last-of-type:before {
content: '';
display: block;
position: absolute;
top: 0;
left: 100%;
width: 700%;
height: 100%;
background: $color-app;
}
&:checked {
&, & ~ .day {
color: $color-text-light;
}
& ~ .day {
background-color: transparent;
}
}
&:hover,
&:checked,
& ~ .day:hover,
& ~ .day:checked {
background-color: rgba(0, 0, 0, 0.1);
color: $color-text-light;
}
&, &:checked ~ .day:checked ~ .day,
&:checked ~ .day:hover ~ .day,
&:checked:hover ~ .day {
background-color: $color-app;
color: $color-text-dark;
&.next-month { color: $color-noncurrent-month; }
}
@for $day from 1 through 7 {
&:nth-child(7n + #{$day}):after {
transition-delay: (1/7) * ($day - 1) * $day-speed;
animation-delay: (1/7) * ($day - 1) * $day-speed;
}
}
}
.calendar-label-bg {
display: block;
position: absolute;
outline: none;
top: 0;
left: 0;
width: 100%;
height: 5 * $day-height;
margin: 0;
-webkit-appearance: none;
appearance: none;
transform: translateX(-100%);
animation: bg-inactive $bg-speed $animation-easing;
&:checked {
animation: bg-active $bg-speed $animation-easing forwards;
~ .calendar .day:checked {
&:after, & ~ .day:after {
animation-name: day-active;
animation-duration: $day-speed;
animation-timing-function: $animation-easing;
}
& ~ .day:checked ~ .day:after {
animation: none;
}
}
}
}
@each $label in $day-labels {
$label-color: map-get($colors, $label);
##{$label} { background-color: $label-color; }
##{$label}:checked {
~ .calendar {
@for $weekday from 1 through 7 {
.day {
&:nth-child(7n + #{$weekday}) {
z-index: (8 - $weekday) * 10;
}
}
}
.calendar-labels label[for=#{$label}] {
&:not(.full):before {
visibility: visible;
animation: check-active 0.1s 0.2s ease-out both;
}
&.full {
input { font-weight: 700; }
&:before {
color: $color-text-light;
border-color: $color-text-light;
}
}
> span {
transform: translateX(2rem);
}
}
}
}
}
.calendar-labels {
display: none;
width: 100%;
position: absolute;
top: 0;
z-index: 100;
pointer-events: none;
transition: all 0s ease-in-out;
transition-delay: $label-speed;
transform: translateY(0);
opacity: 0;
.device:hover & { display: block; }
&:before {
content: '';
display: block;
position: absolute;
bottom: 100%;
height: 5 * $day-height;
width: 100%;
pointer-events: auto;
}
> label {
display: block;
float: left;
width: day-span(4);
font-size: 1rem;
padding: 1rem;
color: $color-text-light;
font-weight: 700;
white-space: nowrap;
overflow: hidden;
&:before {
display: block;
position: absolute;
left: 0.75rem;
height: 1rem;
visibility: hidden;
}
&.full {
width: 100%;
font-weight: 700;
&:before {
transition-property: color, border-color;
transition-duration: 0.3s;
transition-timing-function: ease-in-out;
content: '+';
top: calc(50% - 0.75rem);
font-size: 1rem;
font-weight: 700;
width: 1rem;
border: 4px solid $color-check;
color: $color-check;
border-radius: 50%;
text-align: center;
visibility: visible;
}
> input {
display: block;
margin-left: 2rem;
width: calc(100% - 2rem);
font-size: 1rem;
background-image: none;
background-color: transparent;
outline: none;
border: none;
color: $color-text-light;
line-height: 1;
padding: 0;
&::placeholder {
color: $color-check;
}
}
}
&.reset {
width: 100%;
background: $color-app;
float: left;
padding: 0;
height: 3rem;
> input {
display: block;
padding: 1rem;
line-height: 1;
font-size: 0.8rem;
width: 100%;
height: 100%;
-webkit-appearance: none;
border: none;
background: transparent;
outline: none;
text-transform: uppercase;
color: $color-noncurrent-month;
letter-spacing: 3px;
}
}
&:not(.full):before {
content: '';
top: 50%;
width: 0.5rem;
transform: scaleX(-1) rotate(135deg);
transform-origin: left top;
border-right: 5px solid $color-check;
border-top: 5px solid $color-check;
animation: check-inactive 0.3s 0 $animation-easing both;
}
> * {
transition: transform 0.3s $animation-easing;
display: inline-block;
}
@each $label in $day-labels {
&[for="#{$label}"] {
background: map-get($colors, $label);
}
}
@include range(2, 3) {
width: day-span(3);
}
animation-duration: $label-speed;
animation-timing-function: $animation-easing;
animation-fill-mode: both;
&:nth-child(2n - 1) {
z-index: 20;
animation-name: leftLabel-inactive;
}
&:nth-child(2n) {
z-index: 10;
animation-name: rightLabel-inactive;
}
}
}
.day:checked ~ .day:checked {
~ .calendar-labels {
transition-delay: 0s;
display: block;
opacity: 1;
}
@for $week from 1 through 5 {
@include range(($week - 1) * 7 + 1, $week * 7) {
~ .calendar-labels {
transform: translateY($week * $day-height);
pointer-events: auto;
}
}
}
~ .day:nth-child(7n) ~ .day,
&:nth-child(7n) ~ .day {
&:after {
transform: translateY(5 * $day-height);
opacity: 0;
}
}
~ .calendar-labels {
label:nth-child(2n - 1) {
z-index: 20;
animation: leftLabel-active $label-speed $animation-easing both;
}
label:nth-child(2n) {
z-index: 10;
animation: rightLabel-active $label-speed $animation-easing both;
}
}
}
@keyframes check-active {
0% {
width: 0;
height: 0;
opacity: 0;
}
50% {
opacity: 1;
width: 0.5rem;
height: 0;
}
100% {
opacity: 1;
}
}
@keyframes check-inactive {
50% {
opacity: 1;
width: 0.5rem;
height: 0;
}
100% {
width: 0;
height: 0;
opacity: 0;
}
}
@keyframes labels-inactive {
to {
transform: translateY(0);
}
}
@keyframes leftLabel-active {
from {
transform: translateX(-100%);
}
}
@keyframes leftLabel-inactive {
to {
transform: translateX(-100%);
}
}
@keyframes rightLabel-active {
from {
transform: translateX(-300%);
}
}
@keyframes rightLabel-inactive {
to {
transform: translateX(-300%);
}
}
@keyframes bg-active {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes bg-inactive {
from {
transform: translateX(0);
}
to {
transform: translateX(100%);
}
}
@keyframes day-active {
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
.meta {
top: 50%;
transform: translateY(-50%);
font-size: 1.2rem;
p, a { color: rgba(255, 255, 255, 0.5); }
h1 {
font-size: 3rem;
line-height: 1.2;
font-weight: 300;
color: $color-text-light;
}
p { line-height: 1.4; }
a:hover {
color: rgba(255, 255, 255, 0.7);
}
}
.device {
position: absolute;
right: 2rem;
height: $device-height;
width: $device-width;
padding: 90px 10px;
border: 5px solid $color-device-border;
border-radius: 60px;
background-color: $color-device;
box-shadow: 0 0 50px 10px rgba(0,0,0,0.1);
&:before, &:after {
content: '';
position: absolute;
z-index: 2;
}
&:before {
width: 20%;
height: 10px;
top: 40px;
left: 40%;
border-radius: 10px;
background-color: $color-device-part;
}
&:after {
width: 50px;
height: 50px;
border-radius: 50%;
border: solid 5px lighten($color-device-part, 10%);
left: calc(50% - 25px);
bottom: 15px;
}
header {
height: $header-height;
background-color: $color-header;
}
section {
height: calc(100% - #{$header-height});
width: 100%;
overflow: hidden;
background-color: $color-app;
}
}
I hope you did find this tutorial useful!
For more web development or UI/UX design tutorials, follow us on:
Other useful resources: