Skip to content

Commit 77633b4

Browse files
committed
Editorial v2: drop orb, real nav + mobile menu, smooth scroll, motion layer
1 parent 0c0f3db commit 77633b4

3 files changed

Lines changed: 237 additions & 141 deletions

File tree

‎src/pages/index.astro‎

Lines changed: 95 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
---
22
import Base from '../layouts/Base.astro'
3-
import HeroScene from '../components/HeroScene.tsx'
43
import ContactForm from '../components/ContactForm.tsx'
54
import '../styles/global.css'
65
76
const nav = [
8-
{ n: '01', label: 'Index', href: '#top' },
9-
{ n: '02', label: 'Profile', href: '#profile' },
10-
{ n: '03', label: 'Work', href: '#work' },
11-
{ n: '04', label: 'Record', href: '#record' },
12-
{ n: '05', label: 'Contact', href: '#contact' },
7+
{ label: 'Profile', href: '#profile' },
8+
{ label: 'Work', href: '#work' },
9+
{ label: 'Record', href: '#record' },
10+
{ label: 'Contact', href: '#contact' },
1311
]
1412
13+
const stack = ['Java', 'C#', 'Python', 'Node.js', 'Flutter', 'Dart', 'Rust', 'SQL', 'Azure', 'Firebase', 'REST APIs', 'Git']
14+
1515
const skills = [
1616
{ k: 'Languages', v: ['Java', 'C#', 'Python', 'JavaScript', 'SQL', 'Dart', 'Rust'] },
1717
{ k: 'Web', v: ['HTML', 'CSS', 'Node.js', 'REST APIs'] },
1818
{ k: 'Mobile', v: ['Flutter', 'Dart', 'Kivy'] },
1919
{ k: 'Cloud / DB', v: ['Azure', 'Firebase'] },
20-
{ k: 'Tools', v: ['Git', 'CI/CD', 'pytest', 'Agile', 'Async / network'] },
20+
{ k: 'Tools', v: ['Git', 'CI/CD', 'pytest', 'Agile'] },
21+
{ k: 'Spoken', v: ['English', 'Afrikaans'] },
2122
]
2223
2324
const work = [
@@ -29,10 +30,10 @@ const work = [
2930
]
3031
3132
const record = [
32-
{ year: '2026 -', role: 'Junior Software Engineer', org: 'Coast IT Pty (Ltd) · Full-time' },
33-
{ year: '2025', role: 'Junior Software Engineer', org: 'Coast IT Pty (Ltd) · Internship' },
33+
{ year: '2026 -', role: 'Junior Software Engineer', org: 'Coast IT Pty (Ltd), Full-time' },
34+
{ year: '2025', role: 'Junior Software Engineer', org: 'Coast IT Pty (Ltd), Internship' },
3435
{ year: '2025', role: 'Intern', org: 'Open Vantage' },
35-
{ year: '2025 - 26', role: 'Freelance Web Developer', org: 'Self-employed' },
36+
{ year: '2025-26', role: 'Freelance Web Developer', org: 'Self-employed' },
3637
{ year: '2023 -', role: 'Software Engineering (NQF 6)', org: 'CTU Training Solutions, Boksburg' },
3738
{ year: '2021', role: 'National Senior Certificate', org: 'Bergsig Academy, Rustenburg' },
3839
]
@@ -50,114 +51,117 @@ const certs = [
5051
<Base>
5152
<a id="top"></a>
5253

53-
<!-- Fixed index bar -->
54-
<header id="site-header" class="fixed top-0 inset-x-0 z-50" style="background: rgba(var(--bg-rgb),0.85); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); border-bottom: 1px solid var(--border)">
54+
<header id="site-header" class="fixed top-0 inset-x-0 z-50">
5555
<div class="container-wide">
56-
<div id="nav-inner" class="flex items-center justify-between h-16 transition-all duration-300">
57-
<a href="#top" class="ed-mono font-bold tracking-tight">RR / ROBERTS</a>
58-
<nav class="hidden md:flex items-center gap-6">
56+
<div id="nav-inner" class="flex items-center justify-between h-[72px] transition-all duration-300">
57+
<a href="#top" class="ed-mono font-bold tracking-tight text-sm">RR / ROBERTS</a>
58+
<nav class="hidden md:flex items-center gap-7">
5959
{nav.map(item => (
60-
<a href={item.href} class="ed-mono text-xs uppercase tracking-widest nav-link">
61-
<span style="color: var(--accent)">{item.n}</span> {item.label}
62-
</a>
60+
<a href={item.href} class="ed-mono text-xs uppercase tracking-widest nav-link">{item.label}</a>
6361
))}
6462
</nav>
65-
<span class="ed-mono text-xs uppercase tracking-widest hidden sm:inline" style="color: var(--muted)">Germiston, ZA</span>
63+
<div class="flex items-center gap-4">
64+
<span class="ed-avail hidden sm:inline-flex"><i></i>Available for work</span>
65+
<button class="ed-menu-btn ed-mono text-xs uppercase tracking-widest" id="ed-menu-open" aria-label="Open menu">Menu</button>
66+
</div>
6667
</div>
6768
</div>
6869
</header>
6970

70-
<main class="container-wide pt-24">
71+
<div id="ed-mobile" aria-hidden="true">
72+
<button id="ed-menu-close" class="ed-mono text-xs uppercase tracking-widest absolute top-6 right-6" style="color: var(--muted)" aria-label="Close menu">Close</button>
73+
{nav.map(item => <a href={item.href} data-ed-mclose>{item.label}</a>)}
74+
</div>
7175

72-
<!-- Masthead -->
73-
<section class="pt-6 pb-16 lg:pb-24">
74-
<div class="flex items-center justify-between ed-kicker mb-6">
75-
<span>Full-stack Developer</span>
76-
<span>Est. 2021 - Portfolio No.05</span>
77-
</div>
78-
<hr class="ed-rule-accent mb-8" />
79-
80-
<div class="grid grid-cols-1 lg:grid-cols-[1fr_auto] gap-8 items-end">
81-
<h1 class="ed-huge" style="font-size: clamp(3.2rem, 13vw, 11rem)" data-ed-title>
82-
<span class="block">Ronan</span>
83-
<span class="block ed-outline">Roberts</span>
84-
</h1>
85-
<div class="hidden lg:block w-[260px] h-[260px] border" style="border-color: var(--border)" aria-hidden="true">
86-
<HeroScene client:only="react" />
87-
</div>
88-
</div>
76+
<main class="container-wide pt-[72px]">
8977

90-
<hr class="ed-rule mt-8 mb-6" />
91-
<div class="grid grid-cols-1 md:grid-cols-[2fr_1fr] gap-8">
92-
<p class="text-lg leading-relaxed" style="max-width: 60ch">
93-
Full-stack developer building across the stack, web front-ends and
94-
back-ends in Java, C#, Python and Node.js, and cross-platform mobile
95-
apps in Flutter and Dart. Currently a Junior Software Engineer at Coast
96-
IT, completing a Software Engineer qualification (NQF 6) with CTU.
78+
<!-- Masthead -->
79+
<section class="min-h-[calc(100dvh-72px)] flex flex-col justify-center pt-10 pb-16">
80+
<div class="ed-kicker mb-6" data-reveal>Full-stack Developer, Germiston ZA</div>
81+
<hr class="ed-rule-accent mb-8" data-reveal />
82+
83+
<h1 class="ed-huge" style="font-size: clamp(3.4rem, 15vw, 13rem)" data-ed-title>
84+
<span class="ed-line"><span>Ronan</span></span>
85+
<span class="ed-line"><span class="ed-outline">Roberts</span></span>
86+
</h1>
87+
88+
<hr class="ed-rule mt-10 mb-8" data-reveal />
89+
<div class="grid grid-cols-1 md:grid-cols-[1.7fr_1fr] gap-8 md:gap-16 items-end">
90+
<p class="text-lg md:text-xl leading-relaxed" style="max-width: 58ch" data-reveal>
91+
I build across the stack, web front-ends and back-ends in Java, C#,
92+
Python and Node.js, and cross-platform mobile apps in Flutter and Dart.
93+
Currently a Junior Software Engineer at Coast IT.
9794
</p>
98-
<div class="ed-mono text-xs" style="color: var(--muted)">
99-
<div class="flex justify-between py-1"><span>STATUS</span><span style="color: var(--accent)">AVAILABLE</span></div>
100-
<div class="flex justify-between py-1" style="border-top: 1px solid var(--border)"><span>WORK</span><span>FT / PT / CONTRACT</span></div>
101-
<div class="flex justify-between py-1" style="border-top: 1px solid var(--border)"><span>FOCUS</span><span>WEB / MOBILE</span></div>
95+
<div class="flex flex-wrap gap-3 md:justify-end" data-reveal>
96+
<a href="#work" class="btn-primary magnetic" data-magnetic>View work</a>
97+
<a href="#contact" class="btn-ghost magnetic" data-magnetic>Get in touch</a>
10298
</div>
10399
</div>
104100
</section>
101+
</main>
105102

106-
<!-- Profile / skills -->
107-
<section id="profile" class="py-16 lg:py-24">
108-
<div class="flex items-baseline gap-4 mb-10">
109-
<span class="ed-num">02</span>
110-
<h2 class="ed-huge" style="font-size: clamp(2rem, 6vw, 4rem)">Profile</h2>
103+
<!-- Tech marquee -->
104+
<section class="py-6 border-y" style="border-color: var(--border)" aria-hidden="true">
105+
<div class="marquee">
106+
<div class="marquee__track">
107+
{stack.map(s => <span class="marquee__item">{s}</span>)}
108+
</div>
109+
<div class="marquee__track" aria-hidden="true">
110+
{stack.map(s => <span class="marquee__item">{s}</span>)}
111111
</div>
112-
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-10 gap-y-8">
112+
</div>
113+
</section>
114+
115+
<main class="container-wide">
116+
117+
<!-- Profile / skills -->
118+
<section id="profile" class="py-20 lg:py-28">
119+
<h2 class="ed-huge mb-12" style="font-size: clamp(2rem, 6vw, 4rem)" data-reveal>Profile</h2>
120+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-10 gap-y-10">
113121
{skills.map(group => (
114-
<div>
122+
<div data-reveal>
115123
<div class="ed-kicker mb-3 pb-2" style="border-bottom: 1px solid var(--border)">{group.k}</div>
116124
<ul class="ed-mono text-sm leading-8">
117125
{group.v.map(item => <li>{item}</li>)}
118126
</ul>
119127
</div>
120128
))}
121-
<div>
122-
<div class="ed-kicker mb-3 pb-2" style="border-bottom: 1px solid var(--border)">Languages (spoken)</div>
123-
<ul class="ed-mono text-sm leading-8"><li>English</li><li>Afrikaans</li></ul>
124-
</div>
125129
</div>
126130
</section>
127131

128132
<!-- Work index -->
129-
<section id="work" class="py-16 lg:py-24">
130-
<div class="flex items-baseline gap-4 mb-8">
131-
<span class="ed-num">03</span>
132-
<h2 class="ed-huge" style="font-size: clamp(2rem, 6vw, 4rem)">Selected Work</h2>
133+
<section id="work" class="py-20 lg:py-28">
134+
<div class="flex items-end justify-between mb-10 gap-4">
135+
<h2 class="ed-huge" style="font-size: clamp(2rem, 6vw, 4rem)" data-reveal>Selected Work</h2>
136+
<span class="ed-mono text-xs hidden sm:block" style="color: var(--muted)" data-reveal>2024 to 2026</span>
133137
</div>
134138
<div>
135139
{work.map(item => (
136-
<a href={item.href} target="_blank" rel="noopener noreferrer" class="work-row group" data-ed-row>
140+
<a href={item.href} target="_blank" rel="noopener noreferrer" class="work-row group" data-reveal>
137141
<span class="ed-num">{item.n}</span>
138142
<span>
139143
<span class="work-title block">{item.title}</span>
140144
<span class="ed-mono text-xs" style="color: var(--muted)">{item.meta}</span>
141145
</span>
142-
<span class="flex flex-col items-end gap-2">
143-
<span class="ed-mono text-xs" style="color: var(--muted)">{item.year} ↗</span>
144-
<span class="hidden sm:flex flex-wrap gap-1.5 justify-end" style="max-width: 240px">
146+
<span class="flex items-center gap-4">
147+
<span class="hidden md:flex flex-wrap gap-1.5 justify-end" style="max-width: 220px">
145148
{item.tags.map(t => <span class="ed-tag">{t}</span>)}
146149
</span>
150+
<span class="ed-mono text-xs hidden sm:inline" style="color: var(--muted)">{item.year}</span>
151+
<span class="work-row__arrow" aria-hidden="true">
152+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>
153+
</span>
147154
</span>
148155
</a>
149156
))}
150157
</div>
151158
</section>
152159

153-
<!-- Record: experience + certs -->
154-
<section id="record" class="py-16 lg:py-24">
155-
<div class="flex items-baseline gap-4 mb-8">
156-
<span class="ed-num">04</span>
157-
<h2 class="ed-huge" style="font-size: clamp(2rem, 6vw, 4rem)">Record</h2>
158-
</div>
160+
<!-- Record -->
161+
<section id="record" class="py-20 lg:py-28">
162+
<h2 class="ed-huge mb-12" style="font-size: clamp(2rem, 6vw, 4rem)" data-reveal>Record</h2>
159163
<div class="grid grid-cols-1 lg:grid-cols-2 gap-x-16 gap-y-12">
160-
<div>
164+
<div data-reveal>
161165
<div class="ed-kicker mb-4">Experience &amp; Education</div>
162166
{record.map(r => (
163167
<div class="grid grid-cols-[5.5rem_1fr] gap-4 py-4" style="border-top: 1px solid var(--border)">
@@ -169,7 +173,7 @@ const certs = [
169173
</div>
170174
))}
171175
</div>
172-
<div>
176+
<div data-reveal>
173177
<div class="ed-kicker mb-4">Certifications</div>
174178
{certs.map(c => (
175179
<div class="flex items-baseline justify-between gap-4 py-4" style="border-top: 1px solid var(--border)">
@@ -185,15 +189,12 @@ const certs = [
185189
</section>
186190

187191
<!-- Contact -->
188-
<section id="contact" class="py-16 lg:py-28">
189-
<div class="flex items-baseline gap-4 mb-8">
190-
<span class="ed-num">05</span>
191-
<h2 class="ed-huge" style="font-size: clamp(2rem, 6vw, 4rem)">Get in touch</h2>
192-
</div>
193-
<hr class="ed-rule-accent mb-10" />
192+
<section id="contact" class="py-20 lg:py-28">
193+
<h2 class="ed-huge mb-8" style="font-size: clamp(2rem, 6vw, 4rem)" data-reveal>Get in touch</h2>
194+
<hr class="ed-rule-accent mb-12" data-reveal />
194195
<div class="grid grid-cols-1 lg:grid-cols-[1fr_1fr] gap-12 lg:gap-20">
195-
<div>
196-
<p class="text-2xl md:text-3xl font-display font-bold leading-tight mb-8" style="max-width: 18ch">
196+
<div data-reveal>
197+
<p class="text-2xl md:text-4xl font-display font-bold leading-tight mb-10" style="max-width: 16ch">
197198
Open to freelance projects and job opportunities.
198199
</p>
199200
<div class="ed-mono text-sm flex flex-col gap-3">
@@ -202,20 +203,29 @@ const certs = [
202203
<a href="https://github.com/Ronan-R-R" target="_blank" rel="noopener noreferrer" class="ed-link w-fit">github.com/Ronan-R-R</a>
203204
</div>
204205
</div>
205-
<div>
206+
<div data-reveal>
206207
<ContactForm client:visible />
207208
</div>
208209
</div>
209210
</section>
210211

211212
<footer class="py-10 ed-mono text-xs flex flex-col sm:flex-row justify-between gap-3" style="border-top: 1px solid var(--border); color: var(--muted)">
212213
<span>© <span id="footer-year"></span> Ronan Roberts</span>
213-
<span>Designed &amp; built by Ronan Roberts</span>
214+
<span>Designed and built by Ronan Roberts</span>
214215
</footer>
215216
</main>
216217

217218
<script>
218219
const el = document.getElementById('footer-year')
219220
if (el) el.textContent = String(new Date().getFullYear())
221+
222+
const open = document.getElementById('ed-menu-open')
223+
const close = document.getElementById('ed-menu-close')
224+
const panel = document.getElementById('ed-mobile')
225+
open?.addEventListener('click', () => panel?.classList.add('open'))
226+
close?.addEventListener('click', () => panel?.classList.remove('open'))
227+
document.querySelectorAll('[data-ed-mclose]').forEach((a) =>
228+
a.addEventListener('click', () => panel?.classList.remove('open'))
229+
)
220230
</script>
221231
</Base>

0 commit comments

Comments
 (0)