DEV Community

Cover image for How I put clouds in my portfolio and why?
Akash Ghosh
Akash Ghosh

Posted on

How I put clouds in my portfolio and why?

Well, I made my portfolio and there is one feature I am proud of.

I did not plan on it initially, but there arose the reason. I thought of putting something novel that is unique to my portfolio. I pondered over the thought and then it struck, I could put my name in my portfolio and then when clicked, I could show it's meaning and that's when it hit me that I could put a cloud animation.

I chalked out the basic plan, I needed few transparent cloud images to begin with, looked up the internet, found the best ones in https://pngtree.com/so/cloud. Got 5 cloud images from there.

Now let's discuss the system, what I needed was when clicked on the CTA, I need the clouds to appear from either side and keep on moving horizontally. And I needed the full window to be used for this animation, and the clouds will appear over the existing content.

So three requirements:

  1. Clouds should move from either direction.
  2. Clouds should move from either side of the screen.
  3. The viewport should the the window for the clouds.

This was the viewport stack.

/* Z-Index Structure:
   - Original background: z-index 0 (default)
   - Sky background: z-index 10
   - Component tiles: z-index 20
   - Clouds: z-index 30
   - Navbar: z-index 40 (to stay on top)
*/

Enter fullscreen mode Exit fullscreen mode

Starting with the container this was the container styles.

.cloud-container {
  position: absolute;
  z-index: 30;
  pointer-events: none;
  width: 100%;
  white-space: nowrap;
}

Enter fullscreen mode Exit fullscreen mode

Then the clouds animations, or how it will appear on the screen.

/* Continuous marquee animations for clouds */
@keyframes marqueeLeftToRight {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100vw); }
}

@keyframes marqueeRightToLeft {
  0% { transform: translateX(100vw); }
  100% { transform: translateX(-100%); }
}

Enter fullscreen mode Exit fullscreen mode
@keyframes marqueeLeftToRight {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100vw); }
}

Enter fullscreen mode Exit fullscreen mode

But as it was working it was time to add a few more features.
I had dark/light mode for my website I wanted the clouds background to change with the theme as well, and also add a daytime feature on it. So from dark/light it now has dark/midday/light themes.

The styles for the clouds are as follows.

/* Cloud positions and animations - continuous marquee effect */
.cloud1 {
  top: 0%;
  animation: marqueeRightToLeft 40s linear infinite;
}

.cloud2 {
  top: 15%;
  transform: scale(0.6);
  opacity: 0.9;
  animation: marqueeLeftToRight 45s linear infinite;
}

.cloud3 {
  top: 2%;
  transform: scale(0.8);
  opacity: 0.8;
  animation: marqueeRightToLeft 50s linear infinite;
}

.cloud4 {
  top: 18%;
  transform: scale(0.75);
  opacity: 0.75;
  animation: marqueeLeftToRight 55s linear infinite;
}

.cloud5 {
  top: 20%;
  transform: scale(0.7);
  opacity: 0.8;
  animation: marqueeRightToLeft 60s linear infinite;
}

Enter fullscreen mode Exit fullscreen mode

and the themes

.cloud-img {
  width: 100%;
  height: auto;
  object-fit: contain;
  opacity: 0.85;
}

/* Light theme cloud styles */
.light .cloud-img {
  filter: brightness(1.1);
  opacity: 0.7;
}

/* Dark theme cloud styles */
.dark .cloud-img {
  filter: brightness(0.9) hue-rotate(240deg);
  opacity: 0.6;
}

/* Dusk theme cloud styles */
.dusk .cloud-img {
  filter: brightness(0.7) hue-rotate(320deg);
  opacity: 0.6;
}

Enter fullscreen mode Exit fullscreen mode

Now I get it the complexity increases, but it looks so cool.
The color scheme for the backgrounds are as follows.

**Light Theme:**
- Default: Cream (`#fdf5e0`)
- Clouds: Sky Blue (`#87CEEB`)

**Dark Theme:**
- Default: Black (`#000000`)
- Clouds: Dark Navy (`#141852`)

**Dusk Theme:**
- Default: Dark Blue (`#4E5481`)
- Clouds: Light Purple (`#d2c4f4`)
Enter fullscreen mode Exit fullscreen mode

and the features were


#### Individual Cloud Behaviors
- **Cloud 1**: Right-to-left, 40s duration, top position (0%)
- **Cloud 2**: Left-to-right, 45s duration, scaled to 60%, position (15%)
- **Cloud 3**: Right-to-left, 50s duration, scaled to 80%, position (2%)
- **Cloud 4**: Left-to-right, 55s duration, scaled to 75%, position (18%)
- **Cloud 5**: Right-to-left, 60s duration, scaled to 70%, position (20%)

#### Animation Characteristics
- **Continuous Loop**: Clouds move from completely off-screen to completely off-screen
- **No Interruption**: Animation runs perpetually without stopping
- **Varied Timing**: Different speeds create natural, organic movement
- **Layered Depth**: Different scales and positions create 3D depth illusion

Enter fullscreen mode Exit fullscreen mode

Here it was, everything was working perfectly.
Then it struck me, if I found it so cool, why not publish it as a public npm package.

Another planning phase now, I need to identify the files and styles that are important for the cloud animation feature. My initial plan was to display the clouds when the text আকাশ (akash) is clicked, but I cannot export it like that. I needed a cloud on/off button.

This feature adds an interactive sky background with animated clouds to your portfolio. When the user clicks on the Bengali text "আকাশ" (which means "sky"), the background transforms into a beautiful themed sky with clouds floating across the screen in continuous marquee-style movement. The animation is theme-aware and provides visual feedback to guide user interaction.

Made a extraction plan for all the files and keep it as a separate folder inside the project. The directory structure being.

react-cloud-animation/
├── src/
│   ├── components/
│   │   ├── CloudAnimation.tsx
│   │   └── ThemeToggle.tsx
│   ├── context/
│   │   └── ThemeContext.tsx
│   ├── styles/
│   │   └── animations.css
│   ├── assets/
│   │   ├── cloud1.png
│   │   ├── cloud2.png
│   │   ├── cloud3.png
│   │   ├── cloud4.png
│   │   └── cloud5.png
│   ├── types/
│   │   └── index.ts
│   └── index.ts
├── dist/ (build output)
├── package.json
├── tsconfig.json
├── vite.config.ts
├── README.md
└── CHANGELOG.md
Enter fullscreen mode Exit fullscreen mode

And then the extraction ritual.


#### CloudAnimation.tsx
- **Source**: `src/components/CloudAnimation.tsx`
- **Modifications needed**:
  - Remove portfolio-specific imports
  - Make cloud image paths configurable via props
  - Add prop interface for customization
  - Export component with proper TypeScript types

#### ThemeContext.tsx
- **Source**: `src/context/ThemeContext.tsx`
- **Modifications needed**:
  - Keep core theme logic (time-based, manual override)
  - Remove any portfolio-specific dependencies
  - Ensure localStorage keys are configurable
  - Add proper TypeScript exports

#### ThemeToggle.tsx
- **Source**: `src/components/ThemeToggle.tsx`
- **Modifications needed**:
  - Ensure lucide-react dependency is properly declared
  - Add customizable styling props
  - Remove portfolio-specific styling classes

#### animations.css
- **Source**: Extract from `src/index.css` (lines 71-343)
- **Include**:
  - Cloud container styles (`.cloud-container`, `.cloud-img`)
  - Marquee animations (`@keyframes marqueeLeftToRight`, `@keyframes marqueeRightToLeft`)
  - Cloud positioning classes (`.x1`, `.x2`, `.x3`, `.x4`, `.x5`)
  - Theme-specific cloud filters (`.light .cloud-img`, `.dark .cloud-img`, `.dusk .cloud-img`)
  - Glow animations (`@keyframes subtleGlow`, `@keyframes prominentGlow`)
  - Click indicator animations (`.click-indicator`)
  - Fade animations (`@keyframes fadeIn`, `@keyframes fadeOut`)
  - Z-index structure comments and rules
Enter fullscreen mode Exit fullscreen mode

Now here it was, a new project with the cloud animation ready to be published as a npm package. It was time to test it out and create a preview application for this.

Typed out a few prompts on windsurf and voila, it's up. Another challenge arose, I had two sections on the preview page for the cloud animation and as they were using the same context provider, the theme and cloud animation will all be syncing.

I did not want that, and created a separate provider and controls for the demo section. And now it was perfect.

Ans that's it, that's the story of how I put clouds in my portfolio, it fun throughout the process. I published and put it up everywhere for a little bit promotion and I'm done. I'll keep on contributing to the repo and if anyone has a idea of how to make it better, reach out to me on X.

Checkout the project at cloudanimation,
npm package

Thanks for reading this far and see you again in the next story.

Top comments (0)