Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
02d1d0d
add folder and files for project components
gabriellaberko Nov 14, 2025
b209957
add project section components and pass in project data
gabriellaberko Nov 18, 2025
2f192aa
add tags to project component
gabriellaberko Nov 19, 2025
7221bc5
add Netlify and GitHub links to project component
gabriellaberko Nov 19, 2025
9adb1df
add skill section
gabriellaberko Nov 19, 2025
2258d81
add secondary button in project section
gabriellaberko Nov 19, 2025
518e3eb
add some content
gabriellaberko Nov 19, 2025
72e1c99
add contact section and profile img component
gabriellaberko Nov 19, 2025
4e17179
add tech section
gabriellaberko Nov 19, 2025
d8ba810
add typography component with prop to change element type
gabriellaberko Nov 20, 2025
97fae43
change text elements to Typography component + add intro section
gabriellaberko Nov 21, 2025
e4ae751
re-organize in folders
gabriellaberko Nov 21, 2025
fda4eba
add article component and article section
gabriellaberko Nov 21, 2025
2091bdb
install styled components
gabriellaberko Nov 21, 2025
32488ab
style intro section and buttons
gabriellaberko Nov 24, 2025
bb749e0
style skills section
gabriellaberko Nov 24, 2025
f54faf8
additional styling
gabriellaberko Nov 24, 2025
fb663fb
center card container
gabriellaberko Nov 25, 2025
8675200
refactor Typography component and styling
gabriellaberko Nov 25, 2025
df2fec3
add height prop to Img component and set default value
gabriellaberko Nov 25, 2025
a647cb5
refactor section styling components + minor styling fixes
gabriellaberko Nov 25, 2025
212bd29
add icon container to intro section + some styling
gabriellaberko Nov 25, 2025
347a5e9
add OG tags
gabriellaberko Nov 25, 2025
a073167
add animations on sections + update images
gabriellaberko Nov 27, 2025
fb89949
configure Vite build and Netlify deployment
gabriellaberko Nov 27, 2025
bc5e447
Fix Typography filename casing and update imports for Netlify deploy
gabriellaberko Nov 27, 2025
ce50dde
Temporary rename to tempfile to fix casing
gabriellaberko Nov 27, 2025
4b7101b
Rename Typography file to correct casing
gabriellaberko Nov 27, 2025
1ea03a3
change name of Typography component since previous commits did not work
gabriellaberko Nov 27, 2025
1182b79
fix issue with icon names cache
gabriellaberko Nov 27, 2025
0bb6d74
try changing file names again for icons
gabriellaberko Nov 27, 2025
2fa6436
implement modal popup modal for articles
gabriellaberko Nov 27, 2025
7e6268e
make separations of article sections
gabriellaberko Nov 27, 2025
7955e96
add a GlobalStyles component + fix error of missing keys
gabriellaberko Nov 27, 2025
606fc53
add Readme info + small styling modifications for the cards
gabriellaberko Nov 28, 2025
38ad080
add improvements for accessibility
gabriellaberko Nov 28, 2025
92a3941
modify aria labels for icons
gabriellaberko Nov 28, 2025
52ad42c
modify aria labels
gabriellaberko Nov 28, 2025
c9a6876
center project section heading
gabriellaberko Nov 28, 2025
6e66f09
refactor button styling and components
gabriellaberko Nov 28, 2025
5e5e306
add font + update media query + install Swiper + clean code
gabriellaberko Nov 28, 2025
15a0e46
add favicon + minor styling
gabriellaberko Nov 28, 2025
24301b3
update content in tech section
gabriellaberko Nov 28, 2025
086683b
clean code + correct typo
gabriellaberko Dec 1, 2025
2643254
fix issue with too high theshold for observer on small screens
gabriellaberko Dec 1, 2025
052aea4
update folder structure and use media breakpoint from theme in animat…
gabriellaberko Dec 4, 2025
7ff9512
minor styling changes in typography
gabriellaberko Dec 4, 2025
59b1f69
add accent and outline in colors in theme + use them in icons and oth…
gabriellaberko Dec 4, 2025
ebb42a3
update GlobalStyle component with theme props
gabriellaberko Dec 4, 2025
cf1467e
change porject card layout on desktop and change animations accordingly
gabriellaberko Dec 5, 2025
a85932b
update screen size reactively on screen size changes
gabriellaberko Dec 5, 2025
5367374
minor styling changes + import hooks
gabriellaberko Dec 5, 2025
f68b531
update images
gabriellaberko Dec 5, 2025
fd57b0d
change margin
gabriellaberko Dec 5, 2025
d38b4a9
button styling
gabriellaberko Dec 5, 2025
1b31bb6
change typography sizes
gabriellaberko Dec 8, 2025
0bb86ba
add new project
gabriellaberko Dec 8, 2025
982992c
update project image
gabriellaberko Dec 8, 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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
# Portfolio
A portfolio showcasing projects, built with React.

# Netlify link:
https://gabriellaberkowicz.netlify.app/

# Tech
- JavaScript
- React & Styled components
- CSS + HTML

13 changes: 11 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Portfolio</title>
<title>Portfolio - Gabriella Berkowicz</title>
<!-- OG:tags -->
<meta property="og:title" content="Portfolio - Gabriella Berkowicz">
<meta property="og:type" content="website">
<meta property="og:url" content="https://gabriellaberkowicz.netlify.app/">
<meta property="og:image" content="/og.image">
<meta property="og:description" content="Frontend Developer skilled in JavaScript, TypeScript, React, and Node.js. With a background in web analytics, I combine data-driven thinking with modern web development.">
<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">
</head>
<body>
<div id="root"></div>
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"styled-components": "^6.1.19",
"swiper": "^12.0.3"
},
"devDependencies": {
"@eslint/js": "^9.21.0",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"@vitejs/plugin-react": "^4.7.0",
"babel-plugin-styled-components": "^2.1.4",
"eslint": "^9.21.0",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-react-refresh": "^0.4.19",
Expand Down
7 changes: 7 additions & 0 deletions public/cross-white.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 public/favicon.png
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 public/happy-thoughts.png
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 public/og.image.2.png
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 public/og.image.png
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 public/profile-filled.png
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 public/profile.png
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 public/quiz-game.png
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 public/recipe-library.png
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 public/weather-app.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 23 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
import React from 'react';
import { ThemeProvider } from 'styled-components';
import { theme } from './style/Theme.styled';
import { GlobalStyle } from './style/GlobalStyle';
import { IntroSection } from './components/sections/IntroSection';
import { ProjectSection } from './components/sections/ProjectSection';
import { SkillsSection } from './components/sections/SkillsSection';
import { ContactSection } from './components/sections/ContactSection';
import { TechSection } from './components/sections/TechSection';
import { ArticleSection } from './components/sections/ArticleSection';


export const App = () => {
return (
<>
<h1>Portfolio</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptatem, laborum! Maxime animi nostrum facilis distinctio neque labore consectetur beatae eum ipsum excepturi voluptatum, dicta repellendus incidunt fugiat, consequatur rem aperiam.</p>
<ThemeProvider theme={theme}>
<GlobalStyle />
<IntroSection />
<main>
<SkillsSection />
<ProjectSection />
<TechSection />
<ArticleSection />
</main>
<ContactSection />
</ThemeProvider>
</>
)
}
14 changes: 14 additions & 0 deletions src/components/animations/AnimatedProjectSection.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import { AnimatedSection } from "./AnimatedSection";
import { theme } from "../../style/Theme.styled";

export const AnimatedProjectSection = ({ children }) => {

const isBigScreen = window.matchMedia(theme.media.desktop).matches;

return (
!isBigScreen
? (<AnimatedSection direction="up">{children}</AnimatedSection>)
: (children)
);
}
38 changes: 38 additions & 0 deletions src/components/animations/AnimatedSection.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useRef, useEffect, useState } from "react";
import { StyledAnimatedSection } from "./AnimatedSection.styled";
import { theme } from "../../style/Theme.styled";


export const AnimatedSection = ({ children, direction = "up" }) => {

// create a reference to a DOM element. Becomes the actual DOM node so the IntersectionObserver can observe it.
const ref = useRef();

// the visibility control (defaulting to false), making the component dynamic. When we update the value of visible by calling setVisible, React will re-render the component
const [visible, setVisible] = useState(false);

// to change threshold value depending on screen size
const isBigScreen = window.matchMedia(theme.media.tablet).matches;
const threshold = isBigScreen ? 0.3 : 0.1;


// observes if the referenced element comes into view (becomes visible) - then call setVisible(true);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) setVisible(true);
},
{ threshold }
);
if (ref.current) observer.observe(ref.current);

return () => observer.disconnect();
}, []);


return (
<StyledAnimatedSection ref={ref} visible={visible} direction={direction}>
{children}
</StyledAnimatedSection>
);
};
14 changes: 14 additions & 0 deletions src/components/animations/AnimatedSection.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import styled from "styled-components";

const slideDirections = {
up: 'translateY(50px)',
down: 'translateY(-50px)',
left: 'translateX(-50px)',
right: 'translateX(50px)',
};

export const StyledAnimatedSection = styled.div`
opacity: ${({ visible }) => (visible ? 1 : 0)};
transform: ${({ direction, visible }) => visible ? 'translate(0, 0)' : slideDirections[direction] || 'translateY(50px)'};
transition: all 1s cubic-bezier(0.25, 0.1, 0.25, 1);
`;
27 changes: 27 additions & 0 deletions src/components/buttons/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { StyledButton, StyledLinkButton } from './Button.styled';


export const LinkButton = ({ variant="primaryBtn", link, children, ...props }) => {
return (
<StyledLinkButton
variant={variant}
href={link}
target="_blank"
{...props}
>
{children}
</StyledLinkButton>
);
}

export const Button = ({ variant="secondaryBtn", children, ...props} ) => {
return (
<StyledButton
variant="secondaryBtn"
{...props}
>
{children}
</StyledButton>
);
}
43 changes: 43 additions & 0 deletions src/components/buttons/Button.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import styled from "styled-components";

export const StyledLinkButton = styled.a`
color: ${(props) => props.theme.colors[props.variant]?.text};
background-color: ${(props) => props.theme.colors[props.variant]?.bg};
outline: 2px solid ${(props) => props.theme.colors[props.variant]?.outline};
border-radius: 12px;
padding: 8px 16px;
text-decoration: none;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: transform 0.25s cubic-bezier(.2,.8,.2,1),
box-shadow 0.25s cubic-bezier(.2,.8,.2,1);

&:hover{
transform: translateY(-3px) scale(1.03);
box-shadow: 0 4px 6px rgba(0,0,0,0.12);
}

@media ${(props) => props.theme.media.desktop} {
flex: 1;
}
`;


export const StyledButton = styled.button`
color: ${(props) => props.theme.colors[props.variant]?.text};
background-color: ${(props) => props.theme.colors[props.variant]?.bg};
outline: 2px solid ${(props) => props.theme.colors[props.variant]?.outline};
border:none;
border-radius: 12px;
padding: 8px 16px;
transition: transform 0.25s cubic-bezier(.2,.8,.2,1),
box-shadow 0.25s cubic-bezier(.2,.8,.2,1);
cursor: pointer;

&:hover{
transform: translateY(-3px) scale(1.03);
box-shadow: 0 4px 6px rgba(0,0,0,0.12);
}
`;
26 changes: 26 additions & 0 deletions src/components/cards/Article.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { Typography } from '../typography/CustomTypography';
import { Button } from '../buttons/Button';
import { Img } from '../images/Img';
import { StyledCardDiv, StyledTextDivFaded } from './Card.styled';


export const Article = ({ article, onOpen }) => {
return (
<StyledCardDiv>
<Img src={article.image} alt="article image" />
<div className="tags">
<Typography>{article.tag}</Typography>
</div>
<StyledTextDivFaded>
<Typography as="h3">{article.title}</Typography>
{article.sections.map((section, index) => (
<Typography key={index}>{section}</Typography>
))}
</StyledTextDivFaded>
<Button onClick={onOpen}>
View article
</Button>
</StyledCardDiv>
);
}
94 changes: 94 additions & 0 deletions src/components/cards/Card.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import styled from "styled-components";

export const StyledCardDiv = styled.div`
background-color: #ffffff;
border-radius: 12px;
padding: 16px;
overflow: hidden;
display: flex;
flex-direction: column;
flex: 1;
gap: 24px;
transition: transform 0.25s ease;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
max-width: 400px;
/* width: 100%; */

@media ${(props) => props.theme.media.desktop} {
width: initial;
}

&:hover {
transform: scale(1.02);
box-shadow:
0 0 6px rgba(253, 111, 0, 0.15),
0 8px 20px rgba(0, 0, 0, 0.10);
}
`;

export const StyledProjectCardDiv = styled(StyledCardDiv)`
max-width: initial;
width: initial;

@media ${(props) => props.theme.media.desktop} {
flex-direction: row;
justify-content: space-around;
gap: 12px;
box-shadow: none;
padding: 32px 12px;
flex-direction: ${({ reverse }) => (reverse ? "row-reverse" : "row")}; //reverse the content for every second project
}
`;


export const StyledContentDiv = styled.div`
display: flex;
flex-direction: column;
gap: 18px;
flex: 1;
`;

export const StyledTextDiv = styled.div `
overflow: hidden;
padding-bottom: 1em;
position: relative;
`;

export const StyledTextDivFaded = styled(StyledTextDiv)`
max-height: 270px;

// fade end of text content
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 3em;
pointer-events: none;
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,1) 100%);
}
`;

export const StyledButtonDiv = styled.div `
display: flex;
flex-direction: column;
align-items: column;
gap: 16px;

@media ${(props) => props.theme.media.desktop} {
flex-direction: row;
width: 70%;
}
`;

export const StyledProjectContentWrapper = styled.div`
display:flex;
flex-direction: column;
flex: 1;
justify-content: space-between;
@media ${(props) => props.theme.media.desktop} {
max-width: 50%;
margin: 12px 0;
}
`;
16 changes: 16 additions & 0 deletions src/components/cards/CardContainer.styled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import styled from "styled-components";

export const StyledCardContainer = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
grid-gap: 16px;
`;

export const StyledProjectCardContainer = styled(StyledCardContainer)`
@media ${(props) => props.theme.media.desktop} {
display: flex;
flex-direction: column;
gap: 42px;
margin-top: 24px;
}
`;
Loading