Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/components/hero.astro
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const cvFile = locale === 'it' ? 'andrea-lisi-cv-it.pdf' : 'andrea-lisi-cv.pdf';
<p class="greeting">{hero.greeting}</p>
<h1 class="name">Andrea Lisi.</h1>
<p class="tagline">{hero.tagline}</p>
<p class="specialization">{hero.eyebrow}</p>
<p class="intro">{hero.intro}</p>

<div class="actions">
Expand Down
6 changes: 6 additions & 0 deletions src/components/hero.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
line-height: 1.3;
color: var(--head);
}
.hero .specialization {
margin: 8px 0 0;
font-size: 17px;
line-height: 1.5;
color: var(--body);
}
.hero .intro {
margin: 22px 0 0;
font-size: 17px;
Expand Down
1 change: 1 addition & 0 deletions src/components/work.astro
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const ROOT_TAGS = new Set(['Design']);
)}
<span class:list={['years', { live: job.current }]}>{job.years}</span>
</div>
{job.specialization && <p class="job-specialization">{job.specialization}</p>}
<p class="meta">{job.context} · {job.place}</p>

<div class="duties">
Expand Down
7 changes: 7 additions & 0 deletions src/components/work.css
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@
}
}

.work .job-specialization {
margin: 7px 0 0;
font-size: 16px;
line-height: 1.5;
color: var(--body);
}

.work .meta {
margin: 5px 0 16px;
font-family: var(--font-mono);
Expand Down
5 changes: 3 additions & 2 deletions src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const en: Copy = {
hero: {
greeting: 'Ciao. My name is',
tagline: 'Senior Software Engineer.',
eyebrow: 'Frontend engineering, architecture and developer tools',
eyebrow: 'Frontend platforms, developer tooling & design systems',
stats: [
{ label: 'Based in', value: 'Vienna, Austria' },
{ label: 'Working', value: 'Remote or hybrid', tone: 'now' },
Expand Down Expand Up @@ -163,8 +163,9 @@ export const en: Copy = {
place: 'VIENNA, AT',
title: 'Senior Software Engineer',
company: 'Tricentis',
specialization: 'Frontend Platform, Governance & Design Systems',
context: 'Work on Tosca Cloud',
tags: ['Micro-frontends', '16 projects', '12+ teams', 'Quality gates'],
tags: ['React', 'TypeScript', 'Micro-frontends', '16 projects', '12+ teams', 'Quality gates'],
current: true,
paragraphs: [
'<b>Cross-team decisions.</b> On Tosca Cloud, together with the team’s architect, I was a point of contact for initiatives spanning the monorepo. I worked with developers, engineering managers, principal engineers from other platforms and product stakeholders to define shared solutions.',
Expand Down
5 changes: 3 additions & 2 deletions src/i18n/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const it: Copy = {
hero: {
greeting: 'Ciao. Mi chiamo',
tagline: 'Senior Software Engineer.',
eyebrow: 'Sviluppo frontend, architettura e strumenti per sviluppatori',
eyebrow: 'Piattaforme frontend, strumenti per sviluppatori e design system',
stats: [
{ label: 'Dove vivo', value: 'Vienna, Austria' },
{ label: 'Come lavoro', value: 'Remoto o ibrido', tone: 'now' },
Expand Down Expand Up @@ -171,8 +171,9 @@ export const it: Copy = {
place: 'VIENNA, AT',
title: 'Senior Software Engineer',
company: 'Tricentis',
specialization: 'Piattaforma frontend, governance e design system',
context: 'Esperienza su Tosca Cloud',
tags: ['Microfrontend', '16 progetti', '12+ team', 'Quality gates'],
tags: ['React', 'TypeScript', 'Microfrontend', '16 progetti', '12+ team', 'Quality gates'],
current: true,
paragraphs: [
'<b>Decisioni tra team.</b> Su Tosca Cloud, insieme all’architetto del team, sono stato un riferimento per le iniziative dell’intera monorepo. Ho lavorato con sviluppatori, engineering manager, principal engineer di altre piattaforme e referenti di prodotto per definire le soluzioni condivise.',
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export type WorkEntry = {
years: string;
place: string;
company: string;
/** Scope of the experience, separate from the official job title. */
specialization?: string;
/**
* What the place was, on the line under the title. Never the job title again:
* three of these repeated it word for word, which the phone layout made
Expand Down
24 changes: 22 additions & 2 deletions tests/e2e/homepage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@ for (const locale of ['en', 'it'] as const) {
expect((description as string).length).toBeLessThan(155);
});

test('opens with the greeting, the name and the tagline', async ({ page }) => {
test('opens with the greeting, the name and the tagline', async ({ page }, testInfo) => {
await page.goto(home);

await expect(page.getByText(copy.hero.greeting)).toBeVisible();
await expect(page.getByRole('heading', { level: 1 })).toHaveText('Andrea Lisi.');
await expect(page.getByText(copy.hero.tagline)).toBeVisible();
await expect(page.locator('.hero .specialization')).toHaveText(copy.hero.eyebrow);
await expect(page.getByText(copy.hero.intro)).toBeVisible();
await page.locator('.hero').screenshot({ path: testInfo.outputPath('hero-desktop.png') });
});

test('shows the portrait, served as WebP with a JPEG fallback', async ({ page }) => {
Expand Down Expand Up @@ -226,6 +228,19 @@ for (const locale of ['en', 'it'] as const) {
await expect(page.locator('.work .job-title').first()).toContainText(
'Senior Software Engineer',
);
await expect(page.locator('.work .job-specialization')).toHaveCount(1);
await expect(page.locator('.work .job-specialization')).toHaveText(
copy.work[0]?.specialization ?? '',
);
await expect(page.locator('.work .job').first().locator('.meta')).toContainText(
'Tosca Cloud',
);
for (const tag of ['React', 'TypeScript']) {
await expect(
page.locator('.work .job').first().locator('.chip').filter({ hasText: tag }),
).toBeVisible();
await expect(page.locator('.work .job-title').first()).not.toContainText(tag);
}
await expect(page.locator('#examples')).toHaveCount(0);
await expect(
page
Expand Down Expand Up @@ -298,7 +313,7 @@ for (const locale of ['en', 'it'] as const) {
await context.close();
});

test('holds together at phone width', async ({ page }) => {
test('holds together at phone width', async ({ page }, testInfo) => {
await page.setViewportSize({ width: 375, height: 812 });
await page.goto(home);

Expand All @@ -310,6 +325,11 @@ for (const locale of ['en', 'it'] as const) {
const cv = await page.locator('.hero .cv').boundingBox();
expect(cv).not.toBeNull();
expect((cv?.y ?? 1000) + (cv?.height ?? 0)).toBeLessThanOrEqual(812);
await page.locator('.hero').screenshot({ path: testInfo.outputPath('hero-mobile.png') });
await page
.locator('.work .job')
.first()
.screenshot({ path: testInfo.outputPath('tricentis-mobile.png') });
});

test('passes axe accessibility checks (no critical/serious violations)', async ({ page }) => {
Expand Down
Loading