How I recreated my favorite interactive “desktop simulator” site from Cargo in one day with the help of ChatGPT
The cargo site I wished to recreate in question: https://c3-truestudio.cargo.site/
Firstly, I asked ChatGPT if this was even a possible task for the tech stack of React.js and it concurred that it indeed was possible.
Then I just went straight for the jugular and asked: Can you build this site using react.js: https://c3-truestudio.cargo.site/ ?
To which ChatGPT guided me through a general outline of the steps I might take to build a similar site.
Next I got down to business and inquired about what I believed was one of the toughest components: the video background. To which ChatGPT countered within seconds with a neat step-by-step guide:
(below is verbatim from AI)
Integrating a video as a background in a React application can enhance the visual appeal. Here’s how you can do it:
Step-by-Step Guide to Add a Background Video
- Add the Video File
- Place your video file in the public folder (e.g., public/background.mp4).
2. Create a Background Video Component
- Create a new component, say BackgroundVideo.js:
jsx
Copy code
// BackgroundVideo.js
import React from 'react';
import './BackgroundVideo.css'; // Your CSS for styling
const BackgroundVideo = () => {
return (
<div className="background-video">
<video autoPlay loop muted>
<source src="/background.mp4" type="video/mp4" />
Your browser does not support HTML5 video.
</video>
</div>
);
};
export default BackgroundVideo;
3. Style the Video
- Create a CSS file (BackgroundVideo.css) to style the video:
css
Copy code
/* BackgroundVideo.css */
.background-video {
position: fixed; /* Fixed to cover the whole viewport */
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden; /* Hide overflow */
z-index: -1; /* Send it behind other content */
}
video {
min-width: 100%; /* Cover the viewport */
min-height: 100%;
object-fit: cover; /* Maintain aspect ratio */
}
4. Use the Background Video Component
- Include the BackgroundVideo component in your main component (e.g., App.js):
jsx
Copy code
// App.js
import React from 'react';
import BackgroundVideo from './BackgroundVideo';
import Header from './Header';
import About from './About'; // other components
const App = () => {
return (
<>
<BackgroundVideo />
<Header />
<About />
{/* Add other components here */}
</>
);
};
export default App;
Notes
- Video Format: Ensure your video is in a web-friendly format (e.g., MP4) for compatibility.
- Performance: Large video files can slow down loading times. Optimize the video for web use.
- Accessibility: Provide alternative content for browsers that don’t support video.
- Mobile Considerations: Some mobile devices may not autoplay videos. You might want to provide a fallback image.
This setup will give you a full-screen background video that plays automatically and loops! Let me know if you need any more help!
— — — — — — — — — — — — — — — — — — — — — -
Now saddled with this knowledge, I decided to get down to business and create the darn thing!
Here is my TO DO list:
[ ] Create my own version of the background video
[ ] Create my own version of all assets
[ ] Start new React.js repository
[ ] Keep checking in with ChatGPT and this blog post along the way
— — — — — — — — — — — — — — — — — — — — — -
Next I noted that the cursor changes when hovering over specific icons and I’ve never done that before in React.js, so I asked ChatGPT about it:
ChatGPT Response: Changing the cursor on hover over specific icons in a React application is straightforward. You can achieve this with CSS. Here’s how to do it:
Step-by-Step Guide To Changing Cursor on Icon Hover
- Identify Your Icons
- Make sure you have your icons defined in a component. For example, you might have a component like
Icon.js
.
jsx
Copy code
// Icon.js import React from 'react';
import './Icon.css';
const Icon = ({ iconName }) =>
{
return (
<div className="icon">
<i className={`fa ${iconName}`}></i> {/* Example using Font Awesome */}
</div>
);
};
export default Icon;
2. Create the CSS for Hover Effect
- Create a CSS file (e.g.,
Icon.css
) and define the cursor style for your icons.
/* Icon.css */
.icon {
display: inline-block;
margin: 10px;
font-size: 24px; /* Adjust as necessary */
cursor: pointer; /* Change cursor to pointer on hover */
}
/* Change cursor on hover */
.icon:hover {
cursor: url('/path/to/custom-cursor.png'), pointer; /* Custom cursor */ }
If you want to use the default pointer cursor, you can just keep the cursor: pointer;
in the .icon:hover
class.
3. Use the Icon Component
- Use your
Icon
component wherever you need in your main app component. - jsx
- Copy code
// App.js
import React from 'react';
import Icon from './Icon';
const App = () => {
return (
<div>
<h1>My Icons</h1>
<Icon iconName="fa-home" /> {/* Example icon */}
<Icon iconName="fa-envelope" />
<Icon iconName="fa-user" />
</div>
);
};
export default App;
Using Custom Cursors
If you want to use a custom cursor:
- Prepare the Cursor Image
- Make sure your cursor image is in a suitable format (e.g.,
.png
or.cur
). - Place it in the
public
folder (e.g.,/public/path/to/custom-cursor.png
).
2. Update the CSS
- In the hover state, set the cursor to the custom image as shown above.
Conclusion To Changing Cursor on Icon Hover
Now, when you hover over the icons, the cursor will change based on your CSS rules! Feel free to customize the icon styles further or ask if you need help with specific icons or customizations!
— — — — — — — — — — — — — — — — — — — -
Seemed intuitive enough to me!
Now to tackle my TO DO list…
I created my react repository by going to my terminal and typing in: npx create-react-app cargo-portfolio-2024
and then I was off to the races!
(If you want to learn more about npx create-react-app
check out this documentation.)
And by races I mean putting off shooting the short looping background video. Maybe I could animate it? No, part of the fun would be seeing my face look around the screen… I just had to be screen-ready and film it… all my procrastination senses started tingling… not a good sign!
In the meantime… I was curious: How would ChatGPT tell me to code the Eyes at the top which follow the cursor?
— — — — — — — — — — — — — — — — — — -
Creating eyes that follow the cursor in React.js involves a combination of state management and event handling. Here’s how you can implement this effect:
Step-by-Step Guide to Creating eyes that follow the cursor
- Set Up the Component
- Create a new component, say
Eyes.js
, for the eyes that will follow the cursor.
2. Manage State for Eye Position
- Use the
useState
anduseEffect
hooks to track the cursor's position and update the eyes' position accordingly.
3. Code Example
Here’s how you can do it:
jsx
Copy code
// Eyes.js
import React, { useEffect, useState } from 'react';
import './Eyes.css'; // CSS for styling
const Eyes = () => {
const [eyePosition, setEyePosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (event) => {
const { clientX, clientY } = event;
setEyePosition({ x: clientX, y: clientY });
};
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
};
}, []);
return (
<div className="eyes">
<div
className="eye"
style={{
transform: `translate(${eyePosition.x}px, ${eyePosition.y}px)`,
}}
>
<div className="pupil" />
</div>
<div
className="eye"
style={{
transform: `translate(${eyePosition.x + 50}px, ${eyePosition.y}px)`,
}}
>
<div className="pupil" />
</div>
</div>
);
};
export default Eyes;
- CSS for Styling
- Create a CSS file (
Eyes.css
) to style the eyes and pupils.
css
Copy code
/* Eyes.css */
.eyes {
position: absolute; /* Positioned absolutely in the header */
top: 20px; /* Adjust as necessary */
left: 50%; /* Center horizontally */
transform: translateX(-50%); /* Center adjustment */
display: flex;
}
.eye {
width: 40px; /* Adjust size */
height: 20px;
background-color: white;
border: 2px solid black;
border-radius: 50%;
position: relative;
margin: 0 10px; /* Space between eyes */
overflow: hidden; /* Hide pupil overflow */
}
.pupil {
width: 10px; /* Adjust pupil size */
height: 10px;
background-color: black;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
- Integrate the Eyes Component
- Include the
Eyes
component in your header or main component.
jsx
Copy code
// Header.js
import React from 'react';
import Eyes from './Eyes';
const Header = () => {
return (
<header>
<h1>Your Header</h1>
<Eyes />
</header>
);
};
export default Header;
Explanation
- Mouse Movement Tracking: The
handleMouseMove
function updates the state with the current mouse position. TheuseEffect
hook sets up and cleans up the event listener. - Eye Positioning: The eyes are styled and positioned absolutely to follow the cursor. Adjust the offset (like
+50px
) to separate the eyes. - CSS Styling: The styling creates a simple eye effect, which you can customize further.
Customization
You can customize the size, colors, and overall design of the eyes and pupils based on your needs. Let me know if you need any further modifications or assistance!
— — — — — — — — — — — — — — — — — — — — -
Sweet! Seems extremely doable. TY ChatGPT!
Next I Googled “mac desktop icons” and found a site called iconfinder which seemed to have some free desktop icons I could use for my site!
I was particularly keen on silver and blue set in the inserted image above.
So far so good (from my TO DO list):
[X] Create my own version of all assets
[X] Start new React.js repository
Now the dreaded video… or really prep work for said video:
- getting the right personal look: makeup/attire
- making sure camera is charged/setup and finding a neutral backdrop
- shooting something that will look good in the background of the site and on loop…
— — — — — — — — — — — — — — — — —
Before doing the video… I know, I know… I can’t put it off forever… I decided to put together what I have so far (with a demo stock video asset as the background). And I noticed that the “eyes” on the header were not moving how I wanted so I kindly asked ChatGPT to update the code:
Here’s an updated implementation to achieve this:
Updated Eyes Component
This version will allow the pupils to follow the cursor in both x and y directions while remaining constrained within the eye.
jsx
Copy code
// Eyes.js
import React, { useEffect, useState } from 'react';
import './Eyes.css'; // CSS for styling
const Eyes = () => {
const [pupils, setPupils] = useState([{ x: 0, y: 0 }, { x: 0, y: 0 }]);
const eyeDiameter = 40; // Diameter of the eye
const pupilDiameter = 10; // Diameter of the pupil
const maxOffset = (eyeDiameter - pupilDiameter) / 2; // Maximum offset for pupils
useEffect(() => {
const handleMouseMove = (event) => {
const { clientX, clientY } = event;
// Center positions for the eyes
const eyePositions = [
{ x: window.innerWidth / 2 - 60, y: window.innerHeight / 2 }, // Left eye position
{ x: window.innerWidth / 2 + 60, y: window.innerHeight / 2 }, // Right eye position
];
const newPupils = eyePositions.map((eyePosition) => {
const offsetX = Math.max(-maxOffset, Math.min(maxOffset, clientX - eyePosition.x));
const offsetY = Math.max(-maxOffset, Math.min(maxOffset, clientY - eyePosition.y));
return { x: offsetX, y: offsetY };
});
setPupils(newPupils);
};
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('mousemove', handleMouseMove);
};
}, []);
return (
<div className="eyes">
<div className="eye">
<div
className="pupil"
style={{
transform: `translate(${pupils[0].x}px, ${pupils[0].y}px)`,
}}
/>
</div>
<div className="eye">
<div
className="pupil"
style={{
transform: `translate(${pupils[1].x}px, ${pupils[1].y}px)`,
}}
/>
</div>
</div>
);
};
export default Eyes;
CSS for Eyes and Pupils
Ensure your CSS (Eyes.css
) remains as follows:
css
Copy code
/* Eyes.css */
.eyes {
position: absolute; /* Positioned absolutely in the header */
top: 20px; /* Adjust as necessary */
left: 50%; /* Center horizontally */
transform: translateX(-50%); /* Center adjustment */
display: flex;
}
.eye {
width: 40px; /* Adjust size */
height: 20px;
background-color: white;
border: 2px solid black;
border-radius: 50%;
position: relative;
margin: 0 10px; /* Space between eyes */
overflow: hidden; /* Hide pupil overflow */
}
.pupil {
width: 10px; /* Adjust pupil size */
height: 10px;
background-color: black;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: transform 0.1s; /* Smooth movement */
}
Key Changes
- Eye Positions: Adjusted to be relative to the center of the window. You can change the
60
in thex
position to fit your layout. - Offset Calculation: The pupils will now correctly follow both x and y axes relative to the center of each eye, remaining constrained within the eye.
Testing
Make sure to check that the pupils now follow your mouse cursor in all directions while staying within the eye boundaries. If you need any more adjustments or help, let me know!
— — — — — — — — — — — — — — — — — -
So this is where I’m at so far:
Next I wanted to add a folder icon and style the text below with a drop shadow, so I reached out to my good pal: ChatGPT
Can you help me style the text beneath the folder icons white with a drop shadow?
ChatGPT said:
Sure! To style the text beneath the folder icons to be white with a drop shadow, you’ll need to add some CSS to achieve this effect.
CSS for Text Styling
Here’s an example of how you can style the text:
css
Copy code
/* Assuming your folder icon text has a class like .folder-text */
.folder-text {
color: white; /* Text color */
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.7); /* Drop shadow */
font-size: 14px; /* Adjust font size as needed */
text-align: center; /* Center the text */
margin-top: 5px; /* Space between icon and text */
}
HTML Structure Example
If your folder icons and text are structured like this:
jsx
Copy code
<div className="folder-icon">
<IconComponent /> {/* Your folder icon component */}
<div className="folder-text">Folder Name</div>
</div>
Complete Example
Here’s how it all fits together:
jsx
Copy code
// FolderIcon.js
import React from 'react';
import './FolderIcon.css'; // Import your CSS
const FolderIcon = ({ icon, text }) => {
return (
<div className="folder-icon">
{icon}
<div className="folder-text">{text}</div>
</div>
);
};
export default FolderIcon;
CSS Example
css
Copy code
/* FolderIcon.css */
.folder-icon {
display: flex;
flex-direction: column; /* Stack icon and text */
align-items: center; /* Center items */
}
.folder-text {
color: white; /* Text color */
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.7); /* Drop shadow */
font-size: 14px; /* Adjust font size as needed */
text-align: center; /* Center the text */
margin-top: 5px; /* Space between icon and text */
}
Adjustments
- You can change the
font-size
,text-shadow
values, andmargin-top
as needed to fit your design. - Make sure the parent container of the folder icons has a background color that contrasts well with white text for better visibility.
If you need further adjustments or have specific requirements, just let me know!
— — — — — — — — — — — — — — — —
And with that the site is really coming together:
I also added some media queries to start thinking about responsiveness for mobile devices:
As you can see the menu turns into a hamburger, the “eyes” disappear and the folder container will start displaying an orderly grid.
Next I added Font Awesome Icons onto the Navbar (in the top right) and got some help from this stackoverflow when I got into a bit of a jam. I also added new desktop icons to the page and I think they look quite nice:
Next I wanted a modal attached to a mail/envelope icon. So I asked ChatGPT: When I click the mail icon on the top right can you help me make an accessible modal component that pops open with a netlify contact form embedded in the modal?
ChatGPT said:
Sure! Here’s how you can create an accessible modal component in React that opens when you click the mail icon and contains an embedded Netlify contact form.
Step 1: Create the Modal Component
- Create a new file for the Modal Component
- Name it
Modal.js
.
jsx
Copy code
// Modal.js
import React from 'react';
import './Modal.css'; // Import your CSS for styling
const Modal = ({ isOpen, onClose }) => {
if (!isOpen) return null; // Don't render if modal is not open
return (
<div className="modal-overlay" role="dialog" aria-modal="true" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<button className="close-button" onClick={onClose} aria-label="Close modal">
×
</button>
<h2>Contact Us</h2>
<iframe
title="Netlify Contact Form"
src="https://your-netlify-form-url" // Replace with your Netlify form URL
width="100%"
height="400px"
style={{ border: 'none' }}
/>
</div>
</div>
);
};
export default Modal;
Step 2: Style the Modal
Create a CSS file named Modal.css
for basic styling.
css
Copy code
/* Modal.css */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.7); /* Semi-transparent background */
display: flex;
justify-content: center;
align-items: center;
z-index: 1000; /* Make sure it appears on top */
}
.modal-content {
background-color: white;
padding: 20px;
border-radius: 5px;
width: 90%;
max-width: 500px; /* Adjust as needed */
position: relative;
}
.close-button {
position: absolute;
top: 10px;
right: 10px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
}
Step 3: Integrate the Modal in Your App
Now, let’s integrate the modal into your main application component.
jsx
Copy code
// App.js
import React, { useState } from 'react';
import Modal from './Modal';
import { MailIcon } from './icons'; // Adjust the import according to your icon setup
const App = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);
return (
<div>
<header>
<button onClick={openModal} aria-label="Open contact form">
<MailIcon />
</button>
</header>
<Modal isOpen={isModalOpen} onClose={closeModal} />
</div>
);
};
export default App;
Step 4: Replace the Netlify Form URL
Make sure to replace https://your-netlify-form-url
in the Modal.js
file with the actual URL of your Netlify form.
Explanation
- Accessibility: The modal uses
role="dialog"
andaria-modal="true"
for accessibility, allowing screen readers to identify it as a dialog. - Close Button: The close button has an
aria-label
for better accessibility. - Click Outside to Close: Clicking outside the modal content will close the modal.
- Styling: Basic styles are applied for visibility and layout.
Testing
Ensure you test the modal for keyboard accessibility (like using the Esc
key to close) and responsiveness.
If you need any further enhancements or specific features, let me know!
— — — — — — — — — — — — — — — — —
I had to play around with passing down props through React app components and all that jazz but I finally figured it out and Ta-Da we have a working modal:
Next I started in on the interior pages, such as the “about” and “resume” pages. However I noted that I was missing a site-wide footer so I returned to the handy-dandy ChatGPT with a new prompt.
Can you create a footer component with the year and thanking this site: https://c3-truestudio.cargo.site/ for inspiration?
ChatGPT said:
Sure! Here’s how you can create a simple footer component in React that includes the current year and a thank you message inspired by the site you mentioned.
Step 1: Create the Footer Component
Create a new file named Footer.js
:
jsx
Copy code
// Footer.js
import React from 'react';
import './Footer.css'; // Import your CSS for styling
const Footer = () => {
const currentYear = new Date().getFullYear(); // Get the current year
return (
<footer className="footer">
<p>
© {currentYear} Thank you to <a href="https://c3-truestudio.cargo.site/" target="_blank" rel="noopener noreferrer">C3 True Studio</a> for the inspiration.
</p>
</footer>
);
};
export default Footer;
Step 2: Style the Footer
Create a CSS file named Footer.css
for styling:
css
Copy code
/* Footer.css */
.footer {
background-color: #000; /* Black background */
color: white; /* White text */
text-align: center; /* Center text */
padding: 20px; /* Padding */
position: relative; /* Adjust if needed */
}
.footer a {
color: #fff; /* White link color */
text-decoration: underline; /* Underline for links */
}
.footer a:hover {
color: #ccc; /* Lighter color on hover */
}
Step 3: Integrate the Footer into Your App
Now, let’s add the footer to your main application component, typically at the bottom of your layout.
jsx
Copy code
// App.js
import React, { useState } from 'react';
import Modal from './Modal';
import Footer from './Footer';
import { MailIcon } from './icons'; // Adjust according to your icon setup
const App = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);
return (
<div>
<header>
<button onClick={openModal} aria-label="Open contact form">
<MailIcon />
</button>
</header>
<Modal isOpen={isModalOpen} onClose={closeModal} />
<Footer />
</div>
);
};
export default App;
Explanation
- Dynamic Year: The footer displays the current year dynamically using
new Date().getFullYear()
. - Link: The footer contains a link to the C3 True Studio site, styled for accessibility.
- Styling: The footer is styled with a black background and centered white text.
Testing
Ensure the footer appears at the bottom of your app and is responsive. If you need further customizations or additional elements, feel free to ask!
— — — — — — — — — — — — — — -
So there you have it. A wild Footer appears:
Okay okay okay… only one thing left to do… Create the dang background video and upload the code to a hosting site :)
Before I reveal the final…
I had one last ask (for now) of ChatGPT: How to center the video — so the video would always remain centered in the viewport no matter how small the screen width.
To ensure your background video is always centered while covering the entire viewport, you can adjust your CSS slightly. The object-fit: cover;
property will help maintain the aspect ratio, but to center the video effectively, you should also use top
, left
, and possibly transform
properties. Here’s how you can do it:
Updated CSS
css
Copy code
/* BackgroundVideo.css */
.background-video {
position: fixed; /* Fixed to cover the whole viewport */
top: 50%; /* Center vertically */
left: 50%; /* Center horizontally */
width: 100%;
height: 100%;
overflow: hidden; /* Hide overflow */
z-index: -1; /* Send it behind other content */
transform: translate(-50%, -50%); /* Adjust positioning */
}
video {
min-width: 100%; /* Cover the viewport */
min-height: 100%;
object-fit: cover; /* Maintain aspect ratio */
position: absolute; /* Make it absolute for centering */
top: 50%; /* Center vertically */
left: 50%; /* Center horizontally */
transform: translate(-50%, -50%); /* Adjust positioning */
}
Explanation of Changes
- Centering with
top
,left
, andtransform
: - Setting
top: 50%
andleft: 50%
positions the video element at the center of the viewport. - The
transform: translate(-50%, -50%)
shifts the element back by half its width and height, effectively centering it.
Result
With this configuration, your video should always be centered in the viewport, and it will maintain its aspect ratio while covering the background. If you resize the window, it will remain centered.
If you have any more questions or need further adjustments, feel free to ask!
— — — — — — — — — — — — — — -
And voila:
And just like that my site is LIVE: https://clairefilipek.netlify.app/
It could surely use some adjustments… but for One Day and pretty much just me and ChatGPT… I’m happy with it :)
Thanks for viewing! And check out the site!!