-
Notifications
You must be signed in to change notification settings - Fork 50
Sofie Portfolio #11
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
base: main
Are you sure you want to change the base?
Sofie Portfolio #11
Changes from 12 commits
88122a7
f659f8c
139e53f
42afa76
90f9cb3
7d92c4e
3f899eb
42a2568
1edd8d3
0390714
4958963
03a2015
2ae169c
a258c8a
b294ed0
24f8196
c5e727e
8f4c6c5
e31ca93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| # Portfolio | ||
| This is my personal portfolio built with React. It’s fully responsive, accessible, and styled with styled-components. It highlights my tech skills and projects, with links to GitHub and Netlify. The design follows a Figma layout provided by Technigo to keep everything clean and consistent. | ||
|
|
||
| https://ssofiejohansson.netlify.app/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,39 @@ | ||
| <!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" /> | ||
| <title>Portfolio</title> | ||
| </head> | ||
| <body> | ||
| <div id="root"></div> | ||
| <script type="module" src="/src/main.jsx"></script> | ||
| </body> | ||
| </html> | ||
|
|
||
| <head> | ||
| <meta charset="UTF-8" /> | ||
| <link | ||
| rel="icon" | ||
| type="image/svg+xml" | ||
| href="/disco.png" | ||
| /> | ||
| <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=Montserrat:ital,wght@0,100..900;1,100..900&family=Mynerve&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&family=Urbanist:ital,wght@0,100..900;1,100..900&display=swap" | ||
| rel="stylesheet" | ||
| > | ||
| <meta | ||
| name="viewport" | ||
| content="width=device-width, initial-scale=1.0" | ||
| /> | ||
| <title>Sofie Johansson</title> | ||
| </head> | ||
|
|
||
| <body> | ||
| <div id="root"></div> | ||
| <script | ||
| type="module" | ||
| src="/src/main.jsx" | ||
| ></script> | ||
| </body> | ||
|
|
||
| </html> |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,23 @@ | ||
| import { Nav } from "./assets/sections/Nav" | ||
| import { Header } from "./assets/sections/Header" | ||
| import { Projects } from "./assets/sections/Projects" | ||
| import { Skills } from "./assets/sections/Skills" | ||
| import { Footer } from "./assets/sections/Footer" | ||
| import { Blog } from "./assets/sections/Blog" | ||
|
|
||
| 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> | ||
| <Nav /> | ||
| <Header /> | ||
| <main> | ||
|
|
||
| <Projects /> | ||
| <Skills /> | ||
| <Blog /> | ||
|
|
||
| </main> | ||
| <Footer /> | ||
| </> | ||
| ) | ||
| } |
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import { ScrollAnimation } from "./components/ScrollAnimation"; | ||
| import { useState } from "react"; | ||
| import styled from "styled-components"; | ||
|
|
||
| const BlogContainer = styled.section` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 20px; | ||
| padding: 50px; | ||
| margin: 0 auto; | ||
|
|
||
| @media (min-width: 768px) { | ||
| flex-direction: row; | ||
| gap: 40px; | ||
| justify-content: center; | ||
| } | ||
| `; | ||
|
|
||
| const BlogPost = styled.article` | ||
| background-color: var(--secondary); | ||
| border: 1px solid var(--primary); | ||
| padding: 20px; | ||
| box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); | ||
| `; | ||
|
|
||
| const BlogExcerpt = styled.p` | ||
| font-family: var(--font-family-text); | ||
| color: var(--font-secondary); | ||
| margin-bottom: 10px; | ||
| `; | ||
|
|
||
| const BlogContent = styled.p` | ||
| font-family: var(--font-family-text); | ||
| color: var(--font-secondary); | ||
| margin-top: 10px; | ||
| display: ${({ isVisible }) => (isVisible ? "block" : "none")}; | ||
| `; | ||
|
|
||
| const ToggleButton = styled.button` | ||
| font-family: var(--font-family-text); | ||
| color: var(--primary); | ||
| background: none; | ||
| border: 1px solid var(--primary); | ||
| border-radius: 8px; | ||
| padding: 8px 16px; | ||
| cursor: pointer; | ||
|
|
||
| &:hover { | ||
| background-color: var(--primary); | ||
| color: var(--secondary); | ||
| } | ||
| `; | ||
|
|
||
| export const Blog = () => { | ||
| const [isExpanded, setIsExpanded] = useState(false); | ||
|
|
||
| const toggleContent = () => { | ||
| setIsExpanded(!isExpanded); | ||
| }; | ||
|
|
||
| return ( | ||
| <ScrollAnimation> | ||
| <h2>Blog posts</h2> | ||
| <BlogContainer> | ||
| <BlogPost> | ||
| <h3>Blog title 1</h3> | ||
| <BlogExcerpt> | ||
| Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. | ||
| </BlogExcerpt> | ||
| <BlogContent isVisible={isExpanded}> | ||
| Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. | ||
| </BlogContent> | ||
| <ToggleButton onClick={toggleContent}> | ||
| {isExpanded ? "Read Less" : "Read More"} | ||
| </ToggleButton> | ||
| </BlogPost> | ||
|
|
||
| <BlogPost> | ||
| <h3>Blog title 2</h3> | ||
| <BlogExcerpt> | ||
| Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. | ||
| </BlogExcerpt> | ||
| <BlogContent isVisible={isExpanded}> | ||
| Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. | ||
| </BlogContent> | ||
| <ToggleButton onClick={toggleContent}> | ||
| {isExpanded ? "Read Less" : "Read More"} | ||
| </ToggleButton> | ||
| </BlogPost> | ||
|
|
||
| </BlogContainer> | ||
| </ScrollAnimation> | ||
| ); | ||
| }; |
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Love how it looks! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import profileImage from './img/footer-img.png'; | ||
| import styled from 'styled-components'; | ||
| import { Icons } from './components/Icons'; | ||
| import { ProfileImg } from './Header'; | ||
|
|
||
| const FooterContainer = styled.section` | ||
|
||
| display: flex; | ||
| flex-direction: column-reverse; | ||
|
|
||
| justify-content: center; | ||
| align-items: center; | ||
| padding: 60px 60px; | ||
| background: #C35132; | ||
| color: #FFF; | ||
| height: 100vh; | ||
|
|
||
| @media (min-width: 768px) { | ||
| flex-direction: row; | ||
| gap: 100px; | ||
|
|
||
| } | ||
| ` | ||
|
|
||
| const FooterBio = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| gap: 2px; | ||
|
|
||
| @media (min-width: 768px) { | ||
| align-items: flex-start; | ||
| } | ||
| ` | ||
|
|
||
| const FooterHeading = styled.h2` | ||
| color: var(--secondary); | ||
| text-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); | ||
| font-family: 'Mynerve'; | ||
| font-size: 50px; | ||
| font-style: normal; | ||
| font-weight: 400; | ||
| letter-spacing: -5.76px; | ||
| padding-top: 50px; | ||
|
|
||
| @media (min-width: 768px) { | ||
| font-size: 70px; | ||
| padding-top: 0px; | ||
| } | ||
| ` | ||
|
|
||
| export const Footer = () => { | ||
| return ( | ||
| <> | ||
| <FooterContainer id='contact'> | ||
| <ProfileImg src={profileImage} alt="Profile image" /> | ||
| <FooterBio> | ||
| <FooterHeading>Let's Talk!</FooterHeading> | ||
| <p>Sofie Johansson</p> | ||
| <p>+46(0)72 442 34 97</p> | ||
| <p>[email protected]</p> | ||
| <Icons color="var(--secondary)" /> | ||
| </FooterBio> | ||
| </FooterContainer> | ||
| </> | ||
| ) | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great layout and well executed with all the positioning of all the different elements. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| import React from 'react'; | ||
| import profileImage from './img/header-img.png'; | ||
| import styled from 'styled-components'; | ||
| import { Icons } from './components/Icons'; | ||
|
|
||
| const HeaderContainer = styled.header` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| display: flex; | ||
| flex-direction: column; | ||
| padding: 50px; | ||
| gap: 20px; | ||
| padding-top: 100px; | ||
|
|
||
| @media (min-width: 768px) { | ||
| height: 100vh; | ||
|
||
| justify-content: center; | ||
| align-items: center; | ||
| } | ||
| `; | ||
|
|
||
| const HeaderTitle = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| justify-content: center; | ||
| align-items: left; | ||
| text-align: left; | ||
| `; | ||
|
|
||
| const HeaderIntro = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| padding-top: 10px; | ||
|
|
||
| @media (min-width: 768px) { | ||
| max-width: 500px; | ||
| gap: 10px; | ||
| } | ||
| `; | ||
|
|
||
| const HeaderBio = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 20px; | ||
| justify-content: center; | ||
| align-items: center; | ||
|
|
||
| @media (min-width: 768px) { | ||
| flex-direction: row; | ||
| gap: 80px; | ||
| } | ||
| `; | ||
|
|
||
| export const ProfileImg = styled.img` | ||
| width: 250px; | ||
| height: 260px; | ||
| border-radius: 50%; | ||
| margin-top: 20px; | ||
| padding: 10px; | ||
|
|
||
| @media (min-width: 768px) { | ||
| width: 300px; | ||
| height: 310px; | ||
| margin-top: 0px; | ||
| } | ||
| `; | ||
|
|
||
| export const Header = () => { | ||
| return ( | ||
| <> | ||
| <HeaderContainer id="about"> | ||
| <HeaderTitle> | ||
| <h1>Frontend Developer</h1> | ||
| <h2>+ former hospitality pro</h2> | ||
| </HeaderTitle> | ||
| <HeaderBio> | ||
| <HeaderIntro> | ||
| <h3>Hi, I'm Sofie</h3> | ||
| <p> | ||
| For the past decade I’ve worked in hospitality, lived in 10 different cities and soaked up all I could from life, cultures and people. But I’ve always been curious about tech and I finally decided to stop dreaming about it and just go for it. | ||
| So now I’m deep into JavaScript, React and building very cool things at Technigo — and honestly, I’m loving it. I’m looking for a frontend role where I can keep growing, build more cool stuff and bring some positive energy to a team. | ||
| </p> | ||
| <Icons color="var(--primary)" /> | ||
| </HeaderIntro> | ||
| <ProfileImg src={profileImage} alt="Profile image" /> | ||
| </HeaderBio> | ||
| </HeaderContainer> | ||
| </> | ||
| ); | ||
| }; | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! A understandable and clear structure!