Compare commits
4 Commits
75808307a4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 38a94906d4 | |||
| d62889f7b7 | |||
| e02eee4d30 | |||
| d2ede883d1 |
11
justfile
Normal file
11
justfile
Normal file
@@ -0,0 +1,11 @@
|
||||
dev:
|
||||
npm run dev
|
||||
|
||||
build:
|
||||
npm run build
|
||||
|
||||
lint:
|
||||
npm run lint
|
||||
|
||||
test:
|
||||
echo "No test runner configured yet"
|
||||
BIN
public/me.png
Normal file
BIN
public/me.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 356 KiB |
12
src/App.jsx
12
src/App.jsx
@@ -1,12 +1,12 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { activeSectionState, themeState } from './state/atoms';
|
||||
import Navbar from './components/Navbar/Navbar';
|
||||
import Hero from './components/Hero/Hero';
|
||||
import About from './components/About/About';
|
||||
import Projects from './components/Projects/Projects';
|
||||
import Contact from './components/Contact/Contact';
|
||||
import Footer from './components/Footer/Footer';
|
||||
import Navbar from './components/Navbar';
|
||||
import Hero from './components/Hero';
|
||||
import About from './components/About';
|
||||
import Projects from './components/Projects';
|
||||
import Contact from './components/Contact';
|
||||
import Footer from './components/Footer';
|
||||
import styles from './App.module.scss';
|
||||
|
||||
const SECTIONS = {
|
||||
|
||||
67
src/components/About.jsx
Normal file
67
src/components/About.jsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import styles from '../styles/components/About.module.scss';
|
||||
|
||||
const STACK = ['Azure', '.NET', 'C#', 'JavaScript', 'React', 'Node.js', 'SQL'];
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<section id="about" className={styles.about}>
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.header}>
|
||||
<span className={styles.sectionNum}>01.</span>
|
||||
<h2 className={styles.sectionTitle}>About me</h2>
|
||||
<div className={styles.line} />
|
||||
</div>
|
||||
|
||||
<div className={styles.body}>
|
||||
<div className={styles.text}>
|
||||
<p>
|
||||
I'm a curious software engineer with a passion for building scalable and efficient
|
||||
applications. With deep knowledge in cloud, backend, and frontend development, I enjoy
|
||||
working on projects that challenge me to learn new technologies and improve my skills.
|
||||
</p>
|
||||
<p>
|
||||
I tend to build always something that is useful to others, and I find joy in creating tools
|
||||
and applications that solve real-world problems. I believe that technology has the power to
|
||||
make a positive impact on people's lives, and I strive to contribute to that impact through my work.
|
||||
</p>
|
||||
<p>
|
||||
Here are some technologies I've been working with recently:
|
||||
</p>
|
||||
|
||||
<ul className={styles.stack}>
|
||||
{STACK.map((item) => (
|
||||
<li key={item}>
|
||||
<span className={styles.arrow}>▸</span> {item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={styles.card}>
|
||||
<div className={styles.avatar}>
|
||||
<img src="/me.png" alt="Profile" className={styles.avatarImg} />
|
||||
</div>
|
||||
<div className={styles.meta}>
|
||||
<p className={styles.metaLine}>
|
||||
<span className={styles.metaKey}>location</span>
|
||||
<span className={styles.metaValue}>Turku Archipelago, Finland</span>
|
||||
</p>
|
||||
<p className={styles.metaLine}>
|
||||
<span className={styles.metaKey}>favorite book</span>
|
||||
<span className={styles.metaValue}>The Lord of the Rings</span>
|
||||
</p>
|
||||
<div className={styles.metaLine}>
|
||||
<span className={styles.metaKey}>favorite songs</span>
|
||||
<div className={styles.metaLinks}>
|
||||
<a href="https://www.youtube.com/watch?v=EqDcigWxZuU" target="_blank" rel="noreferrer">Rücksturz – Agitation Free</a>
|
||||
<a href="https://www.youtube.com/watch?v=2i2oJJwgLTk" target="_blank" rel="noreferrer">Year of Love – Jenny Hval</a>
|
||||
<a href="https://www.youtube.com/watch?v=C5lVJkp5TT4" target="_blank" rel="noreferrer">Пачка сигарет – КИНО</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import styles from './About.module.scss';
|
||||
|
||||
const STACK = ['Technology 1', 'Technology 2', 'Technology 3', 'Technology 4', 'Technology 5', 'Technology 6'];
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<section id="about" className={styles.about}>
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.header}>
|
||||
<span className={styles.sectionNum}>01.</span>
|
||||
<h2 className={styles.sectionTitle}>About me</h2>
|
||||
<div className={styles.line} />
|
||||
</div>
|
||||
|
||||
<div className={styles.body}>
|
||||
<div className={styles.text}>
|
||||
<p>
|
||||
A paragraph about yourself and your background. What kind of work
|
||||
do you do and what drives you? Keep it personal and authentic.
|
||||
</p>
|
||||
<p>
|
||||
A second paragraph with more details — hobbies, interests, or
|
||||
anything else that gives visitors a sense of who you are beyond
|
||||
your job title.
|
||||
</p>
|
||||
<p>
|
||||
Here are some technologies I've been working with recently:
|
||||
</p>
|
||||
|
||||
<ul className={styles.stack}>
|
||||
{STACK.map((item) => (
|
||||
<li key={item}>
|
||||
<span className={styles.arrow}>▸</span> {item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={styles.card}>
|
||||
<div className={styles.avatar}>
|
||||
<span className={styles.avatarInner}>YN</span>
|
||||
</div>
|
||||
<div className={styles.meta}>
|
||||
<p className={styles.metaLine}>
|
||||
<span className={styles.metaKey}>location</span>
|
||||
<span>City, Country</span>
|
||||
</p>
|
||||
<p className={styles.metaLine}>
|
||||
<span className={styles.metaKey}>status</span>
|
||||
<span className={styles.green}>Available</span>
|
||||
</p>
|
||||
<p className={styles.metaLine}>
|
||||
<span className={styles.metaKey}>focus</span>
|
||||
<span>Your focus area</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import styles from './Contact.module.scss';
|
||||
import styles from '../styles/components/Contact.module.scss';
|
||||
|
||||
export default function Contact() {
|
||||
return (
|
||||
@@ -12,12 +12,13 @@ export default function Contact() {
|
||||
|
||||
<div className={styles.body}>
|
||||
<p className={styles.lede}>
|
||||
A short message inviting visitors to reach out. Let them know
|
||||
you're open to new opportunities, collaborations, or just a chat.
|
||||
I'm currently employed, but my inbox is always open. Whether you have a question or
|
||||
just want to say hi, I'll try my best to get back to you!
|
||||
</p>
|
||||
<p>I also have a small company if you are interested in collaboration or services.</p>
|
||||
|
||||
<a
|
||||
href="mailto:your@email.com"
|
||||
href="mailto:veikko@lintujarvi.fi"
|
||||
className={styles.emailBtn}
|
||||
>
|
||||
Say hello ↗
|
||||
@@ -25,9 +26,9 @@ export default function Contact() {
|
||||
|
||||
<ul className={styles.socials}>
|
||||
{[
|
||||
{ label: 'GitHub', href: '#' },
|
||||
{ label: 'LinkedIn', href: '#' },
|
||||
{ label: 'Platform', href: '#' },
|
||||
{ label: 'Gitea', href: 'https://gitea.tietokonepaja.fi/' },
|
||||
{ label: 'LinkedIn', href: 'https://www.linkedin.com/in/veikko-lintuj%C3%A4rvi-5888a6175/' },
|
||||
{ label: 'Company', href: 'https://www.tietokonepaja.fi/' },
|
||||
].map(({ label, href }) => (
|
||||
<li key={label}>
|
||||
<a href={href} target="_blank" rel="noreferrer">
|
||||
@@ -1,11 +1,11 @@
|
||||
import styles from './Footer.module.scss';
|
||||
import styles from '../styles/components/Footer.module.scss';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
<p>
|
||||
Designed & built by{' '}
|
||||
<a href="#home">Your Name</a>{' '}
|
||||
<a href="#home">Veikko Lintujärvi</a>{' '}
|
||||
<span className={styles.separator}>·</span>{' '}
|
||||
<span className={styles.stack}>React + Vite</span>
|
||||
</p>
|
||||
@@ -1,4 +1,4 @@
|
||||
import styles from './Hero.module.scss';
|
||||
import styles from '../styles/components/Hero.module.scss';
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
@@ -8,15 +8,15 @@ export default function Hero() {
|
||||
<div className={styles.content}>
|
||||
<p className={styles.greeting}>Hey, I'm</p>
|
||||
<h1 className={styles.name}>
|
||||
Your Name<span className={styles.dot}>.</span>
|
||||
Veikko Lintujärvi<span className={styles.dot}>.</span>
|
||||
</h1>
|
||||
<p className={styles.tagline}>
|
||||
Your tagline goes here —{' '}
|
||||
<span className={styles.highlight}>make it memorable.</span>
|
||||
Making world a better place —{' '}
|
||||
<span className={styles.highlight}>one line at a time.</span>
|
||||
</p>
|
||||
<p className={styles.sub}>
|
||||
A short bio or description about yourself. What do you do, what do
|
||||
you care about, and what makes you interesting?
|
||||
I'm a curious software engineer from Turku Archipelago, Finland. Right now
|
||||
I'm excited about cloud, Linux and AI.
|
||||
</p>
|
||||
|
||||
<div className={styles.cta}>
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useAtom } from 'jotai';
|
||||
import { navOpenState, activeSectionState } from '../../state/atoms';
|
||||
import ThemeToggle from '../ThemeToggle/ThemeToggle';
|
||||
import styles from './Navbar.module.scss';
|
||||
import { navOpenState, activeSectionState } from '../state/atoms';
|
||||
import ThemeToggle from './ThemeToggle';
|
||||
import styles from '../styles/components/Navbar.module.scss';
|
||||
|
||||
const NAV_LINKS = [
|
||||
{ label: 'Home', section: 'home' },
|
||||
@@ -41,6 +41,7 @@ export default function Navbar() {
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<div className={styles.navActions}>
|
||||
<ThemeToggle />
|
||||
|
||||
<button
|
||||
@@ -53,6 +54,7 @@ export default function Navbar() {
|
||||
<span />
|
||||
<span />
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
@@ -1,4 +1,4 @@
|
||||
import styles from './Projects.module.scss';
|
||||
import styles from '../styles/components/Projects.module.scss';
|
||||
|
||||
const PROJECTS = [
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useAtom } from 'jotai';
|
||||
import { themeState } from '../../state/atoms';
|
||||
import styles from './ThemeToggle.module.scss';
|
||||
import { themeState } from '../state/atoms';
|
||||
import styles from '../styles/components/ThemeToggle.module.scss';
|
||||
|
||||
export default function ThemeToggle() {
|
||||
const [theme, setTheme] = useAtom(themeState);
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '../../styles/variables' as *;
|
||||
@use '../variables' as *;
|
||||
|
||||
.about {
|
||||
padding: $space-24 $space-6;
|
||||
@@ -87,18 +87,15 @@
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, $color-primary 0%, $color-primary-mid 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 30px rgb(var(--color-primary-mid) / 0.3);
|
||||
}
|
||||
|
||||
.avatarInner {
|
||||
font-size: $text-xl;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
letter-spacing: 0.05em;
|
||||
.avatarImg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.meta {
|
||||
@@ -110,7 +107,7 @@
|
||||
|
||||
.metaLine {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
font-size: $text-sm;
|
||||
color: $color-text-muted;
|
||||
border-bottom: 1px solid $color-border;
|
||||
@@ -125,8 +122,27 @@
|
||||
font-family: $font-mono;
|
||||
color: $color-text-dim;
|
||||
font-size: $text-xs;
|
||||
flex-shrink: 0;
|
||||
width: 7.5rem;
|
||||
}
|
||||
|
||||
.green {
|
||||
color: #4ade80;
|
||||
.metaValue {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.metaLinks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: $space-2;
|
||||
|
||||
a {
|
||||
color: $color-accent;
|
||||
text-decoration: none;
|
||||
font-size: $text-xs;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '../../styles/variables' as *;
|
||||
@use '../variables' as *;
|
||||
|
||||
.contact {
|
||||
padding: $space-24 $space-6 $space-16;
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '../../styles/variables' as *;
|
||||
@use '../variables' as *;
|
||||
|
||||
.footer {
|
||||
border-top: 1px solid $color-border;
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '../../styles/variables' as *;
|
||||
@use '../variables' as *;
|
||||
|
||||
.hero {
|
||||
position: relative;
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '../../styles/variables' as *;
|
||||
@use '../variables' as *;
|
||||
|
||||
.header {
|
||||
position: fixed;
|
||||
@@ -88,6 +88,12 @@
|
||||
}
|
||||
|
||||
// ── Burger ─────────────────────────────────────────────────────
|
||||
.navActions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $space-1;
|
||||
}
|
||||
|
||||
.burger {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
@@ -129,6 +135,13 @@
|
||||
gap: $space-4;
|
||||
transform: translateY(-110%);
|
||||
transition: transform $transition-base;
|
||||
z-index: 99;
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
transition: transform $transition-base;
|
||||
|
||||
&.open {
|
||||
transform: translateY(0);
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '../../styles/variables' as *;
|
||||
@use '../variables' as *;
|
||||
|
||||
.projects {
|
||||
padding: $space-24 $space-6;
|
||||
@@ -1,4 +1,4 @@
|
||||
@use '../../styles/variables' as *;
|
||||
@use '../variables' as *;
|
||||
|
||||
.toggle {
|
||||
background: none;
|
||||
Reference in New Issue
Block a user