diff --git a/README.md b/README.md index 200f4282..14cff874 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ # Portfolio + +A personal portfolio website built with React and styled-components to showcase my work, skills, and background as a frontend developer. The site also includes a dedicated section for upcoming articles, which I plan to publish soon. + +https://tavanthiry-portfolio.netlify.app diff --git a/index.html b/index.html index 6676fb2d..7c9a5dfb 100644 --- a/index.html +++ b/index.html @@ -3,11 +3,18 @@ + + + - Portfolio + + My Portfolio
+ diff --git a/package.json b/package.json index 48911600..b4042384 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ }, "dependencies": { "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "react-helmet": "^6.1.0", + "react-icons": "^5.5.0", + "styled-components": "^6.1.18" }, "devDependencies": { "@eslint/js": "^9.21.0", diff --git a/src/App.jsx b/src/App.jsx index a161d8d3..69eca96a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,30 @@ +import { ThemeProvider } from "styled-components"; +import theme from "./styles/Theme"; +import GlobalStyles from "./styles/GlobalStyles" +import Header from "./components/Header" +import IntroSection from "./sections/IntroSection" +import TechSection from "./sections/TechSection" +import ProjectSection from "./sections/ProjectSection" +import SkillsSection from "./sections/SkillsSection" +import ArticleSection from "./sections/ArticleSection" +import FooterSection from "./sections/FooterSection" + + export const App = () => { return ( <> -

Portfolio

-

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.

+ + +
+ + + + + + + ) } + +export default App diff --git a/src/assets/images/AI_Prompts.webp b/src/assets/images/AI_Prompts.webp new file mode 100644 index 00000000..5d57858a Binary files /dev/null and b/src/assets/images/AI_Prompts.webp differ diff --git a/src/assets/images/Adobe_Logo.svg b/src/assets/images/Adobe_Logo.svg new file mode 100644 index 00000000..ab8b51f2 --- /dev/null +++ b/src/assets/images/Adobe_Logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/Asana_Logo.svg b/src/assets/images/Asana_Logo.svg new file mode 100644 index 00000000..bdc2f6ad --- /dev/null +++ b/src/assets/images/Asana_Logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/images/CSS_Logo.svg b/src/assets/images/CSS_Logo.svg new file mode 100644 index 00000000..681f269e --- /dev/null +++ b/src/assets/images/CSS_Logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/Collaboration.webp b/src/assets/images/Collaboration.webp new file mode 100644 index 00000000..1e03d394 Binary files /dev/null and b/src/assets/images/Collaboration.webp differ diff --git a/src/assets/images/Creative_Collabs_Website.webp b/src/assets/images/Creative_Collabs_Website.webp new file mode 100644 index 00000000..02d23922 Binary files /dev/null and b/src/assets/images/Creative_Collabs_Website.webp differ diff --git a/src/assets/images/Figma_Logo.svg b/src/assets/images/Figma_Logo.svg new file mode 100644 index 00000000..b65686f9 --- /dev/null +++ b/src/assets/images/Figma_Logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/assets/images/Github_Icon.svg b/src/assets/images/Github_Icon.svg new file mode 100644 index 00000000..471e200f --- /dev/null +++ b/src/assets/images/Github_Icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/Github_Logo.svg b/src/assets/images/Github_Logo.svg new file mode 100644 index 00000000..3006efdf --- /dev/null +++ b/src/assets/images/Github_Logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/HTML_Logo.svg b/src/assets/images/HTML_Logo.svg new file mode 100644 index 00000000..b390dfd4 --- /dev/null +++ b/src/assets/images/HTML_Logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/Happy_Thoughts.webp b/src/assets/images/Happy_Thoughts.webp new file mode 100644 index 00000000..d49b602d Binary files /dev/null and b/src/assets/images/Happy_Thoughts.webp differ diff --git a/src/assets/images/Illustrative_background.png b/src/assets/images/Illustrative_background.png new file mode 100644 index 00000000..9662786d Binary files /dev/null and b/src/assets/images/Illustrative_background.png differ diff --git a/src/assets/images/Javascript_Logo.svg b/src/assets/images/Javascript_Logo.svg new file mode 100644 index 00000000..e50b74fa --- /dev/null +++ b/src/assets/images/Javascript_Logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/Notion_Logo.svg b/src/assets/images/Notion_Logo.svg new file mode 100644 index 00000000..e3bf1afe --- /dev/null +++ b/src/assets/images/Notion_Logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/images/React_Logo.svg b/src/assets/images/React_Logo.svg new file mode 100644 index 00000000..a68fa705 --- /dev/null +++ b/src/assets/images/React_Logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/Slack_Logo.svg b/src/assets/images/Slack_Logo.svg new file mode 100644 index 00000000..1f1077e6 --- /dev/null +++ b/src/assets/images/Slack_Logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/images/Storytelling.webp b/src/assets/images/Storytelling.webp new file mode 100644 index 00000000..44b313fe Binary files /dev/null and b/src/assets/images/Storytelling.webp differ diff --git a/src/assets/images/Sustainability_Housing.webp b/src/assets/images/Sustainability_Housing.webp new file mode 100644 index 00000000..90c43e60 Binary files /dev/null and b/src/assets/images/Sustainability_Housing.webp differ diff --git a/src/assets/images/TaskManagement.webp b/src/assets/images/TaskManagement.webp new file mode 100644 index 00000000..ecbc22e4 Binary files /dev/null and b/src/assets/images/TaskManagement.webp differ diff --git a/src/assets/images/TavanThiry.webp b/src/assets/images/TavanThiry.webp new file mode 100644 index 00000000..3145f987 Binary files /dev/null and b/src/assets/images/TavanThiry.webp differ diff --git a/src/assets/images/Typescript_Logo.svg b/src/assets/images/Typescript_Logo.svg new file mode 100644 index 00000000..853c4871 --- /dev/null +++ b/src/assets/images/Typescript_Logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/Weather_App.webp b/src/assets/images/Weather_App.webp new file mode 100644 index 00000000..ceec2211 Binary files /dev/null and b/src/assets/images/Weather_App.webp differ diff --git a/src/assets/images/Web_Accessibility_Quiz.webp b/src/assets/images/Web_Accessibility_Quiz.webp new file mode 100644 index 00000000..3a713086 Binary files /dev/null and b/src/assets/images/Web_Accessibility_Quiz.webp differ diff --git a/src/assets/images/Web_Icon.svg b/src/assets/images/Web_Icon.svg new file mode 100644 index 00000000..0fa81adc --- /dev/null +++ b/src/assets/images/Web_Icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/images/Wireframing_One.svg b/src/assets/images/Wireframing_One.svg new file mode 100644 index 00000000..df9262b0 --- /dev/null +++ b/src/assets/images/Wireframing_One.svg @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/images/Wireframing_Two.svg b/src/assets/images/Wireframing_Two.svg new file mode 100644 index 00000000..aec44beb --- /dev/null +++ b/src/assets/images/Wireframing_Two.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/ArticleCard.jsx b/src/components/ArticleCard.jsx new file mode 100644 index 00000000..25761193 --- /dev/null +++ b/src/components/ArticleCard.jsx @@ -0,0 +1,127 @@ +import styled from "styled-components" + +const ArticleCard = ({ + img, + title, + text, + button, + buttonLink, +}) => ( + + {title} + + {title} + {text} + + + + ) + + const Card = styled.div` + width: 1000px; + max-width: 100%; + margin: 0 auto; + padding: 1rem; + border-radius: 12px; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + padding: 1rem; + margin-top: 40px; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + flex-direction: row; + gap: 2rem; + } + `; + + const Content = styled.div` + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + max-width: 600px; +`; + +const Image = styled.img` + width: 100%; + max-width: 600px; + height: 300px; + border-radius: 12px; + object-fit: cover; + + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + width: 50%; + height: 400px; + } +`; + +const Title = styled.h3` + font-size: ${(props) => props.theme.fontSizes.headingMedium.mobile}; + font-weight: ${(props) => props.theme.fontWeights.medium}; + line-height: ${(props) => props.theme.lineHeights.heading}; + color: ${(props) => props.theme.colors.primary}; + margin: 0.6rem 0; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.headingMedium.tablet}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: ${(props) => props.theme.fontSizes.headingMedium.desktop}; + } +`; + +const Text = styled.p` + color: ${(props) => props.theme.colors.primary}; + margin: 0 0 2rem 0; + font-size: ${(props) => props.theme.fontSizes.small}; + font-weight: ${(props) => props.theme.fontWeights.regular}; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}){ + font-size: ${(props) => props.theme.fontSizes.large}; + } +`; + +const Button = styled.button` + display: inline-flex; + align-items: center; + justify-content: center; + line-height: ${(props) => props.theme.lineHeights.body}; + flex: 1; + padding: 0.5rem 1rem; + background-color: ${(props) => props.theme.colors.primary}; + color: ${(props) => props.theme.colors.secondary}; + border: none; + border-radius: 0.5rem; + cursor: pointer; + font-size: ${(props) => props.theme.fontSizes.small}; + text-decoration: none; + + &:hover { + background-color: ${(props) => props.theme.colors.accent}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + max-width: 50%; + font-size: ${(props) => props.theme.fontSizes.large}; + } +`; + + export default ArticleCard diff --git a/src/components/Header.jsx b/src/components/Header.jsx new file mode 100644 index 00000000..064be22e --- /dev/null +++ b/src/components/Header.jsx @@ -0,0 +1,82 @@ +import styled from "styled-components" + +const Header = () => { + return ( + + + + ) +} + + +const HeaderContainer = styled.header` + display: flex; + justify-content: center; + align-items: center; + padding: 1.5rem 2rem; + margin-bottom: 1rem; + background-color:${(props) => props.theme.colors.secondary}; + + @media (max-width: ${(props) => props.theme.breakpoints.tablet}) { + display: none; + } + ` + +const Nav = styled.nav` + display: flex; + justify-content: flex-end; + width: 100%; + max-width 800px; + margin: 0 auto; + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + max-width: 1000px; /* max width on large screens */ + } + ` + +const NavList = styled.ul` + display: flex; + gap: 2rem; + list-style: none; + ` + +const NavItem = styled.li` + position: relative; +` + +const NavLink = styled.a` + color: ${(props) => props.theme.colors.primary}; + font-size: ${(props) => props.theme.fontSizes.small}; + font-weight: ${(props) => props.theme.fontWeights.regular}; + text-decoration: none; + text-decoration-thickness: 2px; + text-underline-offset: 4px; + transition: color 0.4s ease; + + + &:hover { + text-decoration: underline; + text-decoration-color: ${(props) => props.theme.colors.primary}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.large}; + } +` + +export default Header diff --git a/src/components/ProjectCard.jsx b/src/components/ProjectCard.jsx new file mode 100644 index 00000000..b6bdd10b --- /dev/null +++ b/src/components/ProjectCard.jsx @@ -0,0 +1,191 @@ +import styled from "styled-components" +import webIcon from "/src/assets/images/Web_Icon.svg"; +import githubIcon from "/src/assets/images/Github_Icon.svg"; + + +const ProjectCard = ({ + img, + title, + tags, + text, + buttonOneText, + buttonTwoText, + liveDemoLink, + viewCodeLink, + singleButtonText, + singleButtonLink, + reverse +}) => ( + + {title} + + {tags && ( + + {tags.map((tag, index) => ( + {tag} + ))} + + )} + {title} + {text} + + {singleButtonText && singleButtonLink ? ( + + ) : ( + <> + {/* Otherwise render the two buttons */} + + + + )} + + + + ) + +const IconImg = styled.img` + width: 22px; + height: 22px; + margin: 0 0.5rem; +`; + +const Card = styled.div` + width: 1000px; + max-width: 100%; + margin: 0 auto; + padding: 1rem; + border-radius: 12px; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + padding: 1rem; + margin-top: 40px; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + flex-direction: ${({ $reverse }) => ($reverse ? "row-reverse" : "row")}; + gap: 2rem; + } + `; + + const Content = styled.div` + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + max-width: 600px; +`; + +const Image = styled.img` + width: 100%; + height: auto; + max-width: 600px; + border-radius: 12px; + object-fit: cover; + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + width: 45%; + } +`; + +const Title = styled.h3` + font-size: ${(props) => props.theme.fontSizes.headingMedium.mobile}; + font-weight: ${(props) => props.theme.fontWeights.medium}; + line-height: ${(props) => props.theme.lineHeights.heading}; + color: ${(props) => props.theme.colors.primary}; + margin: 0.6rem 0; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.headingMedium.tablet}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: ${(props) => props.theme.fontSizes.headingMedium.desktop}; + } +`; + +const Text = styled.p` + color: ${(props) => props.theme.colors.primary}; + margin: 0 0 2rem 0; + font-size: ${(props) => props.theme.fontSizes.small}; + font-weight: ${(props) => props.theme.fontWeights.regular}; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}){ + font-size: ${(props) => props.theme.fontSizes.large}; + } +`; + +const ButtonGroup = styled.div` + display: flex; + flex-direction: column; + gap: 0.5rem; +`; + +const Button = styled.button` + display: inline-flex; + align-items: center; + justify-content: center; + line-height: ${(props) => props.theme.lineHeights.body}; + flex: 1; + padding: 0.5rem 1rem; + background-color: ${(props) => props.theme.colors.primary}; + color: ${(props) => props.theme.colors.secondary}; + font-size: ${(props) => props.theme.fontSizes.small}; + border: none; + border-radius: 0.5rem; + cursor: pointer; + text-decoration: none; + + &:hover { + background-color: ${(props) => props.theme.colors.accent}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + max-width: 50%; + font-size: ${(props) => props.theme.fontSizes.large}; + } +`; + +const Tags = styled.div` + display: flex; + gap: 0.3rem; + margin-bottom: 1rem; + flex-wrap: wrap; +`; + +const Tag = styled.span` + background-color:${(props) => props.theme.colors.tertiary}; + color: ${(props) => props.theme.colors.primary}; + padding: 0.1rem 0.6rem; + font-size: ${(props) => props.theme.fontSizes.small}; + border-radius: 8px; + border: 1px solid ${(props) => props.theme.colors.primary}; +`; + + export default ProjectCard diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 61010be6..00000000 --- a/src/index.css +++ /dev/null @@ -1,4 +0,0 @@ -body { - background: pink; - color: hotpink; -} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index ed109d76..01150949 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -3,8 +3,6 @@ import { createRoot } from 'react-dom/client' import { App } from './App.jsx' -import './index.css' - createRoot(document.getElementById('root')).render( diff --git a/src/sections/ArticleSection.jsx b/src/sections/ArticleSection.jsx new file mode 100644 index 00000000..4ebe9060 --- /dev/null +++ b/src/sections/ArticleSection.jsx @@ -0,0 +1,63 @@ +import ArticleCard from "../components/ArticleCard" +import styled from "styled-components" +import StorytellingImg from "../assets/images/Storytelling.webp"; +import CollaborationImg from "../assets/images/Collaboration.webp"; +import PromtingImg from "../assets/images/AI_Prompts.webp"; + +const articles = [ + { + img: StorytellingImg, + title: "Turning Pages: Empowering Financial Literacy through storytelling", + text: "This article explores how storytelling can simplify complex financial concepts, making them more relatable and engaging. By using narratives, users can better understand and apply financial knowledge in their daily lives.", + button: "View LinkedIn Post", + buttonLink: "https://www.linkedin.com/posts/activity-7334172734727331840-uPhY?utm_source=share&utm_medium=member_desktop&rcm=ACoAADoBPNgBvXPL2eT3K8Non_y5OWN1icdjbzo", + }, + { + img: CollaborationImg, + title: "Building Bridges: Connecting Developers and Designers through collaboration", + text: "This article examines how effective collaboration between developers and designers can lead to user-friendly web experiences with seamless communication and mutual understanding in creating cohesive digital products.", + button: "Coming Soon", + buttonLink: "https://example.com", + }, + { + img: PromtingImg, + title: "Prompting Perfection: How AI Is Revolutionizing UX/UI Design", + text: "This article delves into how crafting the right prompts can help designers generate design concepts, optimize user interfaces, and enhance content creation, ultimately streamlining workflows and delivering more personalized user experiences.", + button: "Coming Soon", + buttonLink: "https://example.com", + }, +]; + +const ArticleSection = () => { + return ( +
+ My Words + {articles.map((article, index) => ( + + ))} +
+ ); +}; + + +const SectionTitle = styled.h2` + font-size: ${(props) => props.theme.fontSizes.headingLarge.mobile}; + font-weight: ${(props) => props.theme.fontWeights.Bold}; + color: ${(props) => props.theme.colors.primary}; + text-align: center; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.headingLarge.tablet}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: ${(props) => props.theme.fontSizes.headingLarge.desktop}; + } +`; + +const Section = styled.section` + background-color: ${(props) => props.theme.colors.secondary}; +`; + + +export default ArticleSection diff --git a/src/sections/FooterSection.jsx b/src/sections/FooterSection.jsx new file mode 100644 index 00000000..cc38b79a --- /dev/null +++ b/src/sections/FooterSection.jsx @@ -0,0 +1,153 @@ +import styled from "styled-components" +import portraitImg from "/src/assets/images/TavanThiry.webp" +import { FaGithub, FaLinkedin } from "react-icons/fa"; + +const SocialLinks = () => ( + + + + + + + + +); + +const FooterSection = ({ title }) => ( +
+ Let's Talk + Portrait of Tavan Thiry, a frontend developer and UX/UI designer + + Tavan Thiry + + +46707916107 + tavan.thiry@gmail.com + + + +
+ ) + + const Section = styled.div` + width: 100%; + margin-top: 60px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + background-color: ${(props) => props.theme.colors.primary}; + color: ${(props) => props.theme.colors.secondary}; + padding: 2rem 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + padding: 3rem 1.5rem; + } + `; + + const Title = styled.h3` + font-size: ${(props) => props.theme.fontSizes.headingMedium.mobile}; + font-weight: ${(props) => props.theme.fontWeights.medium}; + margin-bottom: 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.headingMedium.tablet}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: ${(props) => props.theme.fontSizes.headingMedium.desktop}; + } + `; + + const Image = styled.img` + width: 100px; + height: 100px; + border-radius: 50%; + object-fit: cover; + margin-bottom: 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + width: 140px; + height: 140px; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + width: 160px; + height: 160px; + } + `; + +const Content = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + padding: 0 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + padding: 0 2rem; + } + `; + +const ProfileName = styled.p` + font-size: ${(props) => props.theme.fontSizes.small}; + font-weight: ${(props) => props.theme.fontWeights.regular}; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.large}; + } +`; + +const Text = styled.span` + display: block; + margin: 0.4rem 0; + font-size: ${(props) => props.theme.fontSizes.small}; + font-weight: ${(props) => props.theme.fontWeights.regular}; + color: ${(props) => props.theme.colors.secondary}; + text-decoration: none; + + &:hover { + text-decoration: underline; + cursor: pointer; + } +`; + +const Contact = styled.div` + text-align: center; +`; + +const SocialIcons = styled.div` + display: flex; + justify-content: center; + margin-top: 1rem; + color: ${(props) => props.theme.colors.secondary}; +`; + +const IconLink = styled.a` + color: ${(props) => props.theme.colors.secondary}; + font-size: 1.5rem; + margin: 0 0.5rem; + transition: color 0.3s ease; + + &:hover { + color: ${(props) => props.theme.colors.accent}; + cursor: pointer; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: 1.75rem; + } +`; + +export default FooterSection diff --git a/src/sections/IntroSection.jsx b/src/sections/IntroSection.jsx new file mode 100644 index 00000000..d0e9bb2f --- /dev/null +++ b/src/sections/IntroSection.jsx @@ -0,0 +1,134 @@ +import styled from "styled-components" +import { Helmet } from "react-helmet"; +import portraitImg from "/src/assets/images/TavanThiry.webp" + + const IntroSection = () => { + return ( + <> + + + +
+ + Hi there, I am + Tavan Thiry + + Web Developer + UX/UI Designer + + + A Web Developer with a background in UX/UI design. I make sure design and functionality go hand in hand. My strength lies in bridging the gap between code and design, helping to create smoother processes and better products. + + + + Portrait of Tavan Thiry, a frontend developer and UX/UI designer + +
+ + ) +} + +const Section = styled.section` + max-width: 1000px; + margin: 0 auto; + margin-top: 1rem; + display: flex; + flex-direction: column; + justify-content: center; + + @media (min-width:${(props) => props.theme.breakpoints.tablet}) { + flex-direction: row; + align-items: center; + margin-top: 0rem; + } + ` + +const IntroContent = styled.div` + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + align-items: flex-start; + text-align: left; + padding: 2rem; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + text-align: left; + } +` + +const Paragraph = styled.p` + font-size: ${(props) => props.theme.fontSizes.small}; + font-weight: ${(props) => props.theme.fontWeights.regular}; + line-height: ${(props) => props.theme.lineHeights.body}; + max-width: 600px; + + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size:${(props) => props.theme.fontSizes.large}; + } +` + +const MediumHeading = styled.h4` + font-size: ${(props) => props.theme.fontSizes.headingSmall.mobile}; + font-weight: ${(props) => props.theme.fontWeights.semiBold}; + color: ${(props) => props.theme.colors.accent}; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.headingSmall.tablet}; + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: ${(props) => props.theme.fontSizes.headingSmall.desktop}; + } + } +` +const LargeHeadingGroup = styled.div` + display: flex; + flex-direction: column; + padding: 0.2rem 0 1rem 0; + ` + +const LargeHeading = styled.h1` + font-size: ${(props) => props.theme.fontSizes.headingXLarge.mobile}; + font-weight: ${(props) => props.theme.fontWeights.Bold}; + line-height: ${(props) => props.theme.lineHeights.heading}; + white-space: nowrap; + + @media (min-width: 768px) { + font-size: ${(props) => props.theme.fontSizes.headingXLarge.tablet}; + } + + @media (min-width: 1024px) { + font-size: ${(props) => props.theme.fontSizes.headingXLarge.desktop}; + } +` + +const ImageContainer = styled.div` + flex: 1; + display: flex; + justify-content: center; + padding: 1rem; + min-height: 350px; +` + +const Image = styled.img` + width: 100%; + height: auto; + min-height: 350px; + max-width: 325px; + border-radius: 3rem; + object-fit: cover; +`; + +export default IntroSection diff --git a/src/sections/ProjectSection.jsx b/src/sections/ProjectSection.jsx new file mode 100644 index 00000000..e4c97743 --- /dev/null +++ b/src/sections/ProjectSection.jsx @@ -0,0 +1,101 @@ +import ProjectCard from "../components/ProjectCard" +import styled from "styled-components" +import TaskManagementImg from "../assets/images/TaskManagement.webp"; +import HappyThoughtsImg from "../assets/images/Happy_Thoughts.webp"; +import WeatherAppImg from "../assets/images/Weather_App.webp"; +import CreativeCollabsImg from "../assets/images/Creative_Collabs_Website.webp"; +import AccessibilityQuizImg from "../assets/images/Web_Accessibility_Quiz.webp"; +import SustainabilityHousingImg from "../assets/images/Sustainability_Housing.webp"; + +const projects = [ + { + img: TaskManagementImg, + title: "ToDo Task Management", + text: "A to-do application built with React, using Zustand for global state management and Styled Components for modular, maintainable styling. Features include task creation, completion toggling, and real-time task counters.", + buttonOneText: "View Demo", + buttonTwoText: "View Code", + tags: ["JavaScript", "React", "Global State Management", "Zustand", "Styled Components"], + liveDemoLink: "https://tthiry-todotaskmanagement.netlify.app", + viewCodeLink: "https://github.com/T-Thiry/js-project-todo", + }, + { + img: HappyThoughtsImg, + title: "Happy Thoughts App", + text: "A cheerful social feed built with React and an external API. Users can share short, positive messages and like others’ thoughts. The app features real-time updates and promotes positivity through simple interactions.", + buttonOneText: "View Demo", + buttonTwoText: "View Code", + tags: ["JavaScript", "React", "React Hooks", "API"], + liveDemoLink: "https://tthiry-happythoughts.netlify.app", + viewCodeLink: "https://github.com/T-Thiry/js-project-happy-thoughts", + }, + { + img: AccessibilityQuizImg, + title: "Web Accessibility Quiz", + text: "The web accessibility quiz helps users test their knowledge of inclusive design principles in a quick and engaging way. It is designed to raise awareness of common accessibility issues while encouraging best practices in web development.", + buttonOneText: "View Demo", + buttonTwoText: "View Code", + tags: ["Javascript","Typescript", "CSS", "HTML", "Accessibility"], + liveDemoLink: "https://accessibility-guiz.netlify.app", + viewCodeLink: "https://github.com/T-Thiry/js-project-accessibility", + }, + { + img: WeatherAppImg, + title: "Weather App", + text: "A weather app built with JavaScript and TypeScript. It fetches real-time data from a weather API, allowing users to search any nordic city and view current conditions, temperature, and forecasts.", + buttonOneText: "View Demo", + buttonTwoText: "View Code", + tags: ["Javascript","Typescript", "CSS", "HTML", "API"], + liveDemoLink: "https://nordic-weatherapp.netlify.app", + viewCodeLink: "https://github.com/T-Thiry/newjs-project-weather-app", + }, + { + img: CreativeCollabsImg, + title: "Creative Collabs Website", + text: "A website that showcases upcoming designer workshops and developer hackathons, making it easy for creatives and coders to discover and join events. The platform features a clean layout, and responsive design for seamless browsing.", + buttonOneText: "View Demo", + buttonTwoText: "View Code", + tags: ["CSS", "HTML", "Responsive Design", ], + liveDemoLink: "https://hookitup.netlify.app", + viewCodeLink: "https://github.com/T-Thiry/js-project-business-site", + }, + { + img: SustainabilityHousingImg, + title: "Sustainable Housing Simulation", + text: "A simulation that explores the principles of sustainable housing through interactive scenarios that highlight energy efficiency, eco-friendly materials, and smart design choices.", + singleButtonText: "Coming Soon", + singleButtonLink: "https://example.com", + tags: ["UX design", "UX research", "UI design", "Accessibility"], + }, + ]; + +const ProjectSection = () => { +return ( +
+ My Projects + {projects.map((project, index) => ( + + ))} +
+); +}; + +const SectionTitle = styled.h2` + font-size: ${(props) => props.theme.fontSizes.headingLarge.mobile}; + font-weight: ${(props) => props.theme.fontWeights.Bold}; + color: ${(props) => props.theme.colors.primary}; + text-align: center; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.headingLarge.tablet}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: ${(props) => props.theme.fontSizes.headingLarge.desktop}; + } +`; + +const Section = styled.section` + background-color: ${(props) => props.theme.colors.secondary}; +`; + +export default ProjectSection diff --git a/src/sections/SkillsSection.jsx b/src/sections/SkillsSection.jsx new file mode 100644 index 00000000..de7990ad --- /dev/null +++ b/src/sections/SkillsSection.jsx @@ -0,0 +1,131 @@ +import styled from "styled-components" + +const skillsData = [ + { + category: "Code Craft πŸ› οΈ", + skills: ["HTML", "CSS", "JavaScript", "TypeScript", "React", "Version Control"] + }, + { + category: "My Dev Tools πŸŽ’", + skills: ["Visual Studio Code", "Chrome DevTools", "Git/Github", "CodePen","Netlify", "Figma"] + }, + { + category: "Collab Tools πŸ”—", + skills: ["Asana", "Trello", "Miro", "Notion", "Slack", "Zoom"] + }, + { + category: "Methods 🎨", + skills: ["Agile Methodology", "Mob Programming", "Pair Programming", "Responsive Design", "Usability Testing", "Prototyping"] + }, + { + category: "Languages πŸ‡ΈπŸ‡ͺ", + skills: ["Swedish", "English", "Kurdish", "Spanish"] + }, +]; + + + const SkillsCard = () => { + return ( + + Skills + + {skillsData.map((group, index) => ( + + {group.category} + + {group.skills.map((skill, i) => ( + {skill} + ))} + + + ))} + + + ); + }; + +const SkillsSection = styled.section` + width: 100%; + background-color: ${(props) => props.theme.colors.primary}; + padding: 2rem 0; + margin: 40px 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + padding: 3rem 0; + margin: 60px 0; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + padding: 4rem 0; + margin-top: 100px; +} +` + +const SkillsTitle = styled.h2` + font-size: ${(props) => props.theme.fontSizes.headingLarge.mobile}; + color: ${(props) => props.theme.colors.secondary}; + text-align: center; + margin-bottom: 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.headingLarge.tablet}; + margin-bottom: 1.5rem; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: ${(props) => props.theme.fontSizes.headingLarge.desktop}; + margin-bottom: 1.75rem; + } +` + +const SkillsWrapper = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: center; + max-width: 1000px; + width: 100%; + margin: 0 auto; +` + +const CategoryWrapper = styled.div` + background-color:${(props) => props.theme.colors.primary}; + padding: 1rem; + border-radius: 12px; + width: 100%; + max-width: 260px; + margin-bottom: 1rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + max-width: 200px; + } +` + +const CategoryTitle = styled.div` + font-weight: ${(props) => props.theme.fontWeights.regular}; + font-size: ${(props) => props.theme.fontSizes.small}; + color: ${(props) => props.theme.colors.secondary}; + padding: 0.2rem 1rem; + margin-bottom: 1rem; + text-align: center; + border: 1px solid ${(props) => props.theme.colors.secondary}; + border-radius: 8px; +` + +const SkillsList = styled.div` + display: flex; + flex-direction: column; + text-align: left; + gap: 0.25rem; + width: 100%; +` + +const SkillItem = styled.div` + font-size: ${(props) => props.theme.fontSizes.small}; + color: ${(props) => props.theme.colors.secondary}; +` + +export default SkillsCard diff --git a/src/sections/TechSection.jsx b/src/sections/TechSection.jsx new file mode 100644 index 00000000..c68bd97b --- /dev/null +++ b/src/sections/TechSection.jsx @@ -0,0 +1,136 @@ +import styled from 'styled-components'; +import HtmlLogo from '../assets/images/HTML_Logo.svg'; +import CssLogo from '../assets/images/CSS_Logo.svg'; +import JavascriptLogo from '../assets/images/Javascript_Logo.svg'; +import TypescriptLogo from '../assets/images/Typescript_Logo.svg'; +import ReactLogo from '../assets/images/React_Logo.svg'; +import GithubLogo from '../assets/images/Github_Logo.svg'; +import FigmaLogo from '../assets/images/Figma_Logo.svg'; +import NotionLogo from '../assets/images/Notion_Logo.svg'; +import AsanaLogo from '../assets/images/Asana_Logo.svg'; +import SlackLogo from '../assets/images/Slack_Logo.svg'; + +const TechSection = () => { + return ( +
+ + Tech + + HTML logo + CSS logo + Javascript logo + Typescript logo + React logo + Github logo + Figma logo + Notion logo + Asana logo + Slack logo + + + HTML, CSS, JavaScript, ES6, JSX, TypeScript, React, React Hooks, Node.js, Mongo DB, APIs, Git, GitHub, Mob Programming, Pair Programming, Web Accessibility (WCAG). + + +
+ ); +}; + +const Section = styled.section` + width: 100%; + padding: 4rem 1rem; + background-color:${(props) => props.theme.colors.primary}; + color: ${(props) => props.theme.colors.secondary}; + margin: 2rem 0; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + padding: 5rem 2rem; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + padding: 6rem 2rem; + } +`; + +const ContentWrapper = styled.div` + max-width: 1000px; + margin: 0 auto; +`; + +const Heading = styled.h2` + font-size: ${(props) => props.theme.fontSizes.headingLarge.mobile}; + text-align: center; + margin-bottom: 1.5rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.headingLarge.tablet}; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + font-size: ${(props) => props.theme.fontSizes.headingLarge.desktop}; + } +`; + +const IconGrid = styled.div` + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.7rem; + padding: 1rem; + margin-bottom: 2rem; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + flex-wrap: nowrap; + gap: 1rem; + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + flex-wrap: nowrap; + gap: 1.4rem; + } +`; + +const IconWrapper = styled.div` + width: 40px; + height: 40px; + transition: transform 0.3s ease, box-shadow 0.3s ease; + + &:hover { + transform: translateY(-5px); + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3); + } + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + width: 46px; + height: 46px; + + &:hover { + transform: translateY(-5px); + box-shadow: 0 10px 15px rgba(0, 0, 0, 0.3); + } + } + + @media (min-width: ${(props) => props.theme.breakpoints.desktop}) { + width: 52px; + height: 52px; + } + + img { + width: 100%; + height: 100%; + object-fit: contain; + } +`; + +const Paragraph = styled.p` + font-size: ${(props) => props.theme.fontSizes.small}; + text-align: center; + max-width: 800px; + margin: 0 auto; + line-height: ${(props) => props.theme.lineHeights.body}; + + @media (min-width: ${(props) => props.theme.breakpoints.tablet}) { + font-size: ${(props) => props.theme.fontSizes.large}; + } +`; + +export default TechSection; diff --git a/src/styles/GlobalStyles.jsx b/src/styles/GlobalStyles.jsx new file mode 100644 index 00000000..3d54ef4d --- /dev/null +++ b/src/styles/GlobalStyles.jsx @@ -0,0 +1,20 @@ +import { createGlobalStyle } from "styled-components"; + +const GlobalStyle = createGlobalStyle` + * { + box-sizing: border-box; + margin: 0; + padding: 0; + } + + html { + scroll-behavior: smooth; + } + + body { + font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + background-color: #ffffff; + } +`; + +export default GlobalStyle; diff --git a/src/styles/Theme.jsx b/src/styles/Theme.jsx new file mode 100644 index 00000000..6454a946 --- /dev/null +++ b/src/styles/Theme.jsx @@ -0,0 +1,58 @@ +const theme = { + colors: { + primary: "#1e1e1e", // black color + secondary: "#ffffff", // white color + tertiary: "#F5F5F5", // light gray + accent: "#CE400C", // orange + }, + + fontSizes: { + // Heading sizes by breakpoint + headingSmall: { + mobile: "1.125rem", // 18px + tablet: "1.25rem", // 20px + desktop: "1.5rem", // 24px + }, + headingMedium: { + mobile: "1.5rem", // 24px + tablet: "1.75rem", // 28px + desktop: "2rem", // 32px + }, + headingLarge: { + mobile: "1.75rem", // 28px + tablet: "2rem", // 32px + desktop: "2.5rem", // 40px + }, + headingXLarge: { + mobile: "1.75rem", // 28px + tablet: "2.5rem", // 40px + desktop: "3rem", // 48px + }, + + // Body font sizes + small: "1rem", // 16px + large: "1.125rem", // 18px + }, + + fontWeights: { + regular: 400, + medium: 500, + semiBold: 600, + bold: 700, + }, + + lineHeights: { + heading: "1.25", + body: "1.5", + }, + + borderRadius: "0.5rem", + + breakpoints: { + mobile: "480px", + tablet: "768px", + desktop: "1024px", + }, +}; + +export default theme; \ No newline at end of file