Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 115 additions & 22 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,38 +1,131 @@
@import 'tailwindcss';

:root {
--background: #171717;
--background: #0b0b0d;
--foreground: #eceae0;
}

@theme inline {
--color-bg: #0b0b0d;
--color-surface: #111115;
--color-surface2: #18181e;
--color-bdr: #26262e;
--color-accent: #f4a229;
--color-accent2: #ff6b35;
--color-ink: #eceae0;
--color-muted: #72706a;
--color-danger: #e84040;
--color-background: var(--background);
--font-sans: var(--font-geist-sans);
--font-mono: var(--font-geist-mono);
--color-coffee-50: #f8f8f8;
--color-coffee-100: #f1eff0;
--color-coffee-200: #e6e2e4;
--color-coffee-300: #d8d2d5;
--color-coffee-400: #baafb5;
--color-coffee-500: #a0939a;
--color-coffee-600: #887a81;
--color-coffee-700: #70656b;
--color-coffee-800: #5f555a;
--color-coffee-900: #524a4e;
--color-coffee-950: #2a2528;
--transparent: #1717178d;
}

@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
--font-sans: var(--font-syne-family);
--font-mono: var(--font-jetbrains-mono);
--font-syne: var(--font-syne-family);
--animate-fade-up-1: fade-up 0.6s ease forwards 0.2s;
--animate-fade-up-2: fade-up 0.7s ease forwards 0.4s;
--animate-fade-up-3: fade-up 0.7s ease forwards 0.6s;
--animate-fade-up-4: fade-up 0.7s ease forwards 0.8s;
--animate-fade-up-5: fade-up 0.7s ease forwards 1s;
--animate-fade-up-6: fade-up 0.7s ease forwards 1.2s;
}

@keyframes fade-up {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}

/* Grid noise background */
body::before {
content: '';
position: fixed;
inset: 0;
background-image:
linear-gradient(#26262e 1px, transparent 1px),
linear-gradient(90deg, #26262e 1px, transparent 1px);
background-size: 40px 40px;
opacity: 0.25;
pointer-events: none;
z-index: 0;
}

/* Scroll-triggered fade-in */
.fade-in {
opacity: 0;
transform: translateY(16px);
transition:
opacity 0.6s ease,
transform 0.6s ease;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}

/* Photo frame — offset accent border */
.photo-frame::after {
content: '';
position: absolute;
inset: 0;
border: 1px solid #f4a229;
border-radius: 4px;
transform: translate(8px, 8px);
pointer-events: none;
}

/* Project card — top accent line on hover */
.project-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: #26262e;
transition: background 0.2s;
}
.project-card:hover::before {
background: #f4a229;
}

/* Hero scroll — vertical trailing line */
.hero-scroll::after {
content: '';
display: block;
width: 1px;
height: 60px;
background: linear-gradient(to bottom, #72706a, transparent);
margin: 0.5rem auto 0;
}

.stats-note code {
color: #f4a229;
background: rgba(244, 162, 41, 0.08);
padding: 0.1rem 0.3rem;
border-radius: 2px;
}

::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: #0b0b0d;
}
::-webkit-scrollbar-thumb {
background: #26262e;
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: #72706a;
}

body {
background: var(--background);
color: var(--foreground);
font-family: Arial, Helvetica, sans-serif;
overflow-x: hidden;
}

p {
Expand Down
14 changes: 8 additions & 6 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { Metadata } from 'next';
import { Geist, Geist_Mono } from 'next/font/google';
import { JetBrains_Mono, Syne } from 'next/font/google';
import './globals.css';
import { Analytics } from '@vercel/analytics/next';

const geistSans = Geist({
variable: '--font-geist-sans',
const syne = Syne({
variable: '--font-syne-family',
subsets: ['latin'],
weight: ['400', '500', '600', '700', '800'],
});

const geistMono = Geist_Mono({
variable: '--font-geist-mono',
const jetbrainsMono = JetBrains_Mono({
variable: '--font-jetbrains-mono',
subsets: ['latin'],
weight: ['300', '400', '500'],
});

export const metadata: Metadata = {
Expand All @@ -27,7 +29,7 @@ export default function RootLayout({
return (
<html lang='en' data-nighteye='disabled' suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
className={`${syne.variable} ${jetbrainsMono.variable} bg-bg text-ink font-mono text-sm leading-relaxed antialiased`}
>
{children}
<Analytics />
Expand Down
24 changes: 15 additions & 9 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import Nav from '../components/nav';
import Header from '../components/header';
import Projects from '../components/projects';
import About from '../components/about';
import Stats from '../components/stats';
import Projects from '../components/projects';
import Stack from '../components/stack';
import Contact from '../components/contact';
import Footer from '../components/footer';

export default function Home() {
return (
<div className='bg-[url(/background.png)] bg-no-repeat bg-cover'>
<div className='bg-linear-to-t from-(--color-background) from-60% to-(--transparent) h-screen flex items-center justify-center'>
<div className='h-screen overflow-scroll p-8'>
<Header />
<Projects />
<About id='about' />
</div>
</div>
<div className='relative'>
<Nav />
<Header />
<About id='about' />
<Stats />
<Projects />
<Stack />
<Contact />
<Footer />
</div>
);
}
118 changes: 73 additions & 45 deletions components/about.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,91 @@
import Image from 'next/image';
import FadeIn from './fade-in';

const skills = [
'JavaScript',
'Next.js',
'React',
'React Native',
'Playwright',
'ISTQB',
'MySQL',
'REST APIs',
'Git',
];

interface AboutProps {
id: string;
}

export default function about({ id }: AboutProps) {
export default function About({ id }: AboutProps) {
return (
<section id={id} className='py-12 md:py-16 lg:py-24'>
<div className='max-w-7xl mx-auto px-4 md:px-6 lg:px-8'>
<div className='flex flex-col md:flex-row md:items-center gap-12 md:gap-16'>
{/* Image */}
<div className='relative w-full aspect-9/6 basis-1/5 md:basis-2/5 md:aspect-3/5 lg:basis-2/7 lg:aspect-4/5'>
<section id={id} className='relative z-10 border-t border-bdr px-6 py-20 md:px-10'>
<FadeIn>
<div className='mb-12 flex items-baseline gap-4'>
<span className='text-[11px] tracking-[0.1em] text-accent'>01</span>
<h2 className='font-syne text-[clamp(1.8rem,4vw,2.8rem)] leading-none font-bold tracking-tight text-ink'>
About Me
</h2>
<div className='mb-1.5 h-px flex-1 bg-bdr'></div>
</div>
</FadeIn>

<div className='grid grid-cols-1 items-start gap-16 md:grid-cols-[1fr_2fr]'>
<div>
<div className='photo-frame relative aspect-[3/4] max-w-[280px] overflow-hidden rounded-[4px] border border-bdr bg-surface'>
<Image
src='/aboutme.jpg'
alt='Portrait of Torun Alenius Wikström'
fill={true}
className='object-cover rounded-2xl'
sizes='(min-width: 768px) 40vw, 100vw'
fill
className='object-cover'
sizes='280px'
priority
/>
</div>
</div>

{/* Text */}
<div className='basis-4/5 md:basis-3/5 lg:basis-5/7 flex flex-col gap-6 md:gap-8'>
<h2 className='text-4xl font-bold'>About Me</h2>

<div className='flex flex-col gap-4 md:gap-5 lg:gap-6 text-base leading-7 md:leading-8 lg:leading-9'>
<p>
I’m an ISTQB Certified QA professional with a background in full
stack development. I work mainly with JavaScript and Next.js,
and I’ve built and deployed both web and mobile applications,
including projects with database integrations, API connections,
and payment systems.
</p>

<p>
I started learning web development through documentation, online
resources, and mentorship from a senior developer. After
building several projects and releasing a mobile app, I became
increasingly interested in quality, edge cases, and system
behavior. That curiosity led me to study software testing more
seriously and earn my ISTQB Foundation Level certification.
</p>

<p>
Having written production code myself, I understand how features
are structured, where complexity hides, and how small changes
can affect the whole system. I now want to work in QA, combining
structured testing knowledge with hands on development
experience.
</p>
<FadeIn>
<h3 className='font-syne mb-5 text-[1.4rem] font-semibold text-ink'>
Hey, I&apos;m Torun Alenius Wikström —
</h3>
<p className='mb-4 text-[13px] leading-[1.9] text-muted'>
I&apos;m an ISTQB Certified QA professional with a background in
full stack development. I work mainly with JavaScript and Next.js,
and I&apos;ve built and deployed both web and mobile applications,
including projects with database integrations, API connections, and
payment systems.
</p>
<p className='mb-4 text-[13px] leading-[1.9] text-muted'>
I started learning web development through documentation, online
resources, and mentorship from a senior developer. After building
several projects and releasing a mobile app, I became increasingly
interested in quality, edge cases, and system behavior. That
curiosity led me to study software testing more seriously and earn
my ISTQB Foundation Level certification.
</p>
<p className='mb-4 text-[13px] leading-[1.9] text-muted'>
Having written production code myself, I understand how features are
structured, where complexity hides, and how small changes can affect
the whole system. I now want to work in QA, combining structured
testing knowledge with hands on development experience.
</p>
<p className='text-[13px] leading-[1.9] text-muted'>
Outside of tech, I design knitting patterns, which, much like
writing test cases, requires translating complex structures into
clear, logical, and reproducible instructions.
</p>

<p>
Outside of tech, I design knitting patterns, which, much like
writing test cases, requires translating complex structures into
clear, logical, and reproducible instructions.
</p>
</div>
<div className='mt-8 flex flex-wrap gap-2'>
{skills.map((skill) => (
<span
key={skill}
className='cursor-default rounded-[2px] border border-bdr px-3 py-1 text-[11px] tracking-[0.06em] text-muted transition hover:border-accent hover:text-accent'
>
{skill}
</span>
))}
</div>
</div>
</FadeIn>
</div>
</section>
);
Expand Down
Loading