diff --git a/README.md b/README.md index 200f4282..021cba4b 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ # Portfolio + + mikaela-js-project-portfolio.netlify.app diff --git a/index.html b/index.html index 6676fb2d..fdb2dfc5 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,10 @@ Portfolio +
diff --git a/package.json b/package.json index 48911600..921365ea 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,15 @@ }, "dependencies": { "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-dom": "^19.0.0", + "styled-components": "^6.1.19" }, "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": "^5.1.1", + "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", diff --git a/public/images/avatar.jpg b/public/images/avatar.jpg new file mode 100644 index 00000000..b5b2d4f0 Binary files /dev/null and b/public/images/avatar.jpg differ diff --git a/src/App.jsx b/src/App.jsx index a161d8d3..b36886bf 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,33 @@ +import { ThemeProvider } from "styled-components" +import { theme, GlobalStyle } from "./styles/styles" +import { + HeroSection, + TechSection, + ProjectSection, + ArticleSection, + SkillsSection, + ContactSection +} from "./components/sections/sections.js" + 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.

+ + +
+ +
+
+ + + + +
+ +
+ ) -} +} \ No newline at end of file diff --git a/src/components/cards/ArticleCard.jsx b/src/components/cards/ArticleCard.jsx new file mode 100644 index 00000000..7a32bf29 --- /dev/null +++ b/src/components/cards/ArticleCard.jsx @@ -0,0 +1,24 @@ +import styled from "styled-components" +import { CardImage, Tag, Button, SvgIcon } from "../ui/ui" +import { CardTitle, BodyText } from "../typography/typography" +import { DocumentIcon } from "../svg-icons/svg-icons" + +const StyledArticleCard = styled.div` + display: flex; + flex-direction: column; + gap: 28px; +` + +export const ArticleCard = ({ article }) => { + return ( + + + + + + + + ) +} \ No newline at end of file diff --git a/src/components/cards/ProjectCard.jsx b/src/components/cards/ProjectCard.jsx new file mode 100644 index 00000000..969401e4 --- /dev/null +++ b/src/components/cards/ProjectCard.jsx @@ -0,0 +1,57 @@ +import styled from "styled-components" +import { CardImage, Tag, Button, SvgIcon} from "../ui/ui" +import { CardTitle, BodyText } from "../typography/typography" +import { GlobeIcon, GithubIcon } from "../svg-icons/svg-icons" +import { FlexWrapper } from "../../styles/styles" + +const StyledProjectCard = styled.div` + display: flex; + flex-direction: column; + gap: 28px; + + @media (min-width: ${({ theme }) => theme.breakpoints.tablet}) { + flex-direction: row; + gap: 30px; + } +` +const ButtonWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 2px; + + @media (min-width: ${({ theme }) => theme.breakpoints.tablet}) { + flex-direction: row; + gap: 30px; + } +` +const Container = styled.div` + display: flex; + flex-direction: column; + gap: 20px; +` + +export const ProjectCard = ({ project }) => { + return ( + + + + + + + + {project.tags.map(tag => ( + + ))} + + + + + + + + ) +} \ No newline at end of file diff --git a/src/components/cards/SkillColumn.jsx b/src/components/cards/SkillColumn.jsx new file mode 100644 index 00000000..b28ada95 --- /dev/null +++ b/src/components/cards/SkillColumn.jsx @@ -0,0 +1,25 @@ +import styled from 'styled-components' +import { UnorderedList } from "../ui/ui" +import { SkillTitle } from "../typography/typography" + +const StyledSkillColumn = styled.div` + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 16px; + + @media (min-width: ${({ theme }) => theme.breakpoints.tablet}) { + text-align: left; + align-items: flex-start; + } +` + +export const SkillColumn = ({ skillsList, colIndex }) => { + return ( + + + + + ) +} \ No newline at end of file diff --git a/src/components/cards/cards.js b/src/components/cards/cards.js new file mode 100644 index 00000000..b691bbd1 --- /dev/null +++ b/src/components/cards/cards.js @@ -0,0 +1,3 @@ +export { ArticleCard } from './ArticleCard' +export { ProjectCard } from './ProjectCard' +export { SkillColumn } from './SkillColumn' \ No newline at end of file diff --git a/src/components/layout/Card.jsx b/src/components/layout/Card.jsx new file mode 100644 index 00000000..9ad39816 --- /dev/null +++ b/src/components/layout/Card.jsx @@ -0,0 +1,6 @@ +export const Card = () => { + return ( + <> + + ) +} \ No newline at end of file diff --git a/src/components/layout/FooterStrip.jsx b/src/components/layout/FooterStrip.jsx new file mode 100644 index 00000000..21f0584b --- /dev/null +++ b/src/components/layout/FooterStrip.jsx @@ -0,0 +1,6 @@ +export const FooterStrip = () => { + return ( + <> + + ) +} \ No newline at end of file diff --git a/src/components/layout/Section.jsx b/src/components/layout/Section.jsx new file mode 100644 index 00000000..904a37c1 --- /dev/null +++ b/src/components/layout/Section.jsx @@ -0,0 +1,6 @@ +export const Section = () => { + return ( + <> + + ) +} \ No newline at end of file diff --git a/src/components/layout/Wrapper.jsx b/src/components/layout/Wrapper.jsx new file mode 100644 index 00000000..f8b2cb4c --- /dev/null +++ b/src/components/layout/Wrapper.jsx @@ -0,0 +1,6 @@ +export const Wrapper = () => { + return ( + <> + + ) +} \ No newline at end of file diff --git a/src/components/layout/layout.js b/src/components/layout/layout.js new file mode 100644 index 00000000..9247bac1 --- /dev/null +++ b/src/components/layout/layout.js @@ -0,0 +1,4 @@ +export { Card } from './Card' +export { FooterStrip } from './FooterStrip' +export { Section } from './Section' +export { Wrapper } from './Wrapper' \ No newline at end of file diff --git a/src/components/sections/ArticleSection.jsx b/src/components/sections/ArticleSection.jsx new file mode 100644 index 00000000..983a28e0 --- /dev/null +++ b/src/components/sections/ArticleSection.jsx @@ -0,0 +1,42 @@ +import styled from "styled-components" +import { SectionTitle } from "../typography/typography" +import { ArticleCard } from "../cards/cards" +import articles from "../../data/articles" +import { StyledSection } from '../../styles/StyledSection' + + +const GridContainer = styled.div` + display: flex; + flex-direction: column; + gap: 40px; + + + @media (min-width: ${props => props.theme.breakpoints.desktop}) { + display: grid; + width: fit-content; + margin: 0 auto; + grid-template-columns: repeat(2, auto); + column-gap: 40px; + row-gap: 40px; + } + @media (min-width: ${props => props.theme.breakpoints.desktopLarge}) { + column-gap: 64px; + row-gap: 64px; + } +` +export const ArticleSection = ({ variant }) => { + return ( + + + + + {articles.articles.map((article, index) => ( + + ))} + + + ) +} \ No newline at end of file diff --git a/src/components/sections/ContactSection.jsx b/src/components/sections/ContactSection.jsx new file mode 100644 index 00000000..9badf54c --- /dev/null +++ b/src/components/sections/ContactSection.jsx @@ -0,0 +1,30 @@ +import styled from 'styled-components' +import { Avatar } from '../ui/ui' +import { SectionTitle, BodyText } from '../typography/typography' +import aboutMe from '../../data/aboutMe' +import { StyledSection } from '../../styles/StyledSection' + +const StyledDiv = styled.div` + display: flex; + flex-direction: column; + align-items: center; +` + + + +export const ContactSection = ({ variant }) => { + return ( + + + + + + + + + + ) +} \ No newline at end of file diff --git a/src/components/sections/HeroSection.jsx b/src/components/sections/HeroSection.jsx new file mode 100644 index 00000000..fa8d7489 --- /dev/null +++ b/src/components/sections/HeroSection.jsx @@ -0,0 +1,65 @@ +import styled from 'styled-components' +import { Avatar } from '../ui/ui' +import { BodyText, PageTitle } from '../typography/typography' +import { intro, role, description, avatar } from "../../data/aboutMe" +import { StyledSection } from '../../styles/StyledSection' + +// Don't show from Tablet breakpoint and up +const MobileLayout = styled.div` + @media (min-width: ${({ theme }) => theme.breakpoints.tablet}) { + display: none; + } +` +// Don't show up until Tablet breakpoint +const DesktopLayout = styled.div` + @media (max-width: ${({ theme }) => theme.breakpoints.tablet}) { + display: none; + } +` +// Grouping into and PageTitle +const StyledContainer1 = styled.div` + display: flex; + flex-direction: column; +` + +// Grouping Avatar and description +const StyledContainer2 = styled.div` + display: flex; + align-items: center; + gap: 28px; +` + +export const HeroSection = ({ variant }) => { + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + ) +} \ No newline at end of file diff --git a/src/components/sections/ProjectSection.jsx b/src/components/sections/ProjectSection.jsx new file mode 100644 index 00000000..ff2e01cd --- /dev/null +++ b/src/components/sections/ProjectSection.jsx @@ -0,0 +1,19 @@ +import { SectionTitle } from "../typography/typography" +import { ProjectCard } from "../cards/cards" +import projects from "../../data/projects" +import { StyledSection } from '../../styles/StyledSection' + +export const ProjectSection = ({ variant }) => { + return ( + + + + {projects.projects.map((project, index) => ( + + ))} + + ) +} \ No newline at end of file diff --git a/src/components/sections/SkillsSection.jsx b/src/components/sections/SkillsSection.jsx new file mode 100644 index 00000000..82743467 --- /dev/null +++ b/src/components/sections/SkillsSection.jsx @@ -0,0 +1,43 @@ +import styled from 'styled-components' +import { SectionTitle } from "../typography/typography" +import { SkillColumn } from "../cards/cards" +import skills from "../../data/skills" +import { StyledSection } from '../../styles/StyledSection' + +const GridContainer = styled.div` + display: flex; + flex-direction: column; + gap: 40px; + + + @media (min-width: ${props => props.theme.breakpoints.tablet}) { + display: grid; + width: fit-content; + margin: 0 auto; + grid-template-columns: repeat(2, 200px); + column-gap: 20px; + row-gap: 40px; + } + + @media (min-width: ${props => props.theme.breakpoints.desktop}) { + grid-template-columns: repeat(4, 200px); + column-gap: 40px; + } +` + + +export const SkillsSection = ({ variant }) => { + return ( + + + + {skills.skillsList.map((skill, index) => ( + + ))} + + + ) +} \ No newline at end of file diff --git a/src/components/sections/TechSection.jsx b/src/components/sections/TechSection.jsx new file mode 100644 index 00000000..8bd6bf7a --- /dev/null +++ b/src/components/sections/TechSection.jsx @@ -0,0 +1,33 @@ +import styled from "styled-components" +import { SectionTitle, BodyText } from "../typography/typography" +import aboutMe from "../../data/aboutMe" +import { StyledSection } from '../../styles/StyledSection' + +const Container = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + gap: 56px; + + @media (min-width: ${({ theme }) => theme.breakpoints.desktop}) { + flex-direction: row; + gap: 110px; + align-items: center; + justify-content: center; + } +` + + +export const TechSection = ({ variant }) => { + return ( + + + + + + + ) +} \ No newline at end of file diff --git a/src/components/sections/sections.js b/src/components/sections/sections.js new file mode 100644 index 00000000..baf5ea9b --- /dev/null +++ b/src/components/sections/sections.js @@ -0,0 +1,6 @@ +export { ArticleSection } from './ArticleSection' +export { ContactSection } from './ContactSection' +export { HeroSection } from './HeroSection' +export { ProjectSection } from './ProjectSection' +export { SkillsSection } from './SkillsSection' +export { TechSection } from './TechSection' \ No newline at end of file diff --git a/src/components/svg-icons/DocumentIcon.jsx b/src/components/svg-icons/DocumentIcon.jsx new file mode 100644 index 00000000..5e0e21cc --- /dev/null +++ b/src/components/svg-icons/DocumentIcon.jsx @@ -0,0 +1,4 @@ +export const DocumentIcon = () => { + return ( + ) +} \ No newline at end of file diff --git a/src/components/svg-icons/GithubIcon.jsx b/src/components/svg-icons/GithubIcon.jsx new file mode 100644 index 00000000..6bf00a1c --- /dev/null +++ b/src/components/svg-icons/GithubIcon.jsx @@ -0,0 +1,5 @@ +export const GithubIcon = () => { + return ( + + ) +} \ No newline at end of file diff --git a/src/components/svg-icons/GlobeIcon.jsx b/src/components/svg-icons/GlobeIcon.jsx new file mode 100644 index 00000000..20908da8 --- /dev/null +++ b/src/components/svg-icons/GlobeIcon.jsx @@ -0,0 +1,5 @@ +export const GlobeIcon = () => { + return ( + + ) +} \ No newline at end of file diff --git a/src/components/svg-icons/svg-icons.js b/src/components/svg-icons/svg-icons.js new file mode 100644 index 00000000..1c496a02 --- /dev/null +++ b/src/components/svg-icons/svg-icons.js @@ -0,0 +1,3 @@ +export { GithubIcon } from './GithubIcon' +export { GlobeIcon } from './GlobeIcon' +export { DocumentIcon } from './DocumentIcon' \ No newline at end of file diff --git a/src/components/typography/BodyText.jsx b/src/components/typography/BodyText.jsx new file mode 100644 index 00000000..3bf7855d --- /dev/null +++ b/src/components/typography/BodyText.jsx @@ -0,0 +1,34 @@ +import styled from "styled-components" + +const StyledBodyText = styled.p` + font-weight: ${({ weight }) => weight === "lightsemibold" ? "450" : weight === "semibold" ? "550" : 'inherit'}; + font-family: ${({ fontFam }) => fontFam === "heading" ? "var(--font-heading)" : "inherit"}; + font-size: ${({ fontSize }) => fontSize === "xs" ? "21px" : "inherit"}; + text-align: ${({ textAlign }) => textAlign === "center" ? "center" : "left"}; + + @media (max-width: ${({ theme }) => theme.breakpoints.mobile}) { + font-size: ${({ fontSize }) => fontSize === "xs" ? "18px" : "inherit"}; + } + + @media (min-width: ${({ theme }) => theme.breakpoints.tablet}) { + font-size: ${({ fontSizeT }) => fontSizeT === "tablet" ? "24px" : "inherit"}; + } + + @media (min-width: ${({ theme }) => theme.breakpoints.desktop}) { + font-size: ${({ fontSizeD }) => fontSizeD === "desktop" ? "28px" : "inherit"}; + text-align: ${({ textAlignD }) => textAlignD === "center" ? "center" : "left"}; + } + + @media (min-width: ${({ theme }) => theme.breakpoints.desktop}) { + font-size: ${({ fontSizeD }) => fontSizeD === "desktop" ? "34px" : "18px"}; + } + +` + +export const BodyText = ({ text, weight, fontFam, fontSize, fontSizeT, fontSizeD, textAlign, textAlignD }) => { + return ( + + {text} + + ) +} \ No newline at end of file diff --git a/src/components/typography/CardTitle.jsx b/src/components/typography/CardTitle.jsx new file mode 100644 index 00000000..9d33b090 --- /dev/null +++ b/src/components/typography/CardTitle.jsx @@ -0,0 +1,7 @@ +export const CardTitle = ({ title }) => { + return ( +

+ {title} +

+ ) +} \ No newline at end of file diff --git a/src/components/typography/ListTitle.jsx b/src/components/typography/ListTitle.jsx new file mode 100644 index 00000000..e0d1df72 --- /dev/null +++ b/src/components/typography/ListTitle.jsx @@ -0,0 +1,7 @@ +export const ListTitle = ({ title }) => { + return ( +

+ {title} +

+ ) +} \ No newline at end of file diff --git a/src/components/typography/PageTitle.jsx b/src/components/typography/PageTitle.jsx new file mode 100644 index 00000000..f4115541 --- /dev/null +++ b/src/components/typography/PageTitle.jsx @@ -0,0 +1,13 @@ +import styled from "styled-components" + +const StyledPageTitle = styled.h1` + color: ${({ theme, variant }) => variant ? theme.sections[variant].headingClr: "inherit"}; +`; + +export const PageTitle = ({ title, variant }) => { + return ( + + {title} + + ) +} diff --git a/src/components/typography/SectionTitle.jsx b/src/components/typography/SectionTitle.jsx new file mode 100644 index 00000000..e3887a9d --- /dev/null +++ b/src/components/typography/SectionTitle.jsx @@ -0,0 +1,25 @@ +import styled from "styled-components"; + +const StyledSectionTitle = styled.h2` + color: ${({ theme, variant }) => variant ? theme.sections[variant].headingClr: "inherit"}; + text-align: center; + + @media (min-width: ${({ theme }) => theme.breakpoints.tablet}) { + text-align: ${({ textAlign }) => textAlign === "left" ? "left" : "center"}; + } + + @media (min-width: ${({ theme }) => theme.breakpoints.desktop}) { + margin-bottom: ${({ marginBottom }) => marginBottom === "0" ? "0" : "60px"}; + + } + + +` + +export const SectionTitle = ({ title, variant, textAlign, marginBottom }) => { + return ( + + {title} + + ) +} diff --git a/src/components/typography/SkillTitle.jsx b/src/components/typography/SkillTitle.jsx new file mode 100644 index 00000000..825d2811 --- /dev/null +++ b/src/components/typography/SkillTitle.jsx @@ -0,0 +1,18 @@ +import styled from 'styled-components' + +export const StyledSkillTitle = styled.h3` + background-color: ${({ theme, $index }) => theme.sections.skills.subBgClr[$index].bgClr || "inherit"}; + color: ${({ theme, $index }) => theme.sections.skills.subBgClr[$index].textClr || "inherit"}; + max-width: fit-content; + padding: 0 8px; + + +` + +export const SkillTitle = ({ title, index }) => { + return ( + + {title} + + ) +} \ No newline at end of file diff --git a/src/components/typography/typography.js b/src/components/typography/typography.js new file mode 100644 index 00000000..8e9bc438 --- /dev/null +++ b/src/components/typography/typography.js @@ -0,0 +1,6 @@ +export { BodyText } from './BodyText' +export { CardTitle } from './CardTitle' +export { PageTitle } from './PageTitle' +export { SectionTitle } from './SectionTitle' +export { SkillTitle } from './SkillTitle' +export { ListTitle } from './ListTitle' \ No newline at end of file diff --git a/src/components/ui/Avatar.jsx b/src/components/ui/Avatar.jsx new file mode 100644 index 00000000..2bcfb4bd --- /dev/null +++ b/src/components/ui/Avatar.jsx @@ -0,0 +1,20 @@ +import styled from 'styled-components' +import aboutMe from '../../data/aboutMe' + +export const StyledAvatar = styled.img` + width: 164px; + aspect-ratio: 1 / 1; + border-radius: 50%; + object-fit: cover; + object-position: 50% 35%; + flex-shrink: 0; + display: block; + margin: 0 auto; + +`; + +export const Avatar = () => { + return ( + + ) +} \ No newline at end of file diff --git a/src/components/ui/Button.jsx b/src/components/ui/Button.jsx new file mode 100644 index 00000000..f6fdb6bb --- /dev/null +++ b/src/components/ui/Button.jsx @@ -0,0 +1,35 @@ +import styled from "styled-components" + +const StyledButton = styled.a` + background-color: ${({ theme, variant }) => variant ? theme.buttons[variant].bgClr : "inherit"}; + color: ${({ theme, variant }) => variant ? theme.buttons[variant].textClr : "inherit"}; + border-radius: var(--btn-border-radius); + font-size: var(--btn-font-size); + font-weight: var(--btn-font-weight); + font-family: var(--btn-font); + margin: var(--btn-margin); + padding: var(--btn-padding); + text-decoration: none; + display: flex; + gap: 8px; + align-items: center; + justify-content: center; + transition: background-color 0.3s ease; + margin-bottom: 1rem; + max-width: fit-content; + + + &:hover { + background-color: ${({ theme, variant }) => variant ? theme.buttons[variant].hoverBgClr : "inherit"}; + color: ${({ theme, variant }) => variant ? theme.buttons[variant].hoverTextClr : "inherit"}; + } +` + +export const Button = ({ href, children, text, variant } ) => { + return ( + + {children} + {text} + + ) +} \ No newline at end of file diff --git a/src/components/ui/CardImage.jsx b/src/components/ui/CardImage.jsx new file mode 100644 index 00000000..8db642f8 --- /dev/null +++ b/src/components/ui/CardImage.jsx @@ -0,0 +1,5 @@ +export const CardImage = ({src, alt}) => { + return ( + {alt}/ + ) +} \ No newline at end of file diff --git a/src/components/ui/ListItem.jsx b/src/components/ui/ListItem.jsx new file mode 100644 index 00000000..7e04a47a --- /dev/null +++ b/src/components/ui/ListItem.jsx @@ -0,0 +1,13 @@ +import styled from "styled-components" + +const StyledListItem = styled.li` + font-family: var(--font-heading); +` + +export const ListItem = ({ item }) => { + return ( + + {item} + + ) +} \ No newline at end of file diff --git a/src/components/ui/SoMeLink.jsx b/src/components/ui/SoMeLink.jsx new file mode 100644 index 00000000..9d938325 --- /dev/null +++ b/src/components/ui/SoMeLink.jsx @@ -0,0 +1,7 @@ +export const SoMeLink = ({ href, src, alt }) => { + return ( + + {alt} + + ) +} \ No newline at end of file diff --git a/src/components/ui/SvgIcon.jsx b/src/components/ui/SvgIcon.jsx new file mode 100644 index 00000000..0762301a --- /dev/null +++ b/src/components/ui/SvgIcon.jsx @@ -0,0 +1,13 @@ +export const SvgIcon = ({ icon, width,viewBox }) => { + return ( + + {icon} + + ) +} + diff --git a/src/components/ui/Tag.jsx b/src/components/ui/Tag.jsx new file mode 100644 index 00000000..05afa8a4 --- /dev/null +++ b/src/components/ui/Tag.jsx @@ -0,0 +1,19 @@ +import styled from "styled-components" + +const StyledTag = styled.span` + background-color: var(--tag-bg-clr); + color: var(--tag-txt-clr); + padding: 1px 8px; + font-size: var(--tag-font-size); + font-weight: var(--tag-font-weight); + font-family: var(--tag-font); + max-width: fit-content; +` + +export const Tag = ({ tag }) => { + return ( + + {tag} + + ) +} \ No newline at end of file diff --git a/src/components/ui/UnorderdList.jsx b/src/components/ui/UnorderdList.jsx new file mode 100644 index 00000000..eb78b455 --- /dev/null +++ b/src/components/ui/UnorderdList.jsx @@ -0,0 +1,17 @@ +import styled from "styled-components" + +const StyledUL = styled.ul` +gap: 8px; +` + +import { ListItem } from "./ui" + +export const UnorderedList = ({ items }) => { + return ( + + {items.map((item, index) => ( + + ))} + + ) +} \ No newline at end of file diff --git a/src/components/ui/ui.js b/src/components/ui/ui.js new file mode 100644 index 00000000..0709a7a8 --- /dev/null +++ b/src/components/ui/ui.js @@ -0,0 +1,8 @@ +export { Avatar } from './Avatar' +export { Button } from './Button' +export { CardImage } from './CardImage' +export { SoMeLink } from './SoMeLink' +export { Tag } from './Tag' +export { UnorderedList } from './UnorderdList' +export { ListItem } from './ListItem' +export { SvgIcon } from './SvgIcon' \ No newline at end of file diff --git a/src/data/aboutMe.json b/src/data/aboutMe.json new file mode 100644 index 00000000..1881e1e1 --- /dev/null +++ b/src/data/aboutMe.json @@ -0,0 +1,11 @@ +{ + "name": "Mikaela Mangert Sturk", + "phone": "+46(0)707 73 17 27", + "email": "mikaelasturk@gmail.com", + "intro": "Hi, I'm Mikaela Sturk", + "role": "Fullstack Developer", + "avatar": "/images/avatar.jpg", + "alt": "Avatar of Mikaela Sturk", + "description": "Mikaela is a thoughtful and detail-oriented developer with a strong sense for structure, clarity, and user-focused design. With a background in music publishing and administration, she brings a rare combination of precision, creativity, and problem-solving into her code. She builds intuitive, accessible applications and approaches complex challenges with calm, analytical thinking. Her drive to learn and improve makes her a valuable addition to any team and a promising developer in the tech field.", + "techStack": "HTML, CSS, Flexbox, JavaScript, ES6, JSX, React, React Hooks, Node.js, Mongo DB, Web Accessibility, API:s, mob-programming, pair-programming, Github." +} \ No newline at end of file diff --git a/src/data/articles.json b/src/data/articles.json new file mode 100644 index 00000000..0761ad3a --- /dev/null +++ b/src/data/articles.json @@ -0,0 +1,20 @@ +{ + "articles": [ + { + "name": "Title", + "image": "", + "date": "month year", + "alt": "Picture of...", + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "link": "" + }, + { + "name": "Title", + "image": "", + "date": "month year", + "alt": "Picture of...", + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + "link": "" + } + ] +} diff --git a/src/data/projects.json b/src/data/projects.json index 7c426028..8aa7a964 100644 --- a/src/data/projects.json +++ b/src/data/projects.json @@ -1,28 +1,32 @@ { "projects": [ { - "name": "Business site", - "image": "https://images.unsplash.com/photo-1557008075-7f2c5efa4cfd?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2497&q=80", + "name": "Weather app", + "image": "", + "alt": "Weather app project screenshot", + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.", "tags": [ "HTML5", "CSS3", - "JavaScript" + "JavaScript", + "TypeScript", + "APIs" ], - "netlify": "link", - "github": "link" + "netlify": "https://mikaelas-js-project-recipe-library.netlify.app/", + "github": "https://github.com/mikaelasturk/js-project-recipe-library" }, { - "name": "Weather app", - "image": "https://images.unsplash.com/photo-1520792532857-293bd046307a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=2370&q=80", + "name": "Business site", + "image": "", + "alt": "Business site project screenshot", + "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "tags": [ "HTML5", "CSS3", - "JavaScript", - "TypeScript", - "APIs" + "JavaScript" ], - "netlify": "link", - "github": "link" + "netlify": "https://malizioso-test1.netlify.app/", + "github": "https://github.com/mikaelasturk/js-project-business-site" } ] -} \ No newline at end of file +} diff --git a/src/data/skills.json b/src/data/skills.json new file mode 100644 index 00000000..8e9373b2 --- /dev/null +++ b/src/data/skills.json @@ -0,0 +1,47 @@ +{ + "skillsList": [ + { + "title": "Code", + "skills": [ + "HTML5", + "CSS3", + "JavaScript ES6", + "TypeScript", + "React", + "Styled Components", + "GitHub" + ] + }, + { + "title": "Toolbox", + "skills": [ + "Figma", + "Slack", + "Notion", + "GitHub Issues", + "Excel" + ] + }, + { + "title": "Upcoming", + "skills": [ + "Tailwind", + "Node.js", + "MongoDB", + "Express.js", + "LIA" + ] + }, + { + "title": "More", + "skills": [ + "Copyright Administration", + "Music Publishing Administration", + "Corporate Administration", + "Music Industry Knowledge", + "Social Media Management", + "Bookkeeping" + ] + } + ] +} 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/styles/FlexWrapper.jsx b/src/styles/FlexWrapper.jsx new file mode 100644 index 00000000..8813cd78 --- /dev/null +++ b/src/styles/FlexWrapper.jsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +export const FlexWrapper = styled.div` + display: flex; + flex-direction: ${({ direction }) => direction || 'row'}; + justify-content: ${({ justify }) => justify || 'flex-start'}; + align-items: ${({ align }) => align || 'stretch'}; + gap: ${({ gap }) => gap || '0px'}; + flex-wrap: ${({ wrap }) => wrap || 'nowrap'}; + +`; \ No newline at end of file diff --git a/src/styles/GlobalStyle.jsx b/src/styles/GlobalStyle.jsx new file mode 100644 index 00000000..8221dc39 --- /dev/null +++ b/src/styles/GlobalStyle.jsx @@ -0,0 +1,229 @@ +import { createGlobalStyle } from "styled-components" + + +export const GlobalStyle = createGlobalStyle` + :root { + /* Font settings */ + --font-heading: "Montserrat", Arial, sans-serif; + --font-txt: "Hind", Arial, sans-serif; + --weight-normal: 400; + --weight-semibold: 550; + --weight-bold: 670; + --xxs-font-size: 18px; + --xs-font-size: 24px; + --s-font-size: 32px; + --m-font-size: 50px; + --l-font-size: 68px; + --xl-font-size: 80px; + --h1-font-weight: var(--weight-bold); + --h2-font-weight: var(--weight-bold); + --h3-font-weight: var(--weight-semibold); + + /* Mobile */ + --h1-font-size-mobile: 27.5px; + --h2-font-size-mobile: 50px; + --h3-font-size-mobile: 24px; + + /* Tablet */ + --h1-font-size-tablet: 55px; + --h2-font-size-tablet: 55px; + --h3-font-size-tablet: 24px; + + /* Desktop */ + --h1-font-size-desktop: 68px; + --h2-font-size-desktop: 68px; + --h3-font-size-desktop: 28px; + + /* Desktop */ + --h1-font-size-desktopL: 80px; + --h2-font-size-desktopL: 80px; + --h3-font-size-desktopL: 32px; + --p-font-size-desktopL: 28px; + + /* Main colors */ + --1st-clr: #000000; + --2nd-clr: #FFFFFF; + --3rd-clr: #0B24F5; + --4th-clr: #FF4575; + + /* Highlight colors */ + --highlight-1st: var(--3rd-clr); + --highlight-2nd: var(--4th-clr); + + /* Background colors */ + --1st-bg-clr: var(--2nd-clr); + --2nd-bg-clr: var(--3rd-clr); + --3rd-bg-clr: #FFECEA; + + /* Accent colors */ + --acc-1st-clr: #EB5577; + --acc-2nd-clr: #2483E0; + --acc-3rd-clr: #6DB486; + --acc-4th-clr: #FFDE30; + + /* SoMe links/icon colors*/ + --icon-clr: #D0D0D0; + --icon-hover-clr: #000000; + + /* Tag styling */ + --tag-bg-clr: #000000; + --tag-txt-clr: #FFFFFF; + --tag-font-size: 16px; + --tag-font-weight: var(--weight-semibold); + --tag-font: var(--font-heading); + + /* Button styling */ + --1st-btn-bg-clr: #F5F5F5; + --1st-btn-txt-clr: #333333; + --2nd-btn-bg-clr: #FFFFFF; + --2nd-btn-txt-clr: #333333; + --btn-border-radius: 40px; + --btn-font-size: 20px; + --btn-font-weight: var(--weight-semibold); + --btn-font: var(--font-heading); + --btn-margin: 0px; + --btn-padding: 0 16px 0 0; + + /* Button hover color variations */ + --demo-btn-hover-bg-clr: var(--highlight-1st); + --demo-btn-hover-txt-clr: #FFFFFF; + --code-btn-hover-bg-clr: var(--highlight-2nd); + --code-btn-hover-txt-clr: #FFFFFF; + --article-btn-hover-bg-clr: #FFD338; + --article-btn-hover-txt-clr: #000000; + + /* Hero, Project and Contact section colors */ + --section1-bg-clr: var(--1st-bg-clr); + --section1-txt-clr: var(--1st-clr); + --section1-highlight-clr: var(--highlight-1st); + --section1-btn-bg-clr: var(--1st-btn-bg-clr); + --section1-btn-txt-clr: var(--1st-btn-txt-clr); + + /* Tech & Skill Section colors */ + --section2-bg-clr: var(--2nd-bg-clr); + --section2-txt-clr: var(--2nd-clr); + --section2-heading-clr: var(--2nd-clr); + --section2-subheading1-bg-clr: var(--acc-1st-clr); + --section2-subheading1-txt-clr: var(--2nd-clr); + --section2-subheading2-bg-clr: var(--acc-2nd-clr); + --section2-subheading2-txt-clr: var(--2nd-clr); + --section2-subheading3-bg-clr: var(--acc-3rd-clr); + --section2-subheading3-txt-clr: var(--2nd-clr); + --section2-subheading4-bg-clr: var(--acc-4th-clr); + --section2-subheading4-txt-clr: var(--3rd-clr); + + /* Article Section colors */ + --section3-bg-clr: var(--3rd-bg-clr); + --section3-txt-clr: var(--1st-clr); + --section3-highlight-clr: var(--highlight-2nd); + --section3-btn-bg-clr: var(--2nd-btn-bg-clr); + --section3-btn-txt-clr: var(--2nd-btn-txt-clr); + } + + /* Reset */ + *, *::before, *::after { + box-sizing: border-box; + } + + * { + margin: 0; + padding: 0; + } + + html { + scroll-behavior: smooth; + } + + body { + min-height: 100vh; + line-height: 1.6; + font-family: var(--font-txt); + max-width: 100vw; + } + + img, picture, video, canvas, svg { + display: block; + max-width: 100%; + } + + input, button, textarea, select { + font: inherit; + } + + h1, h2, h3, h4, h5, h6 { + overflow-wrap: break-word; + font-family: var(--font-heading); + line-height: 1.2; + } + + p { + /* overflow-wrap: break-word; */ + font-size: var(--xxs-font-size); + } + + ul, ol { + list-style: none; + } + + h1 { + font-size: var(--h1-font-size-mobile); + font-weight: var(--h1-font-weight); + } + + h2 { + font-size: var(--h2-font-size-mobile); + font-weight: var(--h2-font-weight); + } + + h3 { + font-size: var(--h3-font-size-mobile); + font-weight: var(--h3-font-weight); + } + + + @media (min-width: ${({ theme }) => theme.breakpoints.tablet}) { + h1 { + font-size: var(--h1-font-size-tablet); + } + + h2 { + font-size: var(--h2-font-size-tablet); + } + + h3 { + font-size: var(--h3-font-size-tablet); + } + } + + @media (min-width: ${({ theme }) => theme.breakpoints.desktop}) { + h1 { + font-size: var(--h1-font-size-desktop); + } + + h2 { + font-size: var(--h2-font-size-desktop); + } + + h3 { + font-size: var(--h3-font-size-desktop); + } + } + + @media (min-width: ${({ theme }) => theme.breakpoints.desktopLarge}) { + h1 { + font-size: var(--h1-font-size-desktopL); + } + + h2 { + font-size: var(--h2-font-size-desktopL); + } + + h3 { + font-size: var(--h3-font-size-desktopL); + } + + p { + font-size: var(--p-font-size-desktopL); + } + } +` diff --git a/src/styles/StyledSection.jsx b/src/styles/StyledSection.jsx new file mode 100644 index 00000000..31ff1e33 --- /dev/null +++ b/src/styles/StyledSection.jsx @@ -0,0 +1,31 @@ +import styled from "styled-components"; + +export const StyledSection = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + background-color: ${({ theme, variant }) => variant ? theme.sections[variant].bgClr : "inherit"}; + color: ${({ theme, variant }) => variant ? theme.sections[variant].textClr : "inherit"}; + padding: ${({ padding }) => padding || "120px 16px"}; + gap: ${({ gap }) => gap || "56px"}; + + /* Tablet and up --> */ + @media (min-width: ${({ theme }) => theme.breakpoints.tablet}) { + display: flex; + flex-direction: column; + justify-content: center; + padding: ${({ paddingTablet }) => paddingTablet || "120px 32px"}; + gap: ${({ gapTablet }) => gapTablet || "gap"}; + } + + /* Desktop and up --> */ + @media (min-width: ${({ theme }) => theme.breakpoints.desktop}) { + padding: ${({ paddingDesktop }) => paddingDesktop || "120px 140px"}; + gap: ${({ gapDesktop }) => gapDesktop || "gap"}; + } + /* Desktop Large and up --> */ + @media (min-width: ${({ theme }) => theme.breakpoints.desktopLarge}) { + padding: ${({ paddingDesktopL }) => paddingDesktopL || "140px 230px"}; + gap: ${({ gapDesktopL }) => gapDesktopL || "gap"}; + } +` \ No newline at end of file diff --git a/src/styles/styles.js b/src/styles/styles.js new file mode 100644 index 00000000..3a807677 --- /dev/null +++ b/src/styles/styles.js @@ -0,0 +1,4 @@ +export { GlobalStyle } from './GlobalStyle' +export { theme } from './theme' +export { StyledSection } from './StyledSection' +export { FlexWrapper } from './FlexWrapper' \ No newline at end of file diff --git a/src/styles/theme.jsx b/src/styles/theme.jsx new file mode 100644 index 00000000..434b7d6d --- /dev/null +++ b/src/styles/theme.jsx @@ -0,0 +1,94 @@ +export const theme = { + breakpoints: { + mobile: "360px", + tablet: "745px", //(768 is my usual default) + desktop: "1024px", + desktopLarge: "1440px", + }, + + //---------------// + // Button themes // + //---------------// + buttons: { + demo: { + icon: "svg here?", + bgClr: "var(--section1-btn-bg-clr)", + textClr: "var(--section1-btn-txt-clr)", + hoverBgClr: "var(--demo-btn-hover-bg-clr)", + hoverTextClr: "var(--demo-btn-hover-txt-clr)", + }, + code: { + icon: "svg here?", + bgClr: "var(--section1-btn-bg-clr)", + textClr: "var(--section1-btn-txt-clr)", + hoverBgClr: "var(--code-btn-hover-bg-clr)", + hoverTextClr: "var(--code-btn-hover-txt-clr)", + }, + article: { + icon: "svg here?", + bgClr: "var(--section3-btn-bg-clr)", + textClr: "var(--section3-btn-txt-clr)", + hoverBgClr: "var(--article-btn-hover-bg-clr)", + hoverTextClr: "var(--article-btn-hover-txt-clr)", + }, + }, + + //----------------// + // Section themes // + //----------------// + sections: { + hero: { + bgClr: "var(--section1-bg-clr)", + textClr: "var(--section1-txt-clr)", + headingClr: "var(--section1-highlight-clr)", + }, + tech: { + bgClr: "var(--section2-bg-clr)", + textClr: "var(--section2-txt-clr)", + headingClr: "var(--section2-heading-clr)", + }, + projects: { + bgClr: "var(--section1-bg-clr)", + textClr: "var(--section1-txt-clr)", + headingClr: "var(--section1-highlight-clr)", + }, + articles: { + bgClr: "var(--section3-bg-clr)", + textClr: "var(--section3-txt-clr)", + headingClr: "var(--section3-highlight-clr)", + }, + skills: { + bgClr: "var(--section2-bg-clr)", + textClr: "var(--section2-txt-clr)", + headingClr: "var(--section2-heading-clr)", + subBgClr: [ + { + id: "code", + bgClr: "var(--section2-subheading1-bg-clr)", + textClr: "var(--section2-subheading1-txt-clr)" + + }, + { + id: "toolbox", + bgClr: "var(--section2-subheading2-bg-clr)", + textClr: "var(--section2-subheading2-txt-clr)" + }, + { + id: "upcoming", + bgClr: "var(--section2-subheading3-bg-clr)", + textClr: "var(--section2-subheading3-txt-clr)" + }, + { + id: "more", + bgClr: "var(--section2-subheading4-bg-clr)", + textClr: "var(--section2-subheading4-txt-clr)" + }, + ], + }, + contact: { + bgClr: "var(--section1-bg-clr)", + textClr: "var(--section1-txt-clr)", + headingClr: "var(--section1-highlight-clr)", + }, + }, +}; \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 8b0f57b9..03066fb6 100644 --- a/vite.config.js +++ b/vite.config.js @@ -3,5 +3,11 @@ import react from '@vitejs/plugin-react' // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], -}) + plugins: [ + react({ + babel: { + plugins: [['babel-plugin-styled-components', { displayName: true }]] + } + }) + ] +}) \ No newline at end of file