feat: overhaul

This commit is contained in:
2026-02-22 19:58:13 +00:00
parent 3a625a1874
commit bdf5c0d4d7
18 changed files with 666 additions and 157 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -1,7 +1,7 @@
import Intro from "@/src/portfolio/sections/Intro"; import Intro from "@/src/portfolio/sections/Intro";
import Projects from "@/src/portfolio/sections/Projects"; import Projects from "@/src/portfolio/sections/Projects";
import Experience from "@/src/portfolio/sections/Experience"; import Experience from "@/src/portfolio/sections/Experience";
import Achievements from "@/src/portfolio/sections/Achievements"; import CurrentWork from "@/src/portfolio/sections/CurrentWork";
import Footer from "@/src/portfolio/sections/Footer"; import Footer from "@/src/portfolio/sections/Footer";
import SkillsAndLinks from "@/src/portfolio/sections/SkillsAndLinks"; import SkillsAndLinks from "@/src/portfolio/sections/SkillsAndLinks";
@@ -9,7 +9,7 @@ const MainPage = (): JSX.Element => {
return ( return (
<div> <div>
<Intro/> <Intro/>
<Achievements/> <CurrentWork/>
<Experience/> <Experience/>
<Projects/> <Projects/>
<SkillsAndLinks/> <SkillsAndLinks/>

View File

@@ -0,0 +1,26 @@
export interface ICurrentWorkData {
role: string;
company: string;
companyUrl: string;
specialization: string;
location: string;
imagePath: string;
paragraphs: string[];
}
const currentWorkData: ICurrentWorkData = {
role: "Technical Consultant",
company: "Softwire",
companyUrl: "https://www.softwire.com/",
specialization: "AI Engineering",
location: "Manchester, United Kingdom",
imagePath: "/portfolio/work_profile.png",
paragraphs: [
"I spend much of my time <strong>designing and evaluating AI systems</strong>, or systems where AI plays a central role. My work involves the full lifecycle of AI implementation, from initial concept through to production deployment.",
"I have <strong>delivered training and adoption materials</strong> that have significantly improved AI usage within Softwire, helping teams understand both the capabilities and limitations of AI technologies. I've also created <strong>autonomous AI pipelines</strong> that have reduced developer burden by automating tasks that were previously time-consuming manual processes.",
"Working with <strong>diverse clients</strong> (from the highly skeptical to the enthusiastically optimistic), I've developed expertise in <strong>communicating AI benefits</strong> while respecting organizational constraints. This involves not just technical implementation, but also the human and ethical dimensions of AI adoption.",
"A particular area of passion for me is <strong>Ethical AI and Sustainable AI</strong>. I've spent considerable time advocating for sustainability considerations in AI systems, conducting <strong>environmental assessments for embedded AI</strong>, and building adoption systems that leverage <strong>local open-source AI</strong> instead of relying on large providers whose ethical standards can be questionable."
]
};
export default currentWorkData;

View File

@@ -13,7 +13,7 @@ const projectData : IProjectArguments[] = [
], ],
github: "https://github.com/KuchtaVR6/Multi-LLM-Agent", github: "https://github.com/KuchtaVR6/Multi-LLM-Agent",
document: "/multiAgent.pdf", document: "/multiAgent.pdf",
title: "Research Review of Neural Techniques for low-resource language translation", title: "Multi-LLM Tool Use Modular Pipeline with Expert Adapters",
text: "In this work, I explore a practical and cost-effective approach to improving how AI models interact with external tools and APIs. Instead of relying on large, expensive models or complex zero-shot learning methods, I utilize a modular pipeline using smaller, specialized components (Planner, Caller, Summariser) trained separately. I introduce to it a hard routing agent system that assigns tasks to expert adapters based on API categories, the system achieves performance that surpasses much larger closed-source models on a key benchmark. This approach enables more efficient, decentralized training and has potential applications beyond the tool-use QA task." text: "In this work, I explore a practical and cost-effective approach to improving how AI models interact with external tools and APIs. Instead of relying on large, expensive models or complex zero-shot learning methods, I utilize a modular pipeline using smaller, specialized components (Planner, Caller, Summariser) trained separately. I introduce to it a hard routing agent system that assigns tasks to expert adapters based on API categories, the system achieves performance that surpasses much larger closed-source models on a key benchmark. This approach enables more efficient, decentralized training and has potential applications beyond the tool-use QA task."
}, },
{ {

View File

@@ -1,13 +1,22 @@
import { WorkExperienceArgs } from "@/src/portfolio/helpers/WorkExperience"; import { WorkExperienceArgs } from "@/src/portfolio/helpers/WorkExperience";
const workExperienceData : WorkExperienceArgs[] = [ const workExperienceData : WorkExperienceArgs[] = [
{
industry: "Software & AI",
title: "Technical Consultant",
company: "Softwire",
city: "Manchester",
country: "United Kingdom",
startDate: "November 2025"
},
{ {
industry: "Software & AI", industry: "Software & AI",
title: "Software Developer", title: "Software Developer",
company: "Softwire", company: "Softwire",
city: "Manchester", city: "Manchester",
country: "United Kingdom", country: "United Kingdom",
startDate: "November 2024" startDate: "November 2024",
endDate: "November 2025"
}, },
{ {
industry: "Artificial Intelligence", industry: "Artificial Intelligence",
@@ -109,8 +118,8 @@ export const workExperienceParagraph =
"Mentor at Queen Mary University, where I later became a Laboratory Demonstrator. In the software industry, " + "Mentor at Queen Mary University, where I later became a Laboratory Demonstrator. In the software industry, " +
"I gained experience as a Software Developer Intern at Softwire. During my master's degree, I contributed to " + "I gained experience as a Software Developer Intern at Softwire. During my master's degree, I contributed to " +
"the Artificial Intelligence industry as a Programming Data Annotator at DataAnnotation Tech. Upon graduation, " + "the Artificial Intelligence industry as a Programming Data Annotator at DataAnnotation Tech. Upon graduation, " +
"I accepted a full-time offer to join Softwire as a Software Developer in their North West office in Manchester, " + "I accepted a full-time offer to join Softwire as a Software Developer in their North West office in Manchester. " +
"known for its focus on innovation in the AI and Data sector. At Softwire, I have gained valuable experience " + "In November 2025, I transitioned to Technical Consultant, AI Specialist. At Softwire, I have gained valuable experience " +
"working on several AI projects, applying my theoretical machine learning knowledge to real-world problems. " + "working on several AI projects, applying my theoretical machine learning knowledge to real-world problems. " +
"Across these diverse roles, I have developed a strong work ethic and a broad set of transferable skills."; "Across these diverse roles, I have developed a strong work ethic and a broad set of transferable skills.";

View File

@@ -1,5 +1,6 @@
import React, { FC, useState } from "react"; import React, { FC, useState } from "react";
import { CSSTransition } from "react-transition-group"; import { CSSTransition } from "react-transition-group";
import { FaChevronDown, FaChevronUp } from "react-icons/fa";
type AccordionArgs = { type AccordionArgs = {
title : string, title : string,
@@ -16,8 +17,10 @@ const Accordion : FC<AccordionArgs> = ({ title, children }): JSX.Element => {
return ( return (
<div className="accordion"> <div className="accordion">
<div className="accordion-header" onClick={toggleAccordion}> <div className="accordion-header" onClick={toggleAccordion}>
<h2>{title} {isOpen ? "-" : "+"}</h2> <h2>{title}</h2>
<span></span> <span className="accordion-icon">
{isOpen ? <FaChevronUp /> : <FaChevronDown />}
</span>
</div> </div>
<CSSTransition in={isOpen} timeout={0} classNames="accordion-content" unmountOnExit> <CSSTransition in={isOpen} timeout={0} classNames="accordion-content" unmountOnExit>
<div className="accordion-content">{children}</div> <div className="accordion-content">{children}</div>

View File

@@ -1,5 +1,6 @@
import { FC } from "react"; import { FC } from "react";
import styles from "../styling/achievements.module.scss"; import styles from "../styling/currentWork.module.scss";
import Location from "./Location";
export type EducationArgs = { export type EducationArgs = {
institution : string, institution : string,
@@ -14,48 +15,42 @@ export type EducationArgs = {
const Education : FC<EducationArgs> = (props) => { const Education : FC<EducationArgs> = (props) => {
const getCountryEmoji = (): string => { const getLocationString = (): string => {
switch (props.city) { switch (props.city) {
case "Warsaw": case "Warsaw":
return ", Poland"; return "Warsaw, Poland";
case "London": case "London":
return ", UK󠁧󠁢󠁥󠁮󠁧󠁿"; return "London, UK";
case "Edinburgh": case "Edinburgh":
return ", UK󠁧󠁢󠁳󠁣󠁴󠁿"; return "Edinburgh, UK";
} }
}; };
return ( return (
<div style={{ fontSize: "1.1em" }} className={styles.education} data-aos={"fade-right"}> <div className={styles.educationEntry} data-aos={"fade-up"}>
<div> <div className={styles.degree}>
<span className={styles.title}>{props.title}</span> in <b>{props.subtitle}</b> {props.title} in {props.subtitle}
<br/> </div>
at <i>{props.institution}</i> <div className={styles.institution}>
{props.notes? at {props.institution}
<ul className={styles.notes}> </div>
{props.notes.map((note, index) => {props.notes && (
<li key={index}> <ul className={styles.notes}>
{note} {props.notes.map((note, index) => (
</li> <li key={index}>{note}</li>
)} ))}
</ul> </ul>
: "" )}
} <div className={styles.details}>
<Location city={getLocationString()} className={styles.location} />
<div className={styles.dates}>
{props.endDate ? (
<span>{props.startDate} - {props.endDate}</span>
) : (
<span>Since {props.startDate}</span>
)}
</div>
</div> </div>
<span className={styles.otherDetails}>
<div className={styles.location}>{props.city}{getCountryEmoji()}</div>
<hr/>
{props.endDate ?
<>
<i>From:&nbsp;&nbsp;</i>
<span style={{ float:"right" }}>{props.startDate}</span>
<br/>
<i>To:&nbsp;&nbsp;</i>
<span style={{ float:"right" }}>{props.endDate}</span>
</>:
"Since " + props.startDate
}
</span>
</div> </div>
); );
}; };

View File

@@ -0,0 +1,18 @@
import { FC } from "react";
import { FaMapMarkerAlt } from "react-icons/fa";
interface ILocationProps {
city: string;
className?: string;
}
const Location: FC<ILocationProps> = ({ city, className }) => {
return (
<span className={className} style={{ display: "flex", alignItems: "center", gap: "0.3rem" }}>
<FaMapMarkerAlt style={{ fontSize: "0.7em" }} />
{city}
</span>
);
};
export default Location;

View File

@@ -0,0 +1,69 @@
import Image from "next/image";
import styles from "../styling/currentWork.module.scss";
import currentWorkData from "@/src/portfolio/data/currentWorkData";
import educationData from "@/src/portfolio/data/educationData";
import Education from "@/src/portfolio/helpers/Education";
import Location from "@/src/portfolio/helpers/Location";
const RoleInfo = (): JSX.Element => {
return (
<div className={styles.roleInfo}>
<h3>{currentWorkData.role}</h3>
<p className={styles.specialization}>
Specializing in <strong>{currentWorkData.specialization}</strong>
</p>
<p className={styles.company}>
at{" "}
<a
href={currentWorkData.companyUrl}
target="_blank"
rel="noopener noreferrer"
className={styles.companyLink}
>
{currentWorkData.company}
</a>
</p>
<div className={styles.locationWrapper}>
<Location city={currentWorkData.location} className={styles.location} />
</div>
</div>
);
};
const CurrentWork = (): JSX.Element => {
return (
<div className={styles.section} id={"current-work"}>
<div className={styles.mainContent}>
<div className={styles.currentWorkSection}>
<h2>What I&apos;m Up To</h2>
<div className={styles.content}>
<div className={styles.imageContainer} data-aos={"fade-right"}>
<div className={styles.imageWrapper}>
<Image
src={currentWorkData.imagePath}
alt="Patryk Kuchta at work"
fill={true}
className={styles.profileImage}
/>
</div>
<RoleInfo />
</div>
<div className={styles.description} data-aos={"fade-left"}>
{currentWorkData.paragraphs.map((paragraph, index) => (
<p key={index} dangerouslySetInnerHTML={{ __html: paragraph }} />
))}
</div>
</div>
</div>
<div className={styles.educationSection}>
<h2>Education</h2>
{educationData.map((entry, index) => (
<Education {...entry} key={index} />
))}
</div>
</div>
</div>
);
};
export default CurrentWork;

View File

@@ -1,9 +1,18 @@
import Image from "next/image";
import styles from "../styling/footer.module.scss"; import styles from "../styling/footer.module.scss";
const Footer = (): JSX.Element => { const Footer = (): JSX.Element => {
return ( return (
<footer className={styles.section}> <footer className={styles.section}>
Copyright &copy; Patryk Kuchta {new Date().getFullYear()} <div className={styles.logoWrapper}>
<Image
src="/portfolio/white_logo.png"
alt="Patryk Kuchta Logo"
fill={true}
className={styles.logo}
/>
</div>
<p>Copyright &copy; Patryk Kuchta {new Date().getFullYear()}</p>
</footer> </footer>
); );
}; };

View File

@@ -1,68 +1,67 @@
import styles from "../../styling/skillsLinks.module.scss"; import styles from "../../styling/skillsLinks.module.scss";
import { FaGithub, FaLinkedin, FaFilePdf, FaEnvelope } from "react-icons/fa";
interface ILink { interface ILink {
title: string; title: string;
link: string; link: string;
icon: JSX.Element;
} }
const moreInfoLinks: ILink[] = [ const moreInfoLinks: ILink[] = [
{ {
title: "Github", title: "Github",
link: "https://github.com/KuchtaVR6/" link: "https://github.com/KuchtaVR6/",
icon: <FaGithub />
}, },
{ {
title: "LinkedIn", title: "LinkedIn",
link: "https://linkedin.com/in/kuchtap" link: "https://linkedin.com/in/kuchtap",
icon: <FaLinkedin />
}, },
{ {
title: "Curriculum Vitae", title: "Curriculum Vitae",
link: "/PatrykKuchta_CV.pdf" link: "/PatrykKuchta_CV.pdf",
icon: <FaFilePdf />
} }
]; ];
const contactLinks: ILink[] = [ const contactLinks: ILink[] = [
{ {
title: "Email", title: "Email",
link: "mailto:patryk@kuchta.uk" link: "mailto:patryk@kuchta.uk",
icon: <FaEnvelope />
}, },
{ {
title: "LinkedIn", title: "LinkedIn",
link: "https://linkedin.com/in/kuchtap" link: "https://linkedin.com/in/kuchtap",
icon: <FaLinkedin />
} }
]; ];
const LinksSection = (): JSX.Element => { const LinksSection = (): JSX.Element => {
return ( return (
<div data-aos = {"fade-right"} className={styles.otherLinks}> <div data-aos={"fade-right"} className={styles.otherLinks}>
<h2>Find out more about me:</h2> <h2>Find out more about me:</h2>
<ul> <ul>
{ {moreInfoLinks.map(({ title, link, icon }, key) => (
moreInfoLinks.map(({ title, link }, key) => { <li key={key}>
return ( <a href={link} className={styles.linkWithIcon}>
<li key = {key}> <span className={styles.icon}>{icon}</span>
<a href={link}> {title}
{title} </a>
</a> </li>
</li> ))}
);
}
)
}
</ul> </ul>
<h2>Contact me through:</h2> <h2>Contact me through:</h2>
<ul> <ul>
{ {contactLinks.map(({ title, link, icon }, key) => (
contactLinks.map(({ title, link }, key) => { <li key={key}>
return ( <a href={link} className={styles.linkWithIcon}>
<li key = {key}> <span className={styles.icon}>{icon}</span>
<a href={link}> {title}
{title} </a>
</a> </li>
</li> ))}
);
}
)
}
</ul> </ul>
</div> </div>
); );

View File

@@ -1,41 +1,58 @@
import styles from "../../styling/skillsLinks.module.scss"; import styles from "../../styling/skillsLinks.module.scss";
import SkillDisplay from "@/src/portfolio/helpers/SkillDisplay";
import Accordion from "@/src/portfolio/helpers/Accordion"; import Accordion from "@/src/portfolio/helpers/Accordion";
import { skillsInCategories } from "@/src/portfolio/data/skillsData"; import { skillsInCategories } from "@/src/portfolio/data/skillsData";
import { modulesTaken } from "@/src/portfolio/data/modulesTaken"; import { modulesTaken } from "@/src/portfolio/data/modulesTaken";
import certificateData from "@/src/portfolio/data/certificateData";
const renderSkillItem = (name: string, level: string): JSX.Element => (
<div className={styles.technology} key={name}>
<b>{name}</b>
<span style={{ float: "right", fontSize: "0.8em" }}>
<i>{level}</i>
</span>
</div>
);
const SkillsSection = (): JSX.Element => { const SkillsSection = (): JSX.Element => {
return ( return (
<div data-aos = {"fade-left"} className={styles.skills}> <div data-aos={"fade-left"} className={styles.skills}>
<div>
<i>Press the &quot;+&quot; to expand a section</i>&nbsp;👀
</div>
<div className={styles.innerskills}> <div className={styles.innerskills}>
{ <Accordion title={"Certificates and Awards"}>
Object.entries(skillsInCategories).map(([category, skills]) => { {certificateData.map(({ title, institution, awardDate }, index) => (
return <Accordion title={category} key={category}> <div className={styles.technology} key={index}>
{ <b>{title}</b>
skills.map((skill) => { <span style={{ float: "right", fontSize: "0.8em" }}>
return <SkillDisplay {...skill} key={skill.name} />; <i>{institution}</i> - {awardDate}
}) </span>
} </div>
</Accordion>; ))}
}) </Accordion>
}
<Accordion title={"Modules Taken"}> <Accordion title={"Modules Taken"}>
{ {modulesTaken.map(({ name, level, score }) => (
modulesTaken.map(({ name, level, score }) => { <div id={name.toLowerCase()} className={styles.technology} key={name}>
return <div id={name.toLowerCase()} <b>{name}</b>
className={styles.technology} <span style={{ float: "right", fontSize: "0.8em" }}>
key={name}> {score >= 0 ? score + "%" : "N/A"} <i>{level}</i>
<b>{name}&nbsp;&nbsp;</b> </span>
<span style={{ float: "right", fontSize: "0.8em" }}> </div>
{score>=0? score + "%" : "🔮"} <i>{level}</i> ))}
</span> </Accordion>
</div>;
}) <Accordion title={"Human languages"}>
} {skillsInCategories["Human languages"].map((skill) => renderSkillItem(skill.name, skill.level))}
</Accordion>
<Accordion title={"Programming Languages"}>
{skillsInCategories["Programming Languages"].map((skill) => renderSkillItem(skill.name, skill.level))}
</Accordion>
<Accordion title={"Frameworks/Libraries"}>
{skillsInCategories["Frameworks/Libraries"].map((skill) => renderSkillItem(skill.name, skill.level))}
</Accordion>
<Accordion title={"Miscellaneous"}>
{skillsInCategories["Miscellaneous"].map((skill) => renderSkillItem(skill.name, skill.level))}
</Accordion> </Accordion>
</div> </div>
</div> </div>

View File

@@ -21,8 +21,6 @@
.education { .education {
text-align: left; text-align: left;
background-color: transparentize($gray, 0.5); background-color: transparentize($gray, 0.5);
padding: 1em;
margin: 1em;
border-radius: $smallGap; border-radius: $smallGap;
box-shadow: 0.5em 0.5em transparentize($black, 0.5); box-shadow: 0.5em 0.5em transparentize($black, 0.5);

View File

@@ -0,0 +1,245 @@
@import "/src/styles/helpers";
.section {
@include lightSection;
@include regularFont;
padding: 3rem 1rem;
overflow-x: hidden;
@media (max-width: 768px) {
padding: 2rem 1rem;
}
h2 {
text-align: center;
font-size: 2.5rem;
margin-bottom: 3rem;
color: $accent_colour;
}
}
.mainContent {
display: flex;
flex-direction: column;
gap: 3rem;
max-width: 1400px;
margin: 0 auto;
@media (min-width: 1280px) {
flex-direction: row;
gap: 1.5rem;
align-items: flex-start;
}
}
.currentWorkSection {
flex: 1;
padding: 0 1rem;
@media (min-width: 1280px) {
flex: 0 0 54%;
padding-right: 1.5rem;
padding-left: 0;
border-right: 2px solid $accent_colour;
}
h2 {
text-align: center;
font-size: 2rem;
margin-bottom: 2rem;
color: $accent_colour;
}
}
.educationSection {
flex: 1;
padding: 0 1rem;
@media (min-width: 1280px) {
flex: 0 0 42%;
padding-left: 1.5rem;
padding-right: 0;
}
@media (max-width: 1279px) {
padding-top: 2rem;
padding-bottom: 2rem;
border-top: 2px solid $accent_colour;
}
h2 {
text-align: center;
font-size: 1.75rem;
margin-bottom: 1.5rem;
margin-top: 0;
color: $accent_colour;
}
.educationEntry {
background-color: transparentize($gray, 0.85);
padding: 1rem;
margin-bottom: 1rem;
border-radius: 8px;
border-left: 3px solid $accent_colour;
&:last-child {
margin-bottom: 0;
}
.degree {
font-weight: bold;
color: $accent_colour;
font-size: 1rem;
margin-bottom: 0.3rem;
line-height: 1.4;
}
.institution {
font-style: italic;
font-size: 0.9rem;
margin-bottom: 0.5rem;
color: #666;
}
.notes {
font-size: 0.85rem;
margin: 0.5rem 0;
padding-left: 1rem;
li {
margin-bottom: 0.25rem;
line-height: 1.5;
}
}
.details {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.8rem;
color: #999;
margin-top: 0.5rem;
flex-wrap: wrap;
gap: 0.5rem;
.location {
color: #999;
}
.dates {
display: flex;
gap: 0.5rem;
align-items: center;
}
}
}
}
.content {
display: flex;
flex-direction: column;
gap: 2rem;
@media (min-width: 900px) {
flex-direction: row;
gap: 2rem;
align-items: flex-start;
}
@media (max-width: 899px) {
align-items: center;
gap: 1.5rem;
}
}
.imageContainer {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.75rem;
flex-shrink: 0;
@media (min-width: 900px) {
flex: 0 0 25%;
gap: 1.5rem;
}
}
.imageWrapper {
position: relative;
width: 200px;
height: 200px;
flex-shrink: 0;
}
.profileImage {
border-radius: 50%;
object-fit: cover;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
border: 4px solid $accent_colour;
}
.roleInfo {
text-align: center;
h3 {
font-size: 1.5rem;
margin-bottom: 0.5rem;
color: $accent_colour;
}
.company {
font-size: 1.2rem;
margin-bottom: 0.3rem;
}
.specialization {
font-style: italic;
color: #666;
margin-bottom: 0.3rem;
}
.locationWrapper {
display: flex;
justify-content: center;
margin-top: 0.5rem;
}
.location {
font-size: 0.75rem;
color: #999;
}
}
.companyLink {
color: rgb(0, 214, 254);
text-decoration: none;
font-weight: 600;
&:hover {
text-decoration: underline;
}
}
.description {
display: flex;
flex-direction: column;
gap: 0.8rem;
@media (min-width: 900px) {
flex: 1;
}
p {
line-height: 1.7;
text-align: justify;
font-size: 0.9rem;
margin: 0;
@media (max-width: 899px) {
text-align: left;
font-size: 0.85rem;
}
}
}

View File

@@ -16,6 +16,7 @@
width: 42em; width: 42em;
max-width: 70vw; max-width: 70vw;
line-height: 1.7; line-height: 1.7;
font-size: 0.85rem;
} }
} }

View File

@@ -1,9 +1,35 @@
@import "@/src/styles/helpers.scss"; @import "@/src/styles/helpers.scss";
.section { .section {
@include darkSection; @include darkSection;
@include regularFont; @include regularFont;
padding: 0.4em; display: flex;
text-align: center; flex-direction: row;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 2rem;
p {
margin: 0;
font-size: 0.9rem;
}
}
.logoWrapper {
position: relative;
width: 2rem;
height: 1rem;
flex-shrink: 0;
}
.logo {
object-fit: contain;
opacity: 0.7;
transition: opacity 0.3s ease;
&:hover {
opacity: 1;
}
} }

View File

@@ -1,53 +1,103 @@
@import "@/src/styles/helpers.scss"; @import "@/src/styles/helpers.scss";
.section { .section {
@include lightSection; @include lightSection;
@include regularFont; @include regularFont;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-evenly; justify-content: space-evenly;
flex-wrap: wrap; flex-wrap: wrap;
gap: 3rem;
padding: 0 6em 3em; padding: 3rem 2rem;
.skills { @media (max-width: 768px) {
display: flex; padding: 2rem 1rem;
flex-direction: column; }
text-align: center;
.innerskills {
text-align: left;
display: flex;
flex-direction: row;
justify-content: space-evenly;
gap: $mediumGap;
flex-wrap: wrap;
flex-basis: 60%;
}
flex-wrap: wrap;
flex-basis: 60%;
}
.otherLinks{ .skills {
margin: auto 1em; display: flex;
padding: 1em 2em; flex-direction: column;
background-color: $black; text-align: center;
color: $white; flex-basis: 60%;
min-width: 300px;
h2{ @media (max-width: 768px) {
font-size: 115%; flex-basis: 100%;
} }
a { .innerskills {
color: $white; text-align: left;
text-decoration: none; display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1.5rem;
}
}
&:hover { .otherLinks {
color: $accent_colour; margin: auto 1em;
font-style: italic; padding: 1.5em 2.5em;
} background-color: $black;
} color: $white;
} border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
h2 {
font-size: 1.3rem;
margin-bottom: 1rem;
}
ul {
list-style: none;
padding: 0;
}
li {
margin-bottom: 0.75rem;
}
a {
color: $white;
text-decoration: none;
transition: all 0.2s ease;
display: flex;
align-items: center;
font-size: 1rem;
&:hover {
color: $accent_colour;
transform: translateX(5px);
.icon {
transform: scale(1.1);
}
}
}
.linkWithIcon {
display: flex;
align-items: center;
gap: 0.75rem;
}
.icon {
font-size: 1.2rem;
transition: transform 0.2s ease;
display: flex;
align-items: center;
}
}
}
.technology {
padding: 0.5rem 0;
border-bottom: 1px solid transparentize($gray, 0.5);
&:last-child {
border-bottom: none;
}
} }

View File

@@ -43,30 +43,74 @@ body {
display: flex; display: flex;
} }
.accordion {
border: 1px solid transparentize($gray, 0.3);
border-radius: 8px;
overflow: hidden;
background-color: $white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.3s ease;
&:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
}
.accordion-header {
padding: 1rem 1.5rem;
cursor: pointer;
background-color: transparentize($gray, 0.7);
transition: background-color 0.2s ease;
user-select: none;
display: flex;
justify-content: space-between;
align-items: center;
&:hover {
background-color: transparentize($gray, 0.5);
}
h2 {
margin: 0;
font-size: 1.2rem;
color: $black;
flex: 1;
}
.accordion-icon {
display: flex;
align-items: center;
font-size: 1rem;
color: $accent_colour;
transition: transform 0.2s ease;
}
}
.accordion-content {
padding: 1.5rem;
background-color: $white;
}
.accordion-content-enter { .accordion-content-enter {
opacity: 0; opacity: 0;
height: 0; max-height: 0;
overflow: hidden; overflow: hidden;
} }
.accordion-content-enter-active { .accordion-content-enter-active {
opacity: 1; opacity: 1;
height: auto; max-height: 2000px;
transition: opacity 300ms, height 300ms; transition: opacity 300ms ease-in-out, max-height 300ms ease-in-out;
} }
.accordion-content-exit { .accordion-content-exit {
opacity: 1; opacity: 1;
height: auto; max-height: 2000px;
} }
.accordion-content-exit-active { .accordion-content-exit-active {
opacity: 0; opacity: 0;
height: 0; max-height: 0;
transition: opacity 300ms, height 300ms; transition: opacity 300ms ease-in-out, max-height 300ms ease-in-out;
overflow: hidden; overflow: hidden;
} }
.accordion-header {
text-align: center;
}