diff --git a/README.md b/README.md index cfb23c60..cb90cdee 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,32 @@ -

- - Project Banner Image - -

- -# Design Handoff Project - -Replace this readme with your own information about your project. - -Start by briefly describing the assignment in a sentence or two. Keep it short and to the point. - -## Getting Started with the Project - -### Dependency Installation & Startup Development Server - -Once cloned, navigate to the project's root directory and this project uses npm (Node Package Manager) to manage its dependencies. - -The command below is a combination of installing dependencies, opening up the project on VS Code and it will run a development server on your terminal. - -```bash -npm i && code . && npm run dev -``` - -### The Problem - -Describe how you approached to problem, and what tools and techniques you used to solve it. How did you plan? What technologies did you use? If you had more time, what would be next? - -### View it live - -Every project should be deployed somewhere. Be sure to include the link to the deployed project so that the viewer can click around and see what it's all about. - -## Instructions - - - See instructions of this project - +# Kindness & Compassion - A Social Support Platform +This project is a collaboration between two developer students and one UX design student as part of Technigo Bootcamp. The website serves as a community hub where users can engage in meaningful conversations, find support groups, and stay updated on activities designed to foster kindness and compassion. + +# Features & Structure +## Global Styles: +The project uses styled-components for a modular and responsive design, with a focus on accessibility and smooth user experience across devices. +## Hero Section: +A visually engaging hero section promotes the platform’s mission, inviting users to learn more about spreading good deeds for a better world. +## Support Groups: +The platform features a section for users to join or start support groups, both online and offline, for personal connection and growth. +## Carousel Section: +A dynamic carousel displays testimonials and community stories, allowing users to swipe through inspiring content. +## Calendar of Events: +The platform includes a calendar with upcoming activities, offering users easy access to relevant events. +## Achievements Section: +A visually appealing section showcases key metrics, like the number of volunteer groups, events, and activities, illustrating the platform's impact. +## Footer: +A footer with social media links, contact information, and a brief overview of the platform’s mission and vision, further encouraging community engagement. + +# Technologies Used +React: For building the user interface and managing application state. + +Styled Components: For styling the app with scoped, reusable components. + +React Swipeable: To implement swipeable functionality for mobile devices. + +React Hooks: For managing state and lifecycle in a functional programming style. + +Responsive Design: The app is fully responsive, ensuring a seamless experience across mobile, tablet, and desktop devices. + +# View live: +https://project-design-handoff.netlify.app/ \ No newline at end of file diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 00000000..0639bf76 --- /dev/null +++ b/babel.config.json @@ -0,0 +1,5 @@ +{ + "presets": [ + "@babel/preset-env" + ] +} \ No newline at end of file diff --git a/index.html b/index.html index a52ab900..3c68c747 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,24 @@ - - - - - Music Releases - Project - Week 5 - - -
- - - + + + + + + Kindness and Compassion + + + + + + + +
+ + + + \ No newline at end of file diff --git a/package.json b/package.json index 4ba5ff02..f1c95580 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,20 @@ "preview": "vite preview" }, "dependencies": { + "prop-types": "^15.8.1", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-swipeable": "^7.0.2", + "styled-components": "^6.1.13" }, "devDependencies": { + "@babel/cli": "^7.25.9", + "@babel/core": "^7.26.0", + "@babel/preset-env": "^7.26.0", "@types/react": "^18.2.15", "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^4.0.3", + "babel-plugin-styled-components": "^2.1.4", "eslint": "^8.45.0", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0", diff --git a/public/assets/HeaderSun.png b/public/assets/HeaderSun.png new file mode 100644 index 00000000..90f04605 Binary files /dev/null and b/public/assets/HeaderSun.png differ diff --git a/public/assets/Menu.svg b/public/assets/Menu.svg new file mode 100644 index 00000000..806c28bd --- /dev/null +++ b/public/assets/Menu.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/Search.svg b/public/assets/Search.svg new file mode 100644 index 00000000..8dcad942 --- /dev/null +++ b/public/assets/Search.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/supportgroup-cancer.png b/public/supportgroup-cancer.png new file mode 100644 index 00000000..3f141b57 Binary files /dev/null and b/public/supportgroup-cancer.png differ diff --git a/public/supportgroup-psych.png b/public/supportgroup-psych.png new file mode 100644 index 00000000..af1c674d Binary files /dev/null and b/public/supportgroup-psych.png differ diff --git a/public/supportgroup-stress.png b/public/supportgroup-stress.png new file mode 100644 index 00000000..40668945 Binary files /dev/null and b/public/supportgroup-stress.png differ diff --git a/src/App.jsx b/src/App.jsx index 1091d431..eabb1610 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,3 +1,27 @@ -export const App = () => { - return
Find me in src/app.jsx!
; +import { Welcome } from './sections/Welcome'; +import { HeaderSection } from './sections/HeaderSection'; +import { GlobalStyles } from './styles/GlobalStyles'; +import { HeaderHeroSection } from './sections/HeaderHeroSection'; +import { FooterSection } from './sections/Footer'; +import { SupportgroupSection } from './sections/Supportgroup'; +import { CarouselSection } from './sections/Carousel'; +import { Calendar } from './sections/Calendar'; +import { Achievements } from './sections/Achievements'; + +const App = () => { + return ( + <> + + + + + + + + + + + ); }; + +export default App; \ No newline at end of file diff --git a/src/assets/Facebook.png b/src/assets/Facebook.png new file mode 100644 index 00000000..e2e6a8fe Binary files /dev/null and b/src/assets/Facebook.png differ diff --git a/src/assets/Hero.png b/src/assets/Hero.png new file mode 100644 index 00000000..3f7bb70c Binary files /dev/null and b/src/assets/Hero.png differ diff --git a/src/assets/Instagram.png b/src/assets/Instagram.png new file mode 100644 index 00000000..713c90c0 Binary files /dev/null and b/src/assets/Instagram.png differ diff --git a/src/assets/calendarcard1.png b/src/assets/calendarcard1.png new file mode 100644 index 00000000..90904209 Binary files /dev/null and b/src/assets/calendarcard1.png differ diff --git a/src/assets/calendarcard2.png b/src/assets/calendarcard2.png new file mode 100644 index 00000000..2034e1a2 Binary files /dev/null and b/src/assets/calendarcard2.png differ diff --git a/src/carousel.json b/src/carousel.json new file mode 100644 index 00000000..b1d68a05 --- /dev/null +++ b/src/carousel.json @@ -0,0 +1,18 @@ +[ + { + "text": "Det kändes så mycket bättre att inse att jag inte var ensam om de tankarna.", + "author": "Sandra, deltagare i bröstcancergruppen" + }, + { + "text": "Det var som om jag fick en helt ny syn på livet efter det här.", + "author": "Johan, deltagare i bröstcancergruppen" + }, + { + "text": "Jag känner mig stärkt och hoppfull.", + "author": "Anna, deltagare i bröstcancergruppen" + }, + { + "text": "Det var en livsförändrande upplevelse.", + "author": "Erik, deltagare i bröstcancergruppen" + } +] \ No newline at end of file diff --git a/src/components/CalendarCard.jsx b/src/components/CalendarCard.jsx new file mode 100644 index 00000000..2873d0e4 --- /dev/null +++ b/src/components/CalendarCard.jsx @@ -0,0 +1,42 @@ +import styled from 'styled-components'; +import { DateCard } from '../ui/DateCard'; +import calendarcard1 from '../assets/calendarcard1.png'; +import calendarcard2 from '../assets/calendarcard2.png'; +import { CalendarCardTitle, CalendarCardText } from '../ui/Typography'; + +const CardContainer = styled.div` + position: relative; + display: flex; + flex-direction: column; + width: 90%; + max-width: 400px; + margin: 1rem auto; + background-color: #f3c1e2; + padding: 0; +`; + +const Image = styled.img` + width: 100%; + height: auto; + border-radius: 0; +`; + +const CardContent = styled.div` + padding: 1rem; +`; + +export const CalendarCard = ({ image, date, month, title, location, time }) => { + const selectedImage = image === 'calendarcard1' ? calendarcard1 : calendarcard2; + + return ( + + {title} + + + {title} + {location} + {time} + + + ); +}; diff --git a/src/components/CarouselCard.jsx b/src/components/CarouselCard.jsx new file mode 100644 index 00000000..163a6266 --- /dev/null +++ b/src/components/CarouselCard.jsx @@ -0,0 +1,23 @@ +import styled from 'styled-components'; +import PropTypes from 'prop-types'; + +const CarouselCardStyles = styled.div` +display: flex; +width: 12.25rem; +height: 22.6rem; +flex-direction: column; +justify-content: flex-start; +background-color: #C1F0EF; +padding: 1rem 1rem 2rem 1rem; +` +export const CarouselCard = ({ children }) => { + return ( + + {children} + + ) +} + +CarouselCard.propTypes = { + children: PropTypes.node.isRequired, +} \ No newline at end of file diff --git a/src/components/HeaderCard.jsx b/src/components/HeaderCard.jsx new file mode 100644 index 00000000..fdf848bd --- /dev/null +++ b/src/components/HeaderCard.jsx @@ -0,0 +1,18 @@ +import styled from 'styled-components'; + +export const HeaderCard = styled.div` + grid-column: span 4; + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 1rem 0; + + @media (min-width: 768px) { + grid-column: span 8; + } + + @media (min-width: 1200px) { + grid-column: span 12; + } +`; diff --git a/src/components/StatisticContainer.jsx b/src/components/StatisticContainer.jsx new file mode 100644 index 00000000..e69de29b diff --git a/src/components/SupportgroupCard.jsx b/src/components/SupportgroupCard.jsx new file mode 100644 index 00000000..68e9d025 --- /dev/null +++ b/src/components/SupportgroupCard.jsx @@ -0,0 +1,81 @@ +import { useState, useEffect } from 'react'; +import styled from 'styled-components'; +import { SupportgroupImage } from '../ui/SupportgroupImage'; +import { SupportgroupTextContainer } from '../ui/SupportgroupTextContainer'; +import { HeaderText } from '../ui/Typography'; +import supportgroupData from '../supportgroups.json'; + +const SupportgroupCardStyle = styled.div` +display: flex; +flex-direction: column; +align-items: center; +justify-content: center; +width: 20rem; +height: 16.875rem; +border-radius: var(--Radius-200, 0.5rem); +box-shadow: 1px 4px 4px 0px rgba(0, 0, 0, 0.25); +overflow: hidden; +margin-bottom: 2.5rem; + +@media(max-width: 360px){ + width: 18rem; +} + +&:hover { + background-color: rgba(255, 255, 255, 0.8); + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25) inset; + border: var(--sds-size-stroke-border) solid #000; + cursor: pointer; + filter: brightness(0.7); + }` + +const SupportgroupCardContainerStyle = styled.div` +display: flex; +flex-direction: column; +justify-content: center; +align-items: center; +grid-column: span 4; +max-width: 700px; + +@media (min-width: 768px){ + grid-column: span 8; + } + +@media (min-width: 1200px){ + grid-column: span 12; + } +` + +export const SupportgroupCard = () => { + const [supportgroups, setSupportgroups] = useState([]) + useEffect(() => { + setSupportgroups(supportgroupData) + console.log(supportgroupData) + }, []); + + const handleCardClick = (supportgroup) => { + console.log(`Card clicked: ${supportgroup.title}`) + }; + + return ( + <> + + {supportgroups.map((supportgroup, index) => ( + < SupportgroupCardStyle + key={index} + onClick={() => handleCardClick(supportgroup)} + > + + + {supportgroup.title} + + + )) + } + + + ) +} \ No newline at end of file diff --git a/src/components/WelcomeCard.jsx b/src/components/WelcomeCard.jsx new file mode 100644 index 00000000..f1276843 --- /dev/null +++ b/src/components/WelcomeCard.jsx @@ -0,0 +1,18 @@ +import styled from 'styled-components'; + +export const WelcomeCard = styled.div` + grid-column: span 4; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-start; + background-color: #fff; + + @media (min-width: 768px) { + grid-column: span 8; + } + + @media (min-width: 1200px) { + grid-column: span 12; + } +` diff --git a/src/index.css b/src/index.css deleted file mode 100644 index 4558f538..00000000 --- a/src/index.css +++ /dev/null @@ -1,13 +0,0 @@ -:root { - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", - "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -code { - font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", - monospace; -} diff --git a/src/main.jsx b/src/main.jsx index 51294f39..02471200 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,7 +1,6 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import { App } from "./App.jsx"; -import "./index.css"; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App.jsx'; ReactDOM.createRoot(document.getElementById("root")).render( diff --git a/src/sections/Achievements.jsx b/src/sections/Achievements.jsx new file mode 100644 index 00000000..5f0b4e3b --- /dev/null +++ b/src/sections/Achievements.jsx @@ -0,0 +1,36 @@ +import styled from 'styled-components'; +import { AchievementElement } from '../ui/AchievementElement'; +import { AchievementsTitle } from '../ui/Typography'; + +const AchievementsSection = styled.div` + display: flex; + flex-direction: column; + align-items: center; + padding: 2rem; + max-width: 700px; + margin: 0 auto; +`; + +const AchievementsContainer = styled.div` + display: flex; + justify-content: space-between; + gap: 1.5rem; + width: 100%; + + @media (max-width: 400px){ +gap: 0.5rem; + } +`; + +export const Achievements = () => { + return ( + + Vår spridning + + + + + + + ); +}; diff --git a/src/sections/Calendar.jsx b/src/sections/Calendar.jsx new file mode 100644 index 00000000..9cbe795e --- /dev/null +++ b/src/sections/Calendar.jsx @@ -0,0 +1,44 @@ +import styled from 'styled-components'; +import { CalendarCard } from '../components/CalendarCard'; +import { Spacer } from '../ui/Spacer'; +import { CalendarTitle } from '../ui/Typography'; +import { CalendarButton } from '../ui/Typography'; + +const CalendarContainer = styled.div` + padding: 2rem; + background-color: #fff; + display: flex; + flex-direction: column; + align-items: center; + + @media (min-width: 768px) { + justify-content: space-around; + } +`; + +export const Calendar = () => { + return ( + + + Aktiviteter + + + Fler aktiviteter + + + ); +}; diff --git a/src/sections/Carousel.jsx b/src/sections/Carousel.jsx new file mode 100644 index 00000000..4682c367 --- /dev/null +++ b/src/sections/Carousel.jsx @@ -0,0 +1,82 @@ +import { useSwipeable } from 'react-swipeable'; +import { useState, useEffect, useRef } from 'react'; +import { CarouselCard } from '../components/CarouselCard'; +import { CarouselAuthor, CarouselHeader, CarouselText } from '../ui/Typography'; +import carouselData from '../carousel.json'; +import { CarouselContainer } from '../ui/CarouselContainer'; +import { Wrapper } from '../ui/Wrapper'; +import { SwipeStatus } from '../ui/SwipeStatus'; + +export const CarouselSection = () => { + const [currentIndex, setCurrentIndex] = useState(0); + const [cardWidth, setCardWidth] = useState(0); + const [showSwipeStatus, setShowSwipeStatus] = useState(true); + const carouselRef = useRef(null); + + const updateCardWidth = () => { + if (carouselRef.current) { + const containerWidth = carouselRef.current.offsetWidth; + const calculatedWidth = containerWidth * (2 / 3); + setCardWidth(calculatedWidth); + } + }; + + const updateScreenWidth = () => { + if (window.innerWidth >= 800) { + setShowSwipeStatus(false); + } else { + setShowSwipeStatus(true); + } + }; + + useEffect(() => { + updateCardWidth(); + updateScreenWidth(); + + window.addEventListener('resize', () => { + updateCardWidth(); + updateScreenWidth(); + }); + + return () => { + window.removeEventListener('resize', updateScreenWidth); + window.removeEventListener('resize', updateCardWidth); + }; + }, []); + + const nextCard = () => { + setCurrentIndex((prevIndex) => (prevIndex + 1) % carouselData.length); + }; + + const prevCard = () => { + setCurrentIndex((prevIndex) => (prevIndex - 1 + carouselData.length) % carouselData.length); + }; + + const handlers = useSwipeable({ + onSwipedLeft: nextCard, + onSwipedRight: prevCard, + preventDefaultTouchmoveEvent: true, + trackMouse: true + }); + + return ( + + + {carouselData.map((card, index) => ( + + + {card.text} + {card.author} + + ))} + + {showSwipeStatus && } + + ); +}; \ No newline at end of file diff --git a/src/sections/Footer.jsx b/src/sections/Footer.jsx new file mode 100644 index 00000000..645cd7be --- /dev/null +++ b/src/sections/Footer.jsx @@ -0,0 +1,80 @@ +import styled from 'styled-components'; +import { FacebookIcon } from '../ui/Facebook'; +import { InstagramIcon } from '../ui/Instagram'; +import { Icon } from '../ui/Icon'; +import { + FooterTitle, + FooterText, + FooterSectionTitle, + FooterContactInfo +} from '../ui/Typography'; + +const FooterContainer = styled.footer` + background-color: #FFCAF8; + padding: 2rem 1rem; + text-align: left; + color: black; + font-family: 'Inter', sans-serif; + font-size: 1rem; + display: flex; + flex-direction: column; + align-items: center; +`; + +const LogoContainer = styled.div` + display: flex; + align-items: center; + margin-bottom: 10px; + margin-left: -24px; + + & > img { + height: 120px; + width: auto; + max-width: 100%; + max-height: 120px; + } +`; + +const SocialIconsContainer = styled.div` + display: flex; + justify-content: flex-end; + gap: 2rem; + margin-top: 1.5rem; +`; +const FooterTextContainer = styled.div` +max-width: 700px; +display: flex; +flex-direction: column; +` + +export const FooterSection = () => { + return ( + + + + + + Kindness & Compassion + + Vi vill skapa en vänligare och mer inkluderande värld genom att skapa mötesplatser där människor kan mötas och prata om det som är viktigt på riktigt. + + + Samtidigt skapar vi en rörelse av goda handlingar människor emellan för att sprida vänlighet, värme och hopp! + + Om oss + Vår Vision + Rörelsen Pay it forward + Engagera dig + Bli volontär + Hitta din stödgrupp + Kontakta oss + info@kindnessandcompassion.se + + + + + + + + ); +}; diff --git a/src/sections/HeaderHeroSection.jsx b/src/sections/HeaderHeroSection.jsx new file mode 100644 index 00000000..ccddd12b --- /dev/null +++ b/src/sections/HeaderHeroSection.jsx @@ -0,0 +1,51 @@ +import styled from 'styled-components'; +import { HeaderBig, BodyTextWithBackground } from '../ui/Typography'; +import Button from '../ui/Button'; +import heroImage from '../../src/assets/Hero.png'; + +const StyledHeroSection = styled.section` + width: 100%; + background-image: url(${heroImage}); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + height: 100vh; + position: relative; + display: flex; + align-items: center; + justify-content: center; + color: white; + + &:before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.4); + } +`; + +const HeroContent = styled.div` + position: relative; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + padding: 2rem; + max-width: 500px; +`; + +export const HeaderHeroSection = () => { + return ( + + + Sprid en god gärning för en vänligare värld + Vi skapar mötesplatser för äkta samtal och goda handlingar + + + + ); +}; diff --git a/src/sections/HeaderSection.jsx b/src/sections/HeaderSection.jsx new file mode 100644 index 00000000..72e6c826 --- /dev/null +++ b/src/sections/HeaderSection.jsx @@ -0,0 +1,40 @@ +import styled from 'styled-components'; +import { Logo } from '../ui/Logo'; +import { HeaderText } from '../ui/Typography'; +import { Icon } from '../ui/Icon'; + +const StyledHeaderSection = styled.section` + width: 100%; + padding: 1rem 2rem; + display: flex; + align-items: center; + flex-direction: row; + justify-content: space-between; + + @media (max-width: 400px) { + padding: 0rem 0rem; + display: flex; + flex-direction: row; + gap: 0.2rem; + } +`; + +const RightSection = styled.div` + display: flex; + align-items: center; + gap: 1rem; + margin-right: 5px; +`; + +export const HeaderSection = () => { + return ( + + + Kindness & Compassion + + + + + + ); +}; diff --git a/src/sections/Supportgroup.jsx b/src/sections/Supportgroup.jsx new file mode 100644 index 00000000..749afaae --- /dev/null +++ b/src/sections/Supportgroup.jsx @@ -0,0 +1,60 @@ +import styled from 'styled-components'; +import { SupportgroupCard } from '../components/SupportgroupCard'; +import { BodyText, Headline2 } from '../ui/Typography'; +import { Spacer } from '../ui/Spacer'; +import WelcomeButton from '../ui/WelcomeButton'; + +const SupportgroupSectionText = styled.div` +grid-column: span 4; +display: flex; +flex-direction: column; +justify-content: center; +width: 100%; +padding: 2rem 1rem 1.5rem 1rem; +max-width: 700px; + +@media (min-width: 768px){ + grid-column: span 8; + } + +@media (min-width: 1200px){ + grid-coulmn: span 12; + } +` +const SupportgroupSectionStyle = styled.div` +grid-column: span 4; +display: flex; +flex-direction: column; +justify-content: center; +align-items: center; +background: #FFCAF8; +padding: 0rem 0rem 2.5rem 0rem; + +@media (min-width: 768px){ + grid-column: span 8; + } +@media (min-width: 1200px){ + grid-column: span 12; + } +` +export const SupportgroupSection = () => { + return ( + + + + Delta i våra samtalsgrupper + + + + Genom att prata med andra - både online och genom fysiska träffar - blir det enklare att hantera det som är svårt. Våra gruppledare har egen erfarenhet och alla grupper är slutna. + + + + Delta i våra grupper eller starta en ny utifrån dina behov. + + + + Fler aktiva stödgrupper + + ) +}; \ No newline at end of file diff --git a/src/sections/SwipeSection.jsx b/src/sections/SwipeSection.jsx new file mode 100644 index 00000000..d8c28d9d --- /dev/null +++ b/src/sections/SwipeSection.jsx @@ -0,0 +1,7 @@ +import { SwipeStatus } from '../ui/SwipeStatus' + +export const SwipeSection = () => { + return ( + + ) +}; \ No newline at end of file diff --git a/src/sections/Welcome.jsx b/src/sections/Welcome.jsx new file mode 100644 index 00000000..2b362da7 --- /dev/null +++ b/src/sections/Welcome.jsx @@ -0,0 +1,53 @@ +import styled from 'styled-components'; +import { Grid } from '../ui/Grid'; +import { WelcomeCard } from '../components/WelcomeCard'; +import { Headline1, BodyText } from '../ui/Typography'; +import WelcomeButton from '../ui/WelcomeButton'; + +const StyledWelcomeContainer = styled.section` + width: 100%; + padding: 2rem 1rem 2.5rem 1rem; + display: flex; + justify-content: center; + align-items: center; + grid-column: span 4; + + @media (min-width: 768px) { + grid-column: span 8; + } + + @media (min-width: 1200px){ + grid-column: span 12; + } +`; + +const WelcomeContent = styled.div` + max-width: 700px; + display: flex; + flex-direction: column; + gap: 1.5rem; +`; + +export const Welcome = () => { + return ( + + + + + Välkommen till Kindness & Compassion + + Kindness & Compassion är en rörelse med visionen att skapa ett vänligare samhälle. Vi skapar mötesplatser och inspirerar till goda handlingar. + + + Vårt mål är att bygga broar mellan människor och sprida vänlighet. Genom samtalsgrupper och enkla vardagliga handlingar kan vi göra skillnad. + + + Ingen kan göra allt, men alla kan göra något. Tillsammans skapar vi en mer inkluderande värld. + + Var med och gör skillnad + + + + + ); +}; diff --git a/src/styles/GlobalStyles.jsx b/src/styles/GlobalStyles.jsx new file mode 100644 index 00000000..58a1879b --- /dev/null +++ b/src/styles/GlobalStyles.jsx @@ -0,0 +1,24 @@ +import { createGlobalStyle } from 'styled-components'; + +export const GlobalStyles = createGlobalStyle` +* { + margin: 0; + padding: 0; + box-sizing: border-box; + } + +html, body { + font-family: 'Inter', sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +h1, h2, h3 { + font-family: 'Roboto Serif', serif; +} + +/* Colors */ +--color-primary: #EA96DF; +--color-secondary1: #82E1DF; +--color-secondary2: #F0EC92; +` \ No newline at end of file diff --git a/src/supportgroups.json b/src/supportgroups.json new file mode 100644 index 00000000..85097d3c --- /dev/null +++ b/src/supportgroups.json @@ -0,0 +1,17 @@ +[ + { + "supportgroupImage": "/supportgroup-stress.png", + "alt": "", + "title": "Utmattning och stress" + }, + { + "supportgroupImage": "/supportgroup-psych.png", + "alt": "", + "title": "Psykisk ohälsa - BTS" + }, + { + "supportgroupImage": "/supportgroup-cancer.png", + "alt": "", + "title": "Bröstcancer" + } +] \ No newline at end of file diff --git a/src/ui/AchievementElement.jsx b/src/ui/AchievementElement.jsx new file mode 100644 index 00000000..7c5220db --- /dev/null +++ b/src/ui/AchievementElement.jsx @@ -0,0 +1,34 @@ +import styled from 'styled-components'; +import PropTypes from 'prop-types'; +import { CircleText, CircleLabel } from './Typography'; + +const Circle = styled.div` + display: flex; + align-items: center; + justify-content: center; + background-color: ${({ color }) => color || '#D3D3D3'}; + color: black; + font-weight: 700; + font-size: 1.5rem; + height: 80px; + width: 80px; + border-radius: 50%; + margin: 0 auto; +`; + +export const AchievementElement = ({ number, label, color }) => { + return ( +
+ + {number} + + {label} +
+ ); +}; + +AchievementElement.propTypes = { + number: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + color: PropTypes.string, +}; diff --git a/src/ui/Button.jsx b/src/ui/Button.jsx new file mode 100644 index 00000000..9e0eb3fb --- /dev/null +++ b/src/ui/Button.jsx @@ -0,0 +1,29 @@ +import styled from 'styled-components'; + +const Button = styled.button` + background-color: #FFEC92; + color: black; + font-size: 1.5rem; + padding: 15px 30px; + border: none; + border-radius: 8px; + margin-top: 30px; + cursor: pointer; + font-weight: bold; + + &:hover { + background-color: #e6c843; + } + + @media (max-width: 768px) { + font-size: 1.2rem; + padding: 12px 24px; + } + + @media (max-width: 480px) { + font-size: 1rem; + padding: 10px 20px; + } +`; + +export default Button; diff --git a/src/ui/CarouselContainer.jsx b/src/ui/CarouselContainer.jsx new file mode 100644 index 00000000..c254b9bd --- /dev/null +++ b/src/ui/CarouselContainer.jsx @@ -0,0 +1,7 @@ +import styled from 'styled-components'; + +export const CarouselContainer = styled.div` +display: flex; +flex-direction: row; +gap: 1rem; +` \ No newline at end of file diff --git a/src/ui/DateCard.jsx b/src/ui/DateCard.jsx new file mode 100644 index 00000000..ba8fbad5 --- /dev/null +++ b/src/ui/DateCard.jsx @@ -0,0 +1,25 @@ +import styled from 'styled-components'; + +const DateCardContainer = styled.div` + position: absolute; + top: 0; + right: 0; + background-color: #fff; + color: #000; + width: 80px; + height: 80px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: 1.5rem; + border: solid; + padding: 0%; +`; + +export const DateCard = ({ date, month }) => ( + + {date} + {month} + +); diff --git a/src/ui/Facebook.jsx b/src/ui/Facebook.jsx new file mode 100644 index 00000000..65b4ac94 --- /dev/null +++ b/src/ui/Facebook.jsx @@ -0,0 +1,19 @@ +import styled from 'styled-components'; +import facebookIconSrc from '../../src/assets/Facebook.png'; + +export const IconImage = styled.img` + height: 24px; +`; + +export const IconContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; +`; + +export const FacebookIcon = () => ( + + + +); + diff --git a/src/ui/Grid.jsx b/src/ui/Grid.jsx new file mode 100644 index 00000000..007588fc --- /dev/null +++ b/src/ui/Grid.jsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +export const Grid = styled.div` + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 1rem; + +@media (min-width: 768px) { + grid-template-columns: repeat(8, 1fr); +} +@media (min-width: 1200px) { + grid-template-columns: repeat(12, 1fr); +} +` diff --git a/src/ui/Icon.jsx b/src/ui/Icon.jsx new file mode 100644 index 00000000..4a9468e2 --- /dev/null +++ b/src/ui/Icon.jsx @@ -0,0 +1,21 @@ +import styled from 'styled-components'; +import PropTypes from 'prop-types'; + +export const IconImage = styled.img` + height: ${({ size }) => size || "21.75px"}; + width: auto; +`; + +const IconContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; +`; + +export const Icon = ({ src, alt, size }) => { + return ( + + + + ); +}; \ No newline at end of file diff --git a/src/ui/Instagram.jsx b/src/ui/Instagram.jsx new file mode 100644 index 00000000..f3aba51f --- /dev/null +++ b/src/ui/Instagram.jsx @@ -0,0 +1,18 @@ +import styled from 'styled-components'; +import instagramIconSrc from '../../src/assets/Instagram.png'; + +export const IconImage = styled.img` + height: 24px; +`; + +export const IconContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; +`; + +export const InstagramIcon = () => ( + + + +); \ No newline at end of file diff --git a/src/ui/Logo.jsx b/src/ui/Logo.jsx new file mode 100644 index 00000000..3c286c8b --- /dev/null +++ b/src/ui/Logo.jsx @@ -0,0 +1,26 @@ +import styled from 'styled-components'; +import PropTypes from 'prop-types'; + +export const LogoImage = styled.img` + height: 2.8125rem; + align-self: stretch; +`; + +const LogoContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; +`; + +export const Logo = ({ src, alt }) => { + return ( + + + + ) +} + +Logo.propTypes = { + src: PropTypes.string.isRequired, + alt: PropTypes.string.isRequired, +} \ No newline at end of file diff --git a/src/ui/Spacer.jsx b/src/ui/Spacer.jsx new file mode 100644 index 00000000..1c0f306d --- /dev/null +++ b/src/ui/Spacer.jsx @@ -0,0 +1,7 @@ +import styled from 'styled-components'; + +export const Spacer = styled.div` + border-top: 1px dashed #000; + width: 60%; + margin: 1.5rem auto; +`; \ No newline at end of file diff --git a/src/ui/SupportgroupImage.jsx b/src/ui/SupportgroupImage.jsx new file mode 100644 index 00000000..eaf75e93 --- /dev/null +++ b/src/ui/SupportgroupImage.jsx @@ -0,0 +1,31 @@ +import PropTypes from 'prop-types'; +import styled from 'styled-components'; + +const SupportgroupImageStyle = styled.img` +align-self: stretch; +width: 100%; +height: 100%; +object-fit: cover; +` + +const SupportgroupImageContainer = styled.div` +display: flex; +align-items: center; +justify-content: center; +height: 13.75rem; +width: 100%; +overflow: hidden; +` + +export const SupportgroupImage = ({ supportgroupImage, alt }) => { + return ( + + + + ) +} + +SupportgroupImage.propTypes = { + alt: PropTypes.string.isRequired, + supportgroupImage: PropTypes.string.isRequired, +} \ No newline at end of file diff --git a/src/ui/SupportgroupTextContainer.jsx b/src/ui/SupportgroupTextContainer.jsx new file mode 100644 index 00000000..aaf5ab3a --- /dev/null +++ b/src/ui/SupportgroupTextContainer.jsx @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +export const SupportgroupTextContainer = styled.div` +display: flex; +align-items: center; +justify-content: center; +align-self: stretch; +height: 3.125rem; +width: 20rem; +padding: 0.625rem; +background-color: #FFF18F; +` \ No newline at end of file diff --git a/src/ui/SwipeStatus.jsx b/src/ui/SwipeStatus.jsx new file mode 100644 index 00000000..e64c2787 --- /dev/null +++ b/src/ui/SwipeStatus.jsx @@ -0,0 +1,37 @@ +import styled from 'styled-components'; +import PropTypes from 'prop-types'; + +const SwipeStatusStyle = styled.div` + grid-column: span 4; + display: flex; + justify-content: center; + align-items: center; + gap: 1rem; + width: 100%; + padding-top: 2rem; + padding-bottom: 2rem; +` +const Circle = styled.div` + display: flex; + width: 1.25rem; + height: 1.25rem; + border-radius: 50%; + background-color: ${(props) => (props.$active ? '#D9D9D9' : '#FFF')}; + border: solid #F77AE7 1px; +` + +export const SwipeStatus = ({ currentIndex }) => { + return ( + + {[...Array(4)].map((_, index) => ( + + ))} + + ) +}; + +SwipeStatus.propTypes = { + currentIndex: PropTypes.number.isRequired, +}; + + diff --git a/src/ui/Typography.jsx b/src/ui/Typography.jsx new file mode 100644 index 00000000..4257d643 --- /dev/null +++ b/src/ui/Typography.jsx @@ -0,0 +1,202 @@ +import styled from 'styled-components'; + +export const Headline1 = styled.h1` + text-align: center; + font-family: Inter; + font-size: 2rem; + font-weight: 700; + line-height: 130%; + + @media (min-width: 1200px){ + font-size: 2.25rem; + } +`; + +export const Headline2 = styled.h2` + text-align: center; + font-family: Inter; + font-size: 1.5rem; + font-weight: 700; + line-height: 130%; + + @media (min-width: 1200px){ + font-size: 1.5rem; + } +`; + +export const Headline3 = styled.h3` + text-align: center; + font-family: Inter; + font-size: 1.25rem; + font-weight: 700; + line-height: 130%; + + @media (min-width: 1200px){ + font-size: 1.5rem; + } +`; + +export const BodyText = styled.p` + font-family: Roboto Serif; + font-size: 1rem; + font-weight: 400; + line-height: 140%; +`; + +export const BodyTextWithBackground = styled(BodyText)` + background-color: rgba(0, 0, 0, 0.2); + font-style: italic; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 8px; + color: white; +`; + +export const TextHeader = styled.p` + font-family: Inter; + font-size: 1rem; + font-weight: 700; + line-height: 130%; +`; + +export const HeaderBig = styled.h1` + grid-column: span 4; + text-align: center; + color: #FFF; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.18); + font-family: Inter; + font-size: 2.5rem; + font-weight: 700; + line-height: 130%; +`; + +export const HeaderText = styled.p` + font-family: Inter; + font-size: 1rem; + font-weight: 700; + line-height: 130%; + white-space: nowrap; +` +export const CarouselHeader = styled.h2` +font-family: "Roboto Serif"; +font-size: 6rem; +font-style: italic; +font-weight: 600; +line-height: 140%; +color: black; +` +export const CarouselText = styled.p` +color: #000; +font-family: Inter; +font-size: 1rem; +font-style: italic; +font-weight: 500; +line-height: 140%; +margin-bottom: 0.62rem; +` +export const CarouselAuthor = styled.p` +color: #000; +font-family: Inter; +font-size: 1rem; +font-style: italic; +font-weight: 300; +line-height: 140%; +` +export const FooterTitle = styled.h2` + font-family: 'Inter', sans-serif; + font-weight: 700; + font-size: 1.5rem; + text-align: center; + margin: 0; +`; + +export const FooterText = styled.p` + margin: 1rem 0; + line-height: 1.5; + text-align: left; + font-family: 'Inter', sans-serif; +`; + +export const FooterSectionTitle = styled.h3` + font-family: 'Inter', sans-serif; + font-weight: 700; + margin: 1rem 0 0.5rem 0; + font-size: 1rem; + text-align: left; +`; + +export const FooterContactInfo = styled.p` + font-family: 'Inter', sans-serif; + font-size: 1rem; + margin-top: 1rem; + text-align: left; +`; + +export const CircleText = styled.span` + font-family: 'Inter', sans-serif; + font-size: 1.5rem; + font-weight: 700; +`; + +export const CircleLabel = styled.p` + margin-top: 0.5rem; + text-align: center; + font-family: 'Inter', sans-serif; + font-size: 0.9rem; + font-weight: 400; + + @media (max-width: 400px){ + max-width: 80px; + word-wrap: break-word + } +`; + +export const AchievementsTitle = styled.h2` + font-family: 'Inter', sans-serif; + font-weight: 700; + font-size: 1.5rem; + line-height: 1.3; + text-align: center; + margin-bottom: 1rem; + + @media (max-width: 768px) { + font-size: 1.25rem; + } +`; + +export const CalendarTitle = styled.h2` + font-family: Inter, sans-serif; + font-size: 1.5rem; + font-weight: 700; + text-align: center; + margin-bottom: 1rem; + color: #000; + + @media (max-width: 768px) { + font-size: 1.25rem; + } +`; + +export const CalendarCardTitle = styled.h3` + font-family: 'Inter', sans-serif; + font-size: 1.2rem; + font-weight: bold; + margin-bottom: 0.5rem; +`; + +export const CalendarCardText = styled.p` + font-family: 'Inter', sans-serif; + font-size: 1rem; + margin: 0.5rem 0; +`; + +export const CalendarButton = styled.button` + background: none; + border: none; + color: #000; + font-weight: bold; + text-decoration: underline; + font-size: 1rem; + cursor: pointer; + margin-top: 1.5rem; +`; \ No newline at end of file diff --git a/src/ui/WelcomeButton.jsx b/src/ui/WelcomeButton.jsx new file mode 100644 index 00000000..a3f3b74f --- /dev/null +++ b/src/ui/WelcomeButton.jsx @@ -0,0 +1,28 @@ +import styled from 'styled-components'; +import PropTypes from 'prop-types'; + +const StyledWelcomeButton = styled.button` +grid-column: span 4; + background: none; + border: none; + color: black; + font-size: 1rem; + font-weight: 700; + text-decoration: underline; + cursor: pointer; + padding: 0; + margin-top: 2rem; + + &:hover { + color: #333; + text-decoration: underline; + } +`; + +export default function WelcomeButton({ children, onClick }) { + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/src/ui/Wrapper.jsx b/src/ui/Wrapper.jsx new file mode 100644 index 00000000..de0e400c --- /dev/null +++ b/src/ui/Wrapper.jsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` +overflow: hidden; +padding-left: 1rem; +padding-top: 2rem; +padding-right: 1rem; + +@media (min-width: 768px){ + display: flex; + justify-content: center; + margin-bottom: 2rem; +} +` \ No newline at end of file