Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fannys Portfolio #401

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
512ed61
started with project portfolio
Fannyhenriques Nov 2, 2024
e5eb611
added components and styling
Fannyhenriques Nov 4, 2024
79918ae
added tagscomponent in tags, projects and articles, and added styling
Fannyhenriques Nov 4, 2024
7396213
added props in gridsection.jsx
Fannyhenriques Nov 11, 2024
a5f0d36
fixed grid-layout for articles and projects
Fannyhenriques Nov 11, 2024
7c7d959
added styling for buttons
Fannyhenriques Nov 11, 2024
c39676a
refined styling for buttons
Fannyhenriques Nov 11, 2024
1b2c37f
updated styling for tags inside the skills and overall styling for sk…
Fannyhenriques Nov 12, 2024
84f91db
added iconbutton.jsx with styling for the buttons in the letstalk-sec…
Fannyhenriques Nov 12, 2024
32e1c0d
added styling in gallary for the images
Fannyhenriques Nov 12, 2024
c0d1f18
added links to the buttons in project and articlesections
Fannyhenriques Nov 12, 2024
8f81618
added styling for responsiveness in articles and projects and grid
Fannyhenriques Nov 12, 2024
d1349b5
fiexed responsive design for header
Fannyhenriques Nov 13, 2024
09c7c28
finnished overall responsive styling
Fannyhenriques Nov 13, 2024
9dbd833
added padding
Fannyhenriques Nov 13, 2024
ca0dda4
changed name for icon and updated pathway
Fannyhenriques Nov 13, 2024
e59afe7
testcommit to se if icon works
Fannyhenriques Nov 13, 2024
85c2848
removed icon stackoverflow because of error in netlify
Fannyhenriques Nov 13, 2024
97894c8
added icon stackoverflow again
Fannyhenriques Nov 13, 2024
163494e
added important in default styling in articles section because it is …
Fannyhenriques Nov 13, 2024
8436666
push-test for deploying to netlify again
Fannyhenriques Nov 13, 2024
10d2466
added media queries for the default styling for the netlify-site
Fannyhenriques Nov 13, 2024
59e999c
fixed indentations
Fannyhenriques Nov 13, 2024
6d16f5c
changed white and black to primary and secondary color
Fannyhenriques Nov 13, 2024
925f84a
added readme
Fannyhenriques Nov 13, 2024
2fcae15
removed the second readme that is not being used
Fannyhenriques Nov 13, 2024
ae77565
added links to buttons
Fannyhenriques Nov 13, 2024
0ab13c1
spacing
Fannyhenriques Nov 15, 2024
5763aef
added hovereffect on buttons, changed the buttoncomponent to use an a…
Fannyhenriques Jan 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
50 changes: 0 additions & 50 deletions README.md

This file was deleted.

24 changes: 24 additions & 0 deletions my-portfolio/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
46 changes: 46 additions & 0 deletions my-portfolio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<h1 align="center">
<a href="">
<img src="/react-p.svg" alt="Project Banner Image">
</a>
</h1>

# Technigo - React Portfolio Project

This project is a portfolio built using React, following a design provided by a UX designer on Figma. The goal was to replicate the design as closely as possible while using reusable components for scalability and maintainability.

Overall Layout
I structured the project using reusable components, such as Typography, Buttons, and Grid. These components were then passed into various section components to construct the layout. To ensure the design matched the Figma prototype as closely as possible, I utilized media queries for adjusting spacing and layouts across different screen sizes.

# Challanges:

- Structure:
The structure of the app was one of the main challenges. Initially, I aimed to use as many reusable components as possible to keep the larger components clean and maintainable. However, as the project evolved, it became more challenging to keep the parent components dry. This led to the need for adding additional logic and styles directly into those parent components to achieve the desired layout.

- Spacing Issues:
I initially tried passing different sectionTypes as props into the Grid component to style all sections using display: grid. However, some sectionTypes were being overridden by styles in the Grid.css, leading to unexpected layout issues.
Using gap in display: grid didn’t work as expected either, causing layout inconsistencies.

- Solution:
I first addressed this by using inline styling within the Grid component. However, it became harder to add media queries while keeping the code clean. Instead I solved this by using the backgroundColor prop for the article and project sections to add padding, and I used Flexbox in the card components to manage spacing. The other sections were styled manually. However, with more time, I would refactor the Grid.jsx to use styled-components for better flexibility in spacing and more maintainable code.

- Image Sizing:
I had to adjust the width and height of the images to ensure they matched the Figma design, this was done by tweaking the CSS for image containers and testing across different screen sizes.

- Media Queries:
The media queries required several adjustments to ensure that the layout remained responsive and close to the Figma design.

If I had more time I would:

- Use styled.components for better code organization and flexibility.
- Simplify the structure to avoid unnecessary components.
- Replace px with rem for better scalability and accessibility.
- Clean up the code to remove redundancy.
- Improve media queries for a more user-friendly layout

# View it live:

https://fannys-portfolio.netlify.app/

## Instructions

[Check this projects instructions here](https://github.com/Technigo/project-portfolio/blob/main/instructions.md)
38 changes: 38 additions & 0 deletions my-portfolio/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import js from '@eslint/js'
import globals from 'globals'
import react from 'eslint-plugin-react'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'

export default [
{ ignores: ['dist'] },
{
files: ['**/*.{js,jsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
ecmaVersion: 'latest',
ecmaFeatures: { jsx: true },
sourceType: 'module',
},
},
settings: { react: { version: '18.3' } },
plugins: {
react,
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...js.configs.recommended.rules,
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
...reactHooks.configs.recommended.rules,
'react/jsx-no-target-blank': 'off',
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
]
21 changes: 21 additions & 0 deletions my-portfolio/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet">
<title>Portfolio</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>

</html>
4,286 changes: 4,286 additions & 0 deletions my-portfolio/package-lock.json

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions my-portfolio/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "my-portfolio",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"eslint": "^9.13.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.11.0",
"vite": "^5.4.10"
}
}
18 changes: 18 additions & 0 deletions my-portfolio/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
:root {
font-family: "Poppins", sans-serif;
margin: 0 auto;

/* Colors */
--primary-color: #000000;
--secondary-color: #FFFFFF;
--accent-color: #7e7070;
}

body {
margin: 0 auto;
}

img {
max-width: 100%;
max-height: 100%;
}
21 changes: 21 additions & 0 deletions my-portfolio/src/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import './App.css';
import { Header } from "./components/sections/HeaderSection/Header.jsx";
import { FeaturedProjects } from "./components/sections/ProjectsSection/FeaturedProjects.jsx";
import { Tech } from "./components/sections/TechSection/Tech.jsx";
import { Skills } from "./components/sections/SkillsSection/Skills.jsx";
import { FeaturedArticles } from "./components/sections/ArticlesSection/FeaturedArticles.jsx";
import { LetsTalk } from "./components/sections/LetsTalkSection/LetsTalk.jsx";

export const App = () => {

return (
<>
<Header />
<FeaturedProjects />
<Tech />
<Skills />
<FeaturedArticles />
<LetsTalk />
</>
);
};
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconArrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconArrowWhite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconGithub.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconGithubWhite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconInstagram.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconLinkedin.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconStackOverflow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconTwitter.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconWeb.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions my-portfolio/src/assets/iconWebWhite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added my-portfolio/src/assets/img1.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added my-portfolio/src/assets/img2.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added my-portfolio/src/assets/img3.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added my-portfolio/src/assets/profile-img.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* default styling */
.articles-container {
grid-column: span 4;
display: flex;
flex-direction: column;
}

.title {
color: var(--secondary-color);
padding-bottom: 128px;
}

.article-wrapper {
display: flex;
flex-direction: column;
gap: 64px;
}

.view-more-container {
display: flex;
justify-content: center;
}

/* added media queries for default styling so the site deployed to netlify will work below 480px */
@media (max-width: 479px) {
.articles-container {
grid-column: span 4;
display: flex;
flex-direction: column;
}

.title {
color: var(--secondary-color);
padding-bottom: 128px;
}

.article-wrapper {
display: flex;
flex-direction: column;
gap: 64px;
}

.view-more-container {
display: flex;
justify-content: center;
}
}

/* Breakpoint for tablet */
@media (min-width: 480px) and (max-width: 768px) {
.articles-container {
grid-column: span 8;

}
}
/* breakpoint for small desktop */
@media (min-width: 769px) and (max-width: 1024px) {
.articles-container {
grid-column: span 12;

}
}
@media (min-width: 1024px) {
.articles-container {
grid-column: span 12;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import articlesData from "../../../data/articles.json";
import { H2 } from "../../ui/Typography/Typography.jsx";
import { Card } from "../Cards/Card.jsx";
import { Grid } from "../../ui/GridLayout/Grid.jsx";
import { images } from "../../ui/Images/Images.jsx";
import { Button } from "../../ui/Buttons/Button.jsx";
import iconArrowWhite from "../../../assets/iconArrowWhite.svg";
import "./FeaturedArticles.css";

export const FeaturedArticles = () => {
console.log(articlesData);
return (
<Grid background="black">
<div className="articles-container">
<div className="title">
<H2> My words</H2>
</div>
<div className="article-wrapper">
{articlesData.articles.map((article) => (
<Card
key={article.name}
imageSource={images[article.image] || article.image}
cardTag={article.tags}
cardTitle={article.name}
cardDescription={article.description}
sectionType="article"
link={article.link}
/>
))}
</div>
<div className="view-more-container">
<Button
text="Read More Articles"
isViewMore={true}
sectionType="article"
icon={iconArrowWhite}
href="https://www.linkedin.com"
target="_blank"
/>
</div>
</div>
</Grid>
);
};
39 changes: 39 additions & 0 deletions my-portfolio/src/components/sections/BioSection/Bio.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.bio-section {
width: 100%;
}

.content-section {
color: var(--secondary-color);
padding: 64px 16px;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
@media (min-width: 375px) {
.content-section {
text-align: center;
}
}
@media (min-width: 375px) and (max-width: 1024px) {
.content-section p {
max-width: 669px;
}
.content-section {
padding-top: 64px;
padding-bottom: 128px;
padding-left: 24px;
padding-right: 24px;
}
}
@media (min-width: 1025px) {
.content-section p {
max-width: 782px;
}
.content-section {
padding-top: 64px;
padding-bottom: 128px;
padding-left: 24px;
padding-right: 24px;
}
}
15 changes: 15 additions & 0 deletions my-portfolio/src/components/sections/BioSection/Bio.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import "./Bio.css";
import { H2, BodyText } from "../../ui/Typography/Typography";

export const Bio = () => {
return (
<section className="bio-section">
<div className="content-section">
<H2>Bio</H2>
<BodyText>
Fanny is an exceptional developer known for her innovative solutions and exceptional coding abilities. She creates user-friendly applications and solves complex issues with ease. Her drive for excellence makes her a valuable asset to any project and a standout in the technology field.
</BodyText>
</div>
</section>
);
};
185 changes: 185 additions & 0 deletions my-portfolio/src/components/sections/Cards/Card.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
.project-card,
.article-card {
display: flex;
flex-direction: column;
gap: 32px;
}

.project-card {
width: 100%;
}

.article-card {
width: 100%;
color: var(--secondary-color);
}

.card-heading {
text-align: center;
}

.project-img {
width: 100%;
height: 479px;
border-radius: 12px;
object-fit: cover;
}

.article-img {
width: 100%;
height: 200px;
border-radius: 12px;
object-fit: cover;
}

.project-info-box,
.article-info-box {
display: flex;
flex-direction: column;
gap: 16px;
max-width: 580px;
}

.tags-container {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: 4px;
max-width: 100%;
flex-wrap: wrap;
}

.description-box{
display: flex;
flex-direction: column;
gap: 32px;
}

.button-container {
display: flex;
flex-direction: column;
gap: 8px;
}

/* Custom-breakpoint for tablet */
@media (min-width: 530px) and (max-width: 589px) {
.article-img {
width: 170px;
height: 390px;
}
.article-card {
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
width: 100%;
}
}
/* Custom-breakpoint for tablet */
@media (min-width: 590px) and (max-width: 768px) {
.article-img {
width: 200px;
height: 338px;
}
.article-card {
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
width: 100%;
}
.project-card {
gap: 64px;
}
}

/* breakpoint for small desktop */
@media (min-width: 769px) and (max-width: 1024px) {
.article-card {
display: flex;
flex-direction: row;
align-items: center;
margin: 0 auto;
gap: 45px;
max-width: 900px;
}
.article-img {
height: 300px;
overflow: hidden;
}
.project-img {
max-width: 379px;
max-height: 379px;
overflow: hidden;
}
.project-card {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 40px;
}
.project-info-box {
max-width: 500px;
}

}

@media (min-width: 1025px) and (max-width: 1200px) {
.article-card {
display: flex;
flex-direction: row;
align-items: center;
margin: 0 auto;
gap: 125px;
max-width: 1100px;
}
.article-img {
height: 311px;
overflow: hidden;
}
.project-img {
width: 379px;
height: 379px;
}
.project-card {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 64px;
}
.project-info-box {
max-width: 500px;
}
}
@media (min-width: 1201px) {
.article-card {
display: flex;
flex-direction: row;
align-items: center;
margin: 0 auto;
gap: 125px;
max-width: 1100px;
}
.article-img {
height: 311px;
overflow: hidden;
}
.project-img {
max-width: 479px;
max-height: 479px;
overflow: hidden;
}
.project-card {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 128px;
}
.project-info-box {
max-width: 580px;
}
}
65 changes: 65 additions & 0 deletions my-portfolio/src/components/sections/Cards/Card.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/* eslint-disable react/prop-types */
import "./Card.css";
import { BodyText, H3 } from "../../ui/Typography/Typography.jsx";
import { Button } from "../../ui/Buttons/Button.jsx";
import iconWeb from "../../../assets/iconWeb.svg";
import iconGithubWhite from "../../../assets/iconGithubWhite.svg";
import iconWebWhite from "../../../assets/iconWebWhite.svg";
import { Tag } from "../../ui/Tags/Tags.jsx";

export const Card = ({
imageSource,
sectionType,
cardTag,
cardTitle,
cardDescription,
netlify,
github,
link,
}) => {
return (
<article className={`${sectionType}-card`}>
<img
src={imageSource}
className={`${sectionType}-img`}
alt="portfolio-img"
/>
<div className={`${sectionType}-info-box`}>
<div className="tags-container">
{Array.isArray(cardTag)
? cardTag.map((tag, index) => <Tag key={index} sectionType={sectionType} text={tag} />)
: <Tag sectionType={sectionType} text={cardTag} />}
</div>
<H3>{cardTitle}</H3>
<div className="description-box">
<BodyText>{cardDescription}</BodyText>
<div className="button-container">
{sectionType === "project" ? (
<>
<Button text="Live Demo"
icon={iconWeb}
sectionType={sectionType}
href={netlify}
target="_blank"
/>
<Button text="View Code"
icon={iconGithubWhite}
sectionType={sectionType}
href={github}
target="_blank"
/>
</>
) : (
<Button text="Read Article"
icon={iconWebWhite}
sectionType={sectionType}
href={link}
target="_blank"
/>
)}
</div>
</div>
</div>
</article>
);
};
152 changes: 152 additions & 0 deletions my-portfolio/src/components/sections/GallerySection/Gallery.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
.gallery-container {
display: flex;
justify-content: center;
height: 100%;
}

.image-container {
margin: 0px 16px;
position: relative;
min-height: 160px;
display: flex;
align-items: center;
justify-content: center;
max-width: 280px;
width: 100%;
}

.image-wrapper {
position: relative;
display: flex;
justify-content: center;
width: 100%;
}

.img1,
.img2,
.img3 {
width: 130px;
height: 140px;
flex-shrink: 0;
border-radius: 12px;
position: absolute;
}

.img1 {
transform: rotate(-4.695deg);
position: absolute;
z-index: 1;
top: 10px;
left: 5px;
}

.img2 {
position: relative;
z-index: 2;
}

.img3 {
transform: rotate(4.695deg);
position: absolute;
z-index: 1;
top: 10px;
left: 145px;
}

@media (min-width: 375px) and (max-width: 480px){
.img1,
.img2,
.img3 {
width: 158px;
height: 168px;
}
.img1 {
top: 10px;
left: -30px;
}
.img3 {
top: 10px;
left: 155px;
}
}
/* breakpoint tablet */
@media (min-width: 481px) and (max-width:668px) {
.image-container {
max-width: 400px;
padding-bottom: 64px;
}
.img1,
.img2,
.img3 {
width: 198px;
height: 208px;
}
.img1 {
top: 10px;
left: -5px;
}
.img3 {
top: 10px;
left: 210px;
}
}
@media (min-width:669px) and (max-width:744px) {
.image-container {
max-width: 630px;
padding-bottom: 64px;
}
.img1,
.img2,
.img3 {
width: 270px;
height: 290px;
}
.img1 {
top: 15px;
left: 15px;
}
.img3 {
top: 15px;
left: 350px;
}
}
@media (min-width:745px) and (max-width:1024px) {
.image-container {
max-width: 700px;
padding-bottom: 64px;
}
.img1,
.img2,
.img3 {
width: 320px;
height: 340px;
}
.img1 {
top: 17px;
left: 16px;
}
.img3 {
top: 17px;
left: 365px;
}
}
@media (min-width:1025px) {
.image-container {
max-width: 810px;
padding-bottom: 64px;
}
.img1,
.img2,
.img3 {
width: 358px;
height: 382px;
}
.img1 {
top: 20px;
left: 17px;
}
.img3 {
top: 20px;
left: 440px;
}
}
16 changes: 16 additions & 0 deletions my-portfolio/src/components/sections/GallerySection/Gallery.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { images } from "../../ui/Images/Images";
import "./Gallery.css";

export const Gallery = () => {
return (
<div className="gallery-container">
<div className="image-container">
<div className="image-wrapper">
<img className="img1" src={images.img1} alt="Image 1" />
<img className="img2" src={images.img2} alt="Image 2" />
<img className="img3" src={images.img3} alt="Image 3" />
</div>
</div>
</div>
);
};
31 changes: 31 additions & 0 deletions my-portfolio/src/components/sections/HeaderSection/Header.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.header {
background-color: var(--primary-color);
color: var(--secondary-color);
width: 100%;

}

.header-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
padding: 64px 16px;
text-align: center;
}

.header-section h3 {
max-width: 782px;
}

@media (min-width: 481px) {
.header-section {
padding: 64px 24px;
}
}

@media (min-width: 1024px) {
.header-section {
padding: 128px 24px;
}
}
22 changes: 22 additions & 0 deletions my-portfolio/src/components/sections/HeaderSection/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { H1, H3 } from "../../ui/Typography/Typography";
import { Gallery } from "../GallerySection/Gallery";
import { Bio } from "../BioSection/Bio";
import "./Header.css";

export const Header = () => {
return (
<div className="header">
<section className="header-container">
<div className="header-section">
<H3> Hi there, I’m</H3>
<H1>Fanny Henriques</H1>
<H3>
Creative Front-End Developer with a background in social science and social work
</H3>
</div>
</section>
<Gallery/>
<Bio/>
</div>
);
};
48 changes: 48 additions & 0 deletions my-portfolio/src/components/sections/LetsTalkSection/LetsTalk.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.talk-card {
width: 100%;
background-color: var(--secondary-color);
}

.talk-card h2 {
align-self: center;
}

.profile-img {
height: 164px;
border-radius: 164px;
margin-top: 16px;
align-self: center;
}

.content-card {
color: var(--primary-color);
padding: 64px 16px;
display: flex;
flex-direction: column;
}

.contact-section {
margin: 64px 10px;
display: flex;
flex-direction: column;
gap: 8px;
}

.icons-container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
gap: 32px;
}

@media (min-width: 481px) {
.content-card {
padding: 128px 24px;
}
.contact-section {
align-items: center;
justify-content: center;
gap: 8px;
}
}
39 changes: 39 additions & 0 deletions my-portfolio/src/components/sections/LetsTalkSection/LetsTalk.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import "./LetsTalk.css";
import profileImg from "../../../assets/profile-img.jpg";
import { H2, BodyTextLarge } from "../../ui/Typography/Typography";
import { IconButton } from "../../ui/Buttons/IconButtons";

export const LetsTalk = () => {

const iconData = [
{ id: 1, iconName: "linkedin", onClick: () => window.open("https://www.linkedin.com", "_blank")
},
{ id: 2, iconName: "github", onClick: () => window.open("https://github.com", "_blank")
},
{ id: 3, iconName: "stackOverflow", onClick: () => window.open("https://stackoverflow.com", "_blank")
},
{ id: 4, iconName: "twitter", onClick: () => window.open("https://twitter.com", "_blank")
},
{ id: 5, iconName: "instagram", onClick: () => window.open("https://www.instagram.com", "_blank")
},
];

return (
<section className="talk-card">
<div className="content-card">
<H2>Lets talk</H2>
<img className="profile-img" src={profileImg} alt="profile picture" />
<div className="contact-section">
<BodyTextLarge>Fanny Henriques</BodyTextLarge>
<BodyTextLarge>+46(0)763 12 33 45</BodyTextLarge>
<BodyTextLarge>sara.svensson@mail.com</BodyTextLarge>
</div>
<div className="icons-container">
{iconData.map(({ id, iconName, onClick }) => (
<IconButton key={id} iconName={iconName} onClick={onClick} />
))}
</div>
</div>
</section>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.projects-container {
grid-column: span 4;
gap: 64px;
}

.projects-container h2 {
margin-bottom: 64px;
}

.project-wrapper {
display: flex;
flex-direction: column;
gap: 64px;
}

.view-more-container {
display: flex;
justify-content: center;
}

/* Breakpoint for tablet */
@media (min-width: 480px) and (max-width: 768px) {
.projects-container {
grid-column: span 8;
gap: 64px;
}
}

/* breakpoint for small desktop */
@media (min-width: 769px) {
.projects-container {
grid-column: span 12;
gap: 64px;
}

.project-wrapper>.project-card:nth-child(even) {
flex-direction: row-reverse;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useState, useEffect } from "react";
import { H2 } from "../../ui/Typography/Typography";
import { Card } from "../Cards/Card.jsx";
import { Grid } from "../../ui/GridLayout/Grid.jsx";
import { images } from "../../ui/Images/Images.jsx";
import { Button } from "../../ui/Buttons/Button.jsx";
import { Tag } from "../../ui/Tags/Tags.jsx";
import projectsData from "../../../data/projects.json";
import iconArrow from "../../../assets/iconArrow.svg";
import "./FeaturedProjects.css";

export const FeaturedProjects = () => {
// display every other card in reversed row on desktop
const [isDesktop, setIsDesktop] = useState(window.innerWidth >= 1024);

useEffect(() => {
const handleResize = () => {
setIsDesktop(window.innerWidth >= 1024);
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);

return (
<Grid background="white">
<div className="projects-container">
<H2>Featured Projects</H2>
<div className="project-wrapper">
{projectsData.projects.map((project, index) => (
<Card
key={project.name}
imageSource={images[project.image] || project.image}
cardTag={project.tags}
cardTitle={project.name}
cardDescription={project.description}
sectionType="project"
netlify={project.netlify}
github={project.github}
isReversed={isDesktop && index % 2 === 1}
>
<div className="tag-container">
{project.tags.map((tag, index) => (
<Tag key={index} sectionType="project" text={tag} />
))}
</div>
</Card>
))}
</div>
<div className="view-more-container">
<Button
text="View More Projects"
isViewMore={true}
sectionType="project"
icon={iconArrow}
href="https://github.com/Fannyhenriques?tab=repositories"
target="_blank"
/>
</div>
</div>
</Grid>
);
};
60 changes: 60 additions & 0 deletions my-portfolio/src/components/sections/SkillsSection/Skills.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.skill-section {
display: flex;
flex-direction: column;
margin: 64px 16px;
gap: 24px;
}

.skill-category {
display: flex;
flex-direction: column;
gap: 16px;
}

.skill-category ul{
list-style-type: none;
margin: 0;
padding: 0;
}

.skill-category li {
list-style-type: none;
font-size: 18px;
margin: 0;
padding: 0;
}

.skill-wrapper {
display: flex;
flex-direction: column;
gap:24px;
}


@media (min-width: 481px) {
.skill-section {
margin: 128px 24px;
}
.skill-category {
justify-content: center;
align-items: center;
}
.skill-category li {
text-align: center;
list-style-type: none;
}
}
@media (min-width: 1024px) {
.skill-wrapper {
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: center;
gap: 24px;
}
.skill-category li {
display: flex;
justify-content: flex-start !important;
align-items: center;
}
}
28 changes: 28 additions & 0 deletions my-portfolio/src/components/sections/SkillsSection/Skills.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import skillsData from "../../../data/skills.json";
import { H2 } from "../../ui/Typography/Typography.jsx";
import { Tag } from "../../ui/Tags/Tags.jsx";
import "./Skills.css";

export const Skills = () => {
return (
<section className="skill-section">
<H2>Skills</H2>
<div className="skill-wrapper">
{skillsData.skills.map((skill) => (
<div key={skill.name} className="skill-category">
<Tag sectionType="skill" text={skill.name} />
<ul>
{typeof skill.tags === "string" ? (
<li>{skill.tags}</li>
) : (
skill.tags.map((tag, index) => (
<li key={index}>{tag}</li>
))
)}
</ul>
</div>
))}
</div>
</section>
);
};
32 changes: 32 additions & 0 deletions my-portfolio/src/components/sections/TechSection/Tech.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.tech-section {
text-align: center;
width: 100%;
background-color: var(--primary-color);
}

.tech-content-section {
color: var(--secondary-color);
padding: 64px 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
}
.tech-content-section p {
max-width: 343px;
}

@media (min-width: 481px) {
.tech-content-section {
padding: 128px 24px;
}
.tech-content-section p {
max-width: 400px;
}
}
@media (min-width: 1024px) {
.tech-content-section p{
max-width: 782px;
}
}
18 changes: 18 additions & 0 deletions my-portfolio/src/components/sections/TechSection/Tech.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import "./Tech.css";
import { H2, BodyText } from "../../ui/Typography/Typography";

export const Tech = () => {
return (
<section className="tech-section">
<div className="tech-content-section">
<H2>Tech</H2>
<BodyText>
HTML, CSS, Flexbox, JavaScript, ES6,
JSX, React, React Hooks, Node.js,
Mongo DB, Web Accessibility, APIs,
mob-programming, pair-programming, GitHub.
</BodyText>
</div>
</section>
);
};
99 changes: 99 additions & 0 deletions my-portfolio/src/components/ui/Buttons/Button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
.project-button,
.article-button {
display: flex;
width: 250px;
height: 48px;
padding: 0px 16px;
justify-content: flex-start;
flex-direction: row;
align-items: center;
font-size: 18px;
border-radius: 12px;
gap: 16px;
text-decoration: none;
font-weight: 500;
}

.project-button {
background-color: var(--primary-color);
color: var(--secondary-color);
}

.project-button:hover {
background-color: var(--accent-color);
}

.article-button {
background-color: var(--secondary-color);
color: var(--primary-color);
}

.article-button:hover {
background-color: var(--accent-color);
color: var(--secondary-color);
}

.project-view-more-button,
.article-view-more-button {
display: flex;
align-items: center;
justify-content: center;
align-self: center;
width: 280px;
height: 48px;
padding: 0px 16px;
gap: 16px;
border-radius: 12px;
font-size: 18px;
margin-top: 64px;
text-decoration: none;
font-weight: 500;
}

.project-view-more-button {
background: var(--secondary-color);
color: var(--primary-color);
border: 2px solid #000;
}

.project-view-more-button:hover {
background-color: var(--accent-color);
color: var(--secondary-color);
}

.article-view-more-button {
background: var(--primary-color);
color: var(--secondary-color);
border: 2px solid #FFF;
}

.article-view-more-button:hover {
background-color: var(--accent-color);
}

.article-button-icon,
.project-button-icon {
width: 32px;
height: 32px;
}

@media (min-width: 375px) {

.project-button,
.article-button {
width: 303px;
}

.project-view-more-button,
.article-view-more-button {
width: 303px;
}
}

@media (min-width: 1024px) {

.project-view-more-button,
.article-view-more-button {
margin-top: 128px;
}
}
20 changes: 20 additions & 0 deletions my-portfolio/src/components/ui/Buttons/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* eslint-disable react/prop-types */
import "./Button.css";

export const Button = ({ text, icon, href, target, sectionType = "default", isViewMore }) => {
const buttonClass = isViewMore
? `${sectionType}-view-more-button`
: `${sectionType}-button`;

return (
<a
className={`button ${buttonClass}`}
href={href}
target={target}
rel={target === "_blank" ? "noopener noreferrer" : undefined}
>
{icon && <img src={icon} alt="icon" className={`${sectionType}-button-icon`} />}
{text}
</a>
);
};
9 changes: 9 additions & 0 deletions my-portfolio/src/components/ui/Buttons/IconButtons.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.icon-button {
all: unset;
cursor: pointer;
}

.icon-only {
width: 32px;
height: 32px;
}
25 changes: 25 additions & 0 deletions my-portfolio/src/components/ui/Buttons/IconButtons.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable react/prop-types */
import "./IconButtons.css";
import iconLinkedin from "../../../assets/iconLinkedin.svg";
import iconTwitter from "../../../assets/iconTwitter.svg";
import iconStackOverflow from "../../../assets/iconStackOverflow.svg"
import iconInstagram from "../../../assets/iconInstagram.svg";
import iconGithub from "../../../assets/iconGithub.svg";

const iconMap = {
linkedin: iconLinkedin,
github: iconGithub,
stackOverflow: iconStackOverflow,
twitter: iconTwitter,
instagram: iconInstagram,
};

export const IconButton = ({ iconName, onClick }) => {
const icon = iconMap[iconName];

return (
<button className="icon-button" onClick={onClick}>
<img src={icon} alt={`${iconName} icon`} className="icon-only" />
</button>
);
};
53 changes: 53 additions & 0 deletions my-portfolio/src/components/ui/GridLayout/Grid.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.grid-section {
display: grid;
grid-template-columns: repeat(4, 1fr);
}

.white-background {
background: var(--secondary-color);
padding: 64px 16px;
}

.black-background {
background: var(--primary-color);
padding: 128px 24px;
}

/* Breakpoint for tablet */
@media (min-width: 480px) and (max-width: 768px) {
.grid-section {
grid-template-columns: repeat(8, 1fr);
}
.white-background {
padding: 128px 24px;
}
}
/* breakpoint for small desktop */
@media (min-width: 769px) and (max-width: 1024px) {
.grid-section {
grid-template-columns: repeat(12, 1fr);
}
}

@media (min-width: 1025px) and (max-width: 1200px) {
.grid-section {
grid-template-columns: repeat(12, 1fr);
}
.white-background {
padding: 128px 64px;
}
.black-background {
padding: 128px 128px;
}
}
@media (min-width: 1200px) {
.grid-section {
grid-template-columns: repeat(12, 1fr);
}
.white-background {
padding: 128px 128px;
}
.black-background {
padding: 128px 128px;
}
}
15 changes: 15 additions & 0 deletions my-portfolio/src/components/ui/GridLayout/Grid.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable react/prop-types */
import "./Grid.css";

export const Grid = ({
background,
children
}) => {
const backgroundClass = background === "black" ? "black-background" : "white-background";

return (
<div className={`${backgroundClass} grid-section`}>
{children}
</div>
);
};
9 changes: 9 additions & 0 deletions my-portfolio/src/components/ui/Images/Images.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import img1 from "../../../assets/img1.jpeg";
import img2 from "../../../assets/img2.jpeg";
import img3 from "../../../assets/img3.jpeg";

export const images = {
img1,
img2,
img3
};
36 changes: 36 additions & 0 deletions my-portfolio/src/components/ui/Tags/Tags.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
.project-tag,
.article-tag,
.skill-tag {
border-radius: 4px;
margin: 0;
font-size: 16px;
text-align: center;
max-width: 100%;
}

.project-tag {
padding: 1px 4px;
border: 1px solid #000;
min-width: 82px;
}

.article-tag {
border: 1px solid #fff;
padding: 2px 6px;
width: 142px;
height: 28px;
}

.skill-tag {
width: 180px;
border: 1px solid #000;
height: 24px;
padding: 1px 4px;
}

/* breakpoint tablet and up */
@media (min-width: 481px) {
.project-tag {
width: 142px;
}
}
8 changes: 8 additions & 0 deletions my-portfolio/src/components/ui/Tags/Tags.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* eslint-disable react/prop-types */
import "./Tags.css";

export const Tag = ({ sectionType, text }) => {
return (
<p className={`${sectionType}-tag`}>{text}</p>
);
};
104 changes: 104 additions & 0 deletions my-portfolio/src/components/ui/Typography/Typography.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/* default styling (moible 375px - figmadesign-mobile) */
.heading1 {
font-size: 60px;
font-weight: 700;
line-height: 133.333%;
margin: 0;
}

.heading2 {
font-size: 60px;
font-weight: 700;
margin: 0;
text-align: center;
}

.heading3 {
font-size: 24px;
font-weight: 500;
margin: 0;
}

.bodytext {
font-size: 16px;
font-weight: 400;
margin: 0;
}

.bodytext-medium {
margin: 0;
font-size: 18px;
}

.bodytext-large {
font-size: 24px;
font-weight: 500;
margin: 0;
}

/* breakpoint for small mobile */
@media (max-width: 375px){
.heading1 {
font-size: 50px;
}
.heading2 {
font-size: 55px;
text-align: center;
}
.heading3 {
font-size: 20px;
}
.bodytext-large {
font-size: 20px;
}
}

/* Breakpoint for tablet */
@media (min-width: 480px) and (max-width: 768px) {
.heading1 {
font-size: 80px;
line-height: 125%;
}
}

/* breakpoint for small desktop */
@media (min-width: 769px) and (max-width: 1024px) {
.heading1 {
font-size: 100px;
line-height: normal;
}
.heading2 {
font-size: 80px;
text-align: center;
}
.heading3 {
font-size: 30px;
}
.bodytext {
font-size: 18px;
}
.bodytext-large {
font-size: 30px;
font-weight: 600;
}
}
@media (min-width: 1024px) {
.heading1 {
font-size: 100px;
line-height: normal;
}
.heading2 {
font-size: 80px;
text-align: center;
}
.heading3 {
font-size: 30px;
}
.bodytext {
font-size: 18px;
}
.bodytext-large {
font-size: 30px;
font-weight: 600;
}
}
14 changes: 14 additions & 0 deletions my-portfolio/src/components/ui/Typography/Typography.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* eslint-disable react/prop-types */
import "./Typography.css";

export const H1 = ({ children }) => <h1 className="heading1">{children}</h1>;

export const H2 = ({ children }) => <h2 className="heading2">{children}</h2>;

export const H3 = ({ children }) => <h3 className="heading3">{children}</h3>;

export const BodyText = ({ children }) => <p className="bodytext">{children}</p>;

export const BodyTextMedium = ({children}) => <p className="bodytext-medium">{children}</p>

export const BodyTextLarge = ({ children }) => <p className="bodytext-large">{children}</p>
25 changes: 25 additions & 0 deletions my-portfolio/src/data/articles.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"articles": [
{
"name": "How to stop being scared: Learning to love code",
"image": "img2",
"tags": "July 14th",
"description": "The chat bot app is a conversational AI-powered tool designed to enhance user experience by providing instant, personalized, and automated responses to user inquiries.",
"link": "https://www.metaltoad.com/blog/how-i-learned-stop-worrying-and-love-code"
},
{
"name": "How to stop being scared and learn to love code",
"image": "img1",
"tags": "July 14th",
"description": "The chat bot app is a conversational AI-powered tool designed to enhance user experience by providing instant, personalized, and automated responses to user inquiries.",
"link": "https://www.metaltoad.com/blog/how-i-learned-stop-worrying-and-love-code"
},
{
"name": "How to learn to love code",
"image": "img3",
"tags": "July 14th",
"description": "The chat bot app is a conversational AI-powered tool designed to enhance user experience by providing instant, personalized, and automated responses to user inquiries.",
"link": "https://www.metaltoad.com/blog/how-i-learned-stop-worrying-and-love-code"
}
]
}
56 changes: 56 additions & 0 deletions my-portfolio/src/data/projects.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"projects": [
{
"name": "Chatbot",
"image": "img2",
"tags": [
"HTML5",
"CSS3",
"JavaScript"
],
"description": "The chat bot app is a conversational AI-powered tool designed to enhance user experience by providing instant, personalized, and automated responses to user inquiries.",
"netlify": "https://moviechatbot.netlify.app/",
"github": "https://github.com/Fannyhenriques/project-chatbot"
},
{
"name": "Weather app",
"image": "img1",
"tags": [
"HTML5",
"CSS3",
"JavaScript",
"APIs"
],
"description": "The chat bot app is a conversational AI-powered tool designed to enhance user experience by providing instant, personalized, and automated responses to user inquiries.",
"netlify": "https://master--weathertheweatherapp.netlify.app",
"github": "https://github.com/Fannyhenriques/project-weather-app"
},
{
"name": "Project Survey",
"image": "img3",
"tags": [
"HTML5",
"CSS3",
"JavaScript",
"React"
],
"description": "The chat bot app is a conversational AI-powered tool designed to enhance user experience by providing instant, personalized, and automated responses to user inquiries.",
"netlify": "https://project-joy-boost-survey.netlify.app/",
"github": "https://github.com/joheri1/project-survey-vite"
},
{
"name": "Happy-thoughts",
"image": "img2",
"tags": [
"HTML5",
"CSS3",
"JavaScript",
"APIs",
"React"
],
"description": "The chat bot app is a conversational AI-powered tool designed to enhance user experience by providing instant, personalized, and automated responses to user inquiries.",
"netlify": "https://fh-happythoughtsapp.netlify.app/",
"github": "https://github.com/Fannyhenriques/project-happy-thoughts-vite"
}
]
}
43 changes: 43 additions & 0 deletions my-portfolio/src/data/skills.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"skills": [
{
"name": "Code",
"tags": [
"HTML5",
"CSS3",
"Javascript ES6",
"React",
"Styled Components",
"GitHub"
]
},
{
"name": "Toolbox",
"tags": [
"Atom",
"Postman",
"Adobe Photoshop",
"Adobe Illustrator",
"Figma",
"Keynote",
"Slack"
]
},
{
"name": "Upcoming",
"tags": [
"Node.js"
]
},
{
"name": "More",
"tags": [
"Branding",
"Strategy",
"Process Design",
"Concept Development",
"Agile methodology"
]
}
]
}
Empty file added my-portfolio/src/index.css
Empty file.
11 changes: 11 additions & 0 deletions my-portfolio/src/main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import React from 'react';
import './index.css'
import { App } from './App.jsx'

createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)
7 changes: 7 additions & 0 deletions my-portfolio/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
plugins: [react()],
})
6 changes: 6 additions & 0 deletions package-lock.json
27 changes: 0 additions & 27 deletions projects.json

This file was deleted.