A landing page for a fictional travel agency, showcasing CSS skills with custom forms, navigation, and smooth animations.
This project adopts established methodologies for clean, scalable, and maintainable code, using:
-
SASS/SCSS for modular and maintainable styling
-
BEM (Block, Element, Modifier) for consistent naming conventions and readability
-
7-1 Architecture for organized, scalable SCSS file structure
Below are highlights of interactive animations and custom UI elements — all built with pure CSS!
Smooth entrance effects using @keyframes and the animation property to animate key elements like headings and buttons.
@keyframes moveInLeft {
0% {
opacity: 0;
transform: translateX(-10rem);
}
80% {
transform: translateX(1rem);
}
100% {
opacity: 1;
transform: translate(0);
}
}Cards flip on hover to reveal additional details using transform, perspective, and backface-visibility to create a 3D flipping effect.
.card {
perspective: 150rem; // For the flipping effects
position: relative;
height: 52rem;
// Card div -> both sides of the card
&__side {
// ...other styling omitted...
// Stack two cards together -> create two sides
position: absolute;
top: 0;
left: 0;
backface-visibility: hidden; // To hide the back part of the element
transition: all 0.6s ease;
// Front side
&--front {
// ...other styling omitted...
}
// Back side
&--back {
// Make back side already rotated at the begining
transform: rotateY(180deg);
// ...other styling omitted...
}
}
// When hover on the card container -> rotate both sides
&:hover &__side--front {
transform: rotateY(180deg);
}
&:hover &__side--back {
transform: rotateY(360deg);
}
}A lightweight popup modal triggered by the :target pseudo-class — no JavaScript required.
.popup {
// Black background
// ...
// Functionality -> hide and show the entire popup
opacity: 0;
visibility: hidden;
transition: all 0.2s;
&:target {
opacity: 1;
visibility: visible;
}
// Popup window -> the container
&__content {
// ...other styling omitted...
transform: scale(0.1);
opacity: 0;
transition: all 0.3s 0.15s; // Set a delay
}
&:target &__content {
transform: scale(1);
opacity: 1;
}
}Live validation and floating labels using pseudo-classes like :focus, :invalid, and :placeholder-shown. Custom radio buttons are styled with hidden inputs and the :checked selector.
.form {
// Overall styling
// ...
// Input styling
&__input {
// ...other styling omitted...
// Focus -> improve accessibility
&:focus {
outline: none;
border-bottom: 3px solid $color-primary;
box-shadow: 0 1rem 2rem rgba($color-black, 0.1);
// Invalid -> provide live feedback
&:invalid {
border-bottom: 3px solid $color-secondary-dark;
}
}
&::placeholder {
// ...other styling omitted...
}
}
// Label styling
&__label {
// ...other styling omitted...
transition: all 0.2s;
}
// Floating label effects
&__input:placeholder-shown + &__label {
// Hide the label -> placeholder not shown, label floats in
visibility: hidden;
opacity: 0;
transform: translateY(-4rem);
}
// Custom radio button
&__radio-group {
// ...other styling omitted...
}
// Hide the default input -> leverage the :checked selector
&__radio-input {
display: none;
}
&__radio-label {
// ...other styling omitted...
}
// Build customized radio botton
&__radio-button {
display: inline-block;
// Build the outline of the circle
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
border: 3.5px solid $color-primary;
position: absolute;
top: -0.3rem;
left: 0;
// Build the inner circle
&::after {
content: "";
display: block;
width: 1rem;
height: 1rem;
border-radius: 50%;
background-color: $color-primary;
opacity: 0; // Hide the circle -> only shown when checked
transition: opacity 0.15s;
// Center the circle
@include absolute-center;
}
}
// Checked effects -> using :checked and adjancent selector
&__radio-input:checked + &__radio-label &__radio-button::after {
opacity: 1;
}
}A floating button toggles a full-screen menu using a hidden checkbox and the :checked selector. Icon transitions are handled via ::before and ::after pseudo-elements.
.navigation {
// Hidden checkbox input -> leverage :checked selector
&__checkbox {
display: none;
}
// Button -> checkbox label
&__button {
// ...other styling omitted...
}
// Background image -> hidden behind the label
&__background {
// ...other styling omitted...
// Menu extended effects -> using hidden checkbox
transition: transform 0.7s cubic-bezier(0.83, 0, 0.17, 1);
}
// Menu container
&__nav {
// ...other styling omitted...
// Hide the menu until checkbox were checked
width: 0;
opacity: 0;
transition: all 0.7s cubic-bezier(0.68, -0.6, 0.32, 1.6);
}
// Menu -> unorder list
&__list {
// ...other styling omitted...
}
// Menu item -> list item
&__item {
// ...other styling omitted...
}
// Menu text -> anchor tag in the list item
&__link {
// Numerical marks -> simply used attribute selector to avoid complexity
span {
display: inline-block;
margin-right: 1.5rem;
}
// Text
&:link,
&:visited {
// ...other styling omitted...
// For hover effects
background-image: linear-gradient(
120deg,
transparent 0%,
transparent 50%,
$color-white 50%
);
background-size: 240%;
transition: all 0.3s;
}
&:hover,
&:active {
background-position: 100%;
color: $color-primary;
transform: translateX(1rem);
}
}
// Navigation functionality -> achieve by hidden checkbox
&__checkbox:checked ~ &__background {
transform: scale(80);
}
&__checkbox:checked ~ &__nav {
width: 100%;
opacity: 1;
}
// Menu icon -> span in the checkbox label
&__icon {
position: relative;
margin-top: 3.4rem;
&,
&::before,
&::after {
width: 3rem;
height: 2px;
border-radius: 10px;
background-color: $color-grey-dark-3;
display: inline-block;
transition: all 0.3s;
}
&::before,
&::after {
content: "";
position: absolute;
left: 0;
}
&::before {
top: -0.8rem;
}
&::after {
top: 0.8rem;
}
}
// Menu icon hover effects
&__button:hover &__icon {
transform: translateY(-0.1rem);
&::before {
top: -1rem;
}
&::after {
top: 1rem;
}
}
// Menu icon clicked effects -> using the hidden checkbox
&__checkbox:checked + &__button &__icon {
background-color: transparent;
&::before {
top: 0;
transform: rotate(
135deg
); // 180deg - 45deg -> to make the animation more dramatic
}
&::after {
top: 0;
transform: rotate(
-135deg
); // -180deg -(-45deg) -> to make the animation more dramatic
}
}
}This project is built under the guidance of Jonas Schmedtmann's Advanced CSS course.
All design credits go to Jonas Schmedtmann.




