From baa5c17a10b4e14f271be0fe7a8768710cff000a Mon Sep 17 00:00:00 2001 From: Andrea Lisi Date: Fri, 21 Aug 2026 10:40:36 +0200 Subject: [PATCH 1/2] content: refine portfolio copy and branding --- public/favicon.svg | 26 ++------- src/components/work.astro | 3 -- src/i18n/en.ts | 105 +++++++++++++++++++----------------- src/i18n/it.ts | 108 ++++++++++++++++++++----------------- src/i18n/types.ts | 7 --- src/layouts/layout.astro | 2 +- tests/e2e/homepage.spec.ts | 20 +++---- 7 files changed, 124 insertions(+), 147 deletions(-) diff --git a/public/favicon.svg b/public/favicon.svg index 928366f..908a138 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,25 +1,5 @@ - Andrea Lisi asteroid mark - - - - - - - - - - - - - - - - - + Andrea Lisi mark + + diff --git a/src/components/work.astro b/src/components/work.astro index 68470a1..f4bcf76 100644 --- a/src/components/work.astro +++ b/src/components/work.astro @@ -36,7 +36,6 @@ const ICONS: Record[0]['name']> = { Web: 'box', Backend: 'server', Design: 'pen', - 'Full-stack': 'stack', Infrastructure: 'server', Infrastruttura: 'server', }; @@ -83,6 +82,4 @@ const ROOT_TAGS = new Set(['Design']); ))} - -

diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 84d2fc3..7633ecc 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -13,7 +13,7 @@ export const en: Copy = { aiMeta: { title: 'AI · Andrea Lisi · Frontend Platform & Governance', description: - 'How I use AI in engineering work: generated code follows the same review, testing and verification as code written by hand.', + 'How I use AI assistants and agents in development: where they help, how I review the result and what checks I apply.', }, skipLink: 'Skip to content', @@ -45,14 +45,14 @@ export const en: Copy = { { label: 'FOUNDATION', value: 'Art school, Florence', tone: 'root' }, ], intro: - 'I am a Senior Software Engineer with over 18 years of experience, focused on frontend platforms and the problems that appear when many teams work on the same codebase. I came to code through art and design, and I still care whether the structure makes sense at a glance.', + 'I’m a Senior Software Engineer with over 18 years of experience. Today I work on the frontend platform behind Tosca Cloud, a monorepo containing 16 projects and shared by more than 12 teams. My focus is architecture, dependencies, builds and shared standards.', buttons: { github: 'GITHUB', linkedin: 'LINKEDIN', email: 'EMAIL', cv: 'CV.PDF ↓' }, }, sections: { approach: { num: '01', title: 'How I work' }, focus: { num: '02', title: 'What I work on' }, - ai: { num: '03', title: 'AI', eyebrow: 'TOOLS, REVIEW AND VERIFICATION' }, + ai: { num: '03', title: 'AI', eyebrow: 'USE, REVIEW AND TESTING' }, experience: { num: '04', title: 'Experience' }, projects: { num: '05', title: 'Personal projects' }, contact: { num: '06', title: 'Get in touch' }, @@ -62,18 +62,18 @@ export const en: Copy = { { label: 'Art_School', tone: 'root', - title: 'I still look at code the way I looked at design', - body: 'I started in design, then moved through frontend, full-stack work and infrastructure. That background probably explains why I care so much about structure. I like software that makes sense when you look at it, not only when you run it.', + title: 'From design to frontend engineering', + body: 'I started in design, but my career is in frontend engineering. Over time I have also worked on backend systems and infrastructure. That experience helps me see what changes elsewhere in the system when I work on the frontend.', }, { label: 'The_Boundaries', - title: 'The rules should survive my absence', - body: 'A large part of my work sits between teams: module boundaries, shared code, dependencies, build tooling and CI. I write down the reason for a rule, then turn it into a check when possible. The codebase should not depend on somebody remembering how things are supposed to work.', + title: 'Rules and automated checks', + body: 'I often work on problems that affect several teams: module boundaries, dependencies, builds and CI. When a rule matters, I document the reason and add an automated check where I can. That lets the check do the reminding, so I do not have to repeat the same thing to dozens of people one at a time.', }, { label: 'Hands_On', - title: 'I stay close to the code', - body: 'I still spend a lot of time in implementation and debugging. I work on shared tooling, investigate platform-wide breakage and automate decisions that would otherwise be repeated in reviews. When a problem belongs to another team, I document it, hand it over clearly and stay available without taking the work away from them.', + title: 'I work on the code and alongside the teams', + body: 'I continue to implement, debug and work on shared engineering tools. Every day I work with developers across the teams, from junior to senior, and I regularly speak with their managers. When a decision affects several products, I also work with design, security and sales to understand what is needed and apply it consistently.', }, ], @@ -84,39 +84,50 @@ export const en: Copy = { 'Monorepo architecture', 'Micro-frontends', 'Module boundaries', - 'Dependency architecture', - 'RFCs', + 'Dependency governance', + 'Technical RFCs', ], }, { - title: 'Frontend', - items: ['React', 'TypeScript', 'Vue', 'GraphQL', 'Node.js'], + title: 'Frontend & integrations', + items: [ + 'React', + 'TypeScript', + 'Vue', + 'GraphQL', + 'Node.js', + 'Frontend Okta integration', + 'JWT handling', + 'OpenTelemetry', + ], }, { - title: 'Tooling & delivery', - items: ['Nx', 'Vite', 'Webpack', 'CI/CD', 'Testing', 'Build performance', 'Quality gates'], + title: 'Build & delivery', + items: [ + 'CI architecture', + 'Pipeline automation', + 'Build caching & performance', + 'Quality gates', + 'Automated testing', + ], }, { title: 'Developer experience', - items: [ - 'Shared tooling', - 'Engineering standards', - 'Developer experience', - 'AI-assisted and agentic workflows', - ], + items: ['Shared tooling', 'Engineering standards', 'Documentation', 'AI tooling evaluation'], }, ], ai: { - heading: 'How I use AI in engineering work', + heading: 'How I use AI in development', paragraphs: [ - 'AI-assisted and agentic development is part of my workflow, but generated code follows the same standard as any other code. If it cannot be reviewed, tested and verified, it does not go in.', + 'I use AI assistants and agents to analyse a project’s code, prepare changes and reduce repetitive work. I use them only when I can understand what they did and check that it works.', + 'I test them on real work to see where they lose context in the codebase. I use that to update the repository’s documentation, rules and tooling.', ], - rulesLead: 'Three rules keep that practical.', + rulesLead: 'In practice:', rules: [ - 'I only delegate what I can verify. If I cannot review and understand the result, doing it faster is not useful.', - 'I check whether the change belongs before I check whether it works. Agents will rebuild things that already exist and make the suggestion sound convincing. The first question is whether the new code should exist at all.', - 'The same gates apply to every contributor. Human or generated, code has to respect the same module boundaries, tests and quality checks before it reaches main.', + 'First I check whether the change is needed. An agent can suggest rebuilding something that already exists in the project.', + 'I review the result like any other contribution. I read the diff, run the tests and check that it respects the project’s boundaries.', + 'I need to be able to explain and maintain every change. If I cannot, I do not use it.', ], backLabel: 'ANDREA LISI', }, @@ -131,12 +142,11 @@ export const en: Copy = { tags: ['Micro-frontends', '16 projects', '12+ teams', 'Quality gates'], current: true, paragraphs: [ - 'I work on the frontend platform behind Tosca Cloud, a micro-frontend monorepo shared across 16 projects and more than 12 teams.', - 'I own architectural and governance work across the monorepo, including module boundaries, dependency rules and shared frontend standards.', - 'I work with the platform architect on build performance, CI topology and quality gates.', - 'I handle platform-wide upgrades and shared integrations, fixing breakage centrally before individual teams inherit it.', - 'I review RFCs and cross-team changes when they affect shared frontend boundaries.', - 'I turn conventions into machine-checkable rules for both human and agent-generated code, and stay hands-on with implementation and debugging when issues affect the platform as a whole.', + 'I work on the frontend platform behind Tosca Cloud, a micro-frontend monorepo containing 16 projects and shared by more than 12 teams.', + 'I work on the monorepo’s architecture and governance, particularly module boundaries, dependency rules and shared frontend standards. I also review RFCs that affect the shared frontend.', + 'I administer the Tosca Cloud repository on GitHub and automate its pipelines, including on Azure. With the platform architect, I work on build caching and performance, CI structure and quality gates.', + 'I integrated OpenTelemetry into the frontend. After the Okta login, I handle the application-side integration, including JWTs and HTTP interceptors. When an issue appears across several projects, I debug it at platform level.', + 'I regularly evaluate AI tools, including Claude and Cursor, and how we use them. When the same limitation keeps appearing, I update the repository’s documentation, rules or shared tools.', ], }, { @@ -147,10 +157,10 @@ export const en: Copy = { context: 'E-commerce platform for tree planting', tags: ['React', 'Vue', 'GraphQL', 'Storefront migration'], paragraphs: [ - 'Joined the frontend team and later led the frontend side of the storefront migration away from the monolith, working with React, Vue and GraphQL.', - 'Rebuilt the storefront foundations as the company grew from around 20 to more than 150 people.', - 'Built high-traffic campaign experiences for a platform that has planted millions of trees.', - 'In my final year, built the frontend of the company’s internal back-office tools from scratch, working closely with the backend team and the people using those tools for forestry operations.', + 'As part of the frontend team, I worked on moving the storefront out of the monolith using React, Vue and GraphQL.', + 'I helped rebuild the storefront as the company grew from around 20 to more than 150 people.', + 'I built interfaces for high-traffic campaigns. I worked regularly with the design and design system teams to integrate requests, match the mockups and follow the shared standards.', + 'For my final two years, I built the frontend for the internal back-office tools from scratch. I was the project’s only frontend developer and worked alongside the backend team and the people who used those tools for forestry operations.', ], }, { @@ -161,25 +171,23 @@ export const en: Copy = { context: 'Luxury e-commerce', tags: ['ASP', 'jQuery', 'React', 'Webpack'], paragraphs: [ - 'Modernized a live luxury e-commerce platform without a full rewrite, moving it from legacy ASP and jQuery to React with a Webpack-based frontend toolchain.', - 'Kept shipping features while the migration was in progress and the production shop remained live.', + 'Modernized a luxury e-commerce platform while it was in production. Gradually replaced ASP and jQuery with React and a new Webpack setup.', + 'Continued to build new features during the migration without taking the shop offline.', 'Worked on both the storefront and the internal CMS used for orders and day-to-day shop operations.', - 'Gradually replaced the legacy frontend while changing the architecture underneath it.', ], }, { years: '2007 · 2017', place: 'FIGLINE VALDARNO, IT', - title: 'Head of Web · Full-Stack Designer', + title: 'Head of Web · Designer & Developer', company: 'Easysystem', context: 'Web agency and IT services', tags: ['Design', 'Web', 'Backend', 'Infrastructure'], paragraphs: [ - 'I joined Easysystem as a designer and gradually ended up doing almost every technical part of client work: web development, backend systems and eventually on-premise infrastructure.', + 'I joined as a designer. Over the years I also worked on web development, backend systems and infrastructure installed at client sites.', 'Designed websites, interfaces, visual identities and print material from scratch for client projects.', 'Built and maintained both frontend and backend applications.', 'Installed servers, networks and firewalls on site for client companies, including manufacturers in Tuscany’s leather and fashion district.', - 'Followed projects from the first design concepts through development and, in some cases, all the way down to the infrastructure they ran on.', ], }, ], @@ -189,7 +197,7 @@ export const en: Copy = { name: 'Lumina', kind: 'FANTASY MUD', description: - 'A fantasy MUD I built end to end, from the Node.js/MongoDB backend to the React client and Cloudflare infrastructure.', + 'A fantasy MUD I built myself: a Node.js/MongoDB backend, a React client and Cloudflare infrastructure.', tags: ['Node.js', 'MongoDB', 'React', 'Cloudflare'], }, { @@ -202,9 +210,6 @@ export const en: Copy = { }, ], - workClosing: - 'I started in design, moved through frontend, backend and infrastructure, and ended up working on the structure shared by many teams. The scale changed but the habit did not: understand how the whole thing fits together, then fix the part that keeps making the same problem return.', - footer: { built: 'Written, designed and shipped by me.', }, @@ -212,9 +217,9 @@ export const en: Copy = { nowBadge: 'CURRENT', contact: { - invite: 'Write any time, even just to talk.', - inviteMore: - 'A new conversation has never once bothered me, and a proposal that makes me curious even less so.', + invite: + 'Hey! If you want to talk about frontend platforms, developer experience or simply compare notes, send me a message.', + inviteMore: 'It doesn’t need to be formal. A quick message is fine too.', cv: 'CV (PDF)', github: 'GitHub', linkedin: 'LinkedIn', diff --git a/src/i18n/it.ts b/src/i18n/it.ts index 5111fc8..a16bf11 100644 --- a/src/i18n/it.ts +++ b/src/i18n/it.ts @@ -14,7 +14,7 @@ export const it: Copy = { aiMeta: { title: 'AI · Andrea Lisi · Frontend Platform & Governance', description: - 'Come uso l’AI nel lavoro di engineering: il codice generato passa dalla stessa revisione, dagli stessi test e dalle stesse verifiche.', + 'Come uso assistenti AI e agenti nello sviluppo: quando servono, come rivedo il risultato e quali controlli applico.', }, skipLink: 'Vai al contenuto', @@ -46,14 +46,14 @@ export const it: Copy = { { label: 'FORMAZIONE', value: 'Istituto d’Arte, Firenze', tone: 'root' }, ], intro: - 'Sono un Senior Software Engineer con più di 18 anni di esperienza, oggi concentrato sulle piattaforme frontend e sui problemi che emergono quando molti team lavorano sulla stessa codebase. Sono arrivato al codice passando dall’arte e dal design, e mi interessa ancora che la struttura si capisca al primo sguardo.', + 'Sono un Senior Software Engineer con più di 18 anni di esperienza. Oggi lavoro sulla piattaforma frontend di Tosca Cloud, una monorepo che raccoglie 16 progetti ed è condivisa da più di 12 team. Mi occupo soprattutto di architettura, dipendenze, build e standard condivisi.', buttons: { github: 'GITHUB', linkedin: 'LINKEDIN', email: 'EMAIL', cv: 'CV.PDF ↓' }, }, sections: { approach: { num: '01', title: 'Come lavoro' }, focus: { num: '02', title: 'Su cosa lavoro' }, - ai: { num: '03', title: 'AI', eyebrow: 'STRUMENTI, REVISIONE E VERIFICA' }, + ai: { num: '03', title: 'AI', eyebrow: 'USO, REVISIONE E TEST' }, experience: { num: '04', title: 'Esperienza' }, projects: { num: '05', title: 'Progetti personali' }, contact: { num: '06', title: 'Restiamo in contatto' }, @@ -63,18 +63,18 @@ export const it: Copy = { { label: 'Art_School', tone: 'root', - title: 'Guardo ancora il codice con l’occhio del design', - body: 'Ho cominciato dal design, poi sono passato dal frontend al full-stack e all’infrastruttura. Probabilmente è per questo che tengo così tanto alla struttura. Mi piace il software che ha senso quando lo guardi, non solo quando lo esegui.', + title: 'Dal design al frontend', + body: 'Ho cominciato dal design, ma la mia carriera è nel frontend. Nel tempo ho lavorato anche su backend e infrastruttura. Questa esperienza mi aiuta a vedere cosa cambia nel resto del sistema quando intervengo sul frontend.', }, { label: 'The_Boundaries', - title: 'Le regole devono reggere anche quando non ci sono', - body: 'Una parte importante del mio lavoro sta fra i team: confini dei moduli, codice condiviso, dipendenze, build tooling e CI. Scrivo il motivo di una regola e, quando posso, la trasformo in un controllo. La codebase non deve dipendere da qualcuno che si ricorda come dovrebbero funzionare le cose.', + title: 'Regole e controlli automatici', + body: 'Lavoro spesso su problemi che coinvolgono più team: confini tra moduli, dipendenze, build e CI. Quando una regola è importante, ne documento il motivo e, se possibile, aggiungo un controllo automatico. Così è il controllo a ricordare la regola, e io evito di ripeterla a decine di persone, una alla volta.', }, { label: 'Hands_On', - title: 'Resto dentro il codice', - body: 'Passo ancora molto tempo a implementare e fare debugging. Lavoro sul tooling condiviso, indago i problemi che toccano tutta la piattaforma e automatizzo decisioni che altrimenti si ripeterebbero in ogni review. Quando un problema appartiene a un altro team, lo documento, lo passo in modo chiaro e resto disponibile senza portargli via il lavoro.', + title: 'Lavoro sul codice e con i team', + body: 'Continuo a implementare, fare debugging e lavorare sugli strumenti condivisi. Collaboro ogni giorno con gli sviluppatori dei team, dagli junior ai senior, e mi confronto spesso con i loro manager. Quando una decisione coinvolge più prodotti, lavoro anche con design, security e sales per capire cosa serve e applicarlo in modo coerente.', }, ], @@ -84,40 +84,56 @@ export const it: Copy = { items: [ 'Architettura di monorepo', 'Micro-frontend', - 'Confini dei moduli', - 'Architettura delle dipendenze', - 'RFC', + 'Confini tra moduli', + 'Governance delle dipendenze', + 'RFC tecnici', ], }, { - title: 'Frontend', - items: ['React', 'TypeScript', 'Vue', 'GraphQL', 'Node.js'], + title: 'Frontend e integrazioni', + items: [ + 'React', + 'TypeScript', + 'Vue', + 'GraphQL', + 'Node.js', + 'Integrazione Okta lato frontend', + 'Gestione JWT', + 'OpenTelemetry', + ], }, { - title: 'Tooling e delivery', - items: ['Nx', 'Vite', 'Webpack', 'CI/CD', 'Testing', 'Build performance', 'Quality gates'], + title: 'Build e rilascio', + items: [ + 'Architettura CI', + 'Automazione delle pipeline', + 'Cache e performance delle build', + 'Controlli di qualità', + 'Test automatici', + ], }, { title: 'Developer experience', items: [ - 'Tool condivisi', - 'Standard di engineering', - 'Developer experience', - 'Workflow assistiti e agentici', + 'Strumenti condivisi', + 'Standard tecnici', + 'Documentazione', + 'Valutazione di strumenti AI', ], }, ], ai: { - heading: 'Come uso l’AI nel lavoro di engineering', + heading: 'Come uso l’AI nello sviluppo', paragraphs: [ - 'Lo sviluppo assistito e agentico fa parte del mio workflow, ma il codice generato segue gli stessi standard di tutto il resto. Se non può essere revisionato, testato e verificato, non entra.', + 'Uso assistenti AI e agenti per analizzare il codice di un progetto, preparare modifiche e ridurre il lavoro ripetitivo. Li uso solo se posso capire cosa hanno fatto e verificare che funzioni.', + 'Li provo su attività reali per capire dove perdono il contesto della codebase. In base a quello aggiorno documentazione, regole e strumenti del repository.', ], - rulesLead: 'Tre regole tengono questo lavoro sul pratico.', + rulesLead: 'In pratica:', rules: [ - 'Delego solo quello che so verificare. Se non riesco a revisionare e capire il risultato, farlo più in fretta non serve.', - 'Prima controllo se la modifica deve esistere, poi se funziona. Gli agenti propongono di ricostruire cose che esistono già e lo fanno in modo convincente. La prima domanda è se quel nuovo codice abbia davvero un posto nel progetto.', - 'Gli stessi gate valgono per ogni contributore. Scritto da una persona o generato, il codice deve rispettare gli stessi confini dei moduli, gli stessi test e gli stessi controlli di qualità prima di arrivare in main.', + 'Prima verifico che la modifica serva. Un agente può suggerire di ricostruire qualcosa che nel progetto esiste già.', + 'Rivedo il risultato come qualsiasi altro contributo. Leggo il diff, eseguo i test e controllo che rispetti i confini del progetto.', + 'Devo poter spiegare e mantenere ogni modifica. Se non ci riesco, non la uso.', ], backLabel: 'ANDREA LISI', }, @@ -132,12 +148,11 @@ export const it: Copy = { tags: ['Microfrontend', '16 progetti', '12+ team', 'Quality gates'], current: true, paragraphs: [ - 'Lavoro sulla piattaforma frontend dietro Tosca Cloud, una monorepo a micro-frontend condivisa da 16 progetti e più di 12 team.', - 'Mi occupo dell’architettura e della governance della monorepo, dai confini dei moduli alle regole sulle dipendenze e agli standard frontend condivisi.', - 'Lavoro con il platform architect sulle performance di build, sulla topologia della CI e sui quality gate.', - 'Gestisco gli aggiornamenti che riguardano tutta la piattaforma e le integrazioni condivise, sistemando il guasto al centro prima che arrivi ai singoli team.', - 'Rivedo RFC e modifiche trasversali quando toccano i confini del frontend condiviso.', - 'Trasformo le convenzioni in regole verificabili dalla macchina, per il codice scritto dalle persone e dagli agenti, e continuo a implementare e fare debugging quando il problema riguarda la piattaforma nel suo insieme.', + 'Lavoro sulla piattaforma frontend di Tosca Cloud, una monorepo a micro-frontend che raccoglie 16 progetti ed è condivisa da più di 12 team.', + 'Lavoro sull’architettura e sulla governance della monorepo, in particolare su confini tra moduli, regole sulle dipendenze e standard frontend condivisi. Rivedo anche gli RFC che coinvolgono il frontend condiviso.', + 'Amministro la repository di Tosca Cloud su GitHub e automatizzo le pipeline, anche su Azure. Con il platform architect lavoro sulla cache e sulle performance delle build, sulla struttura della CI e sui quality gate.', + 'Ho integrato OpenTelemetry nel frontend. Dopo il login Okta, gestisco l’integrazione nell’applicazione, inclusi JWT e interceptor HTTP. Quando un problema compare in più progetti, lo indago a livello di piattaforma.', + 'Valuto regolarmente strumenti AI, tra cui Claude e Cursor, e il modo in cui li usiamo. Quando lo stesso limite si ripresenta, aggiorno la documentazione, le regole o gli strumenti del repository.', ], }, { @@ -148,10 +163,10 @@ export const it: Copy = { context: 'Piattaforma e-commerce per piantare alberi', tags: ['React', 'Vue', 'GraphQL', 'Migrazione storefront'], paragraphs: [ - 'Sono entrato nel team frontend e poi ho guidato la parte frontend della migrazione dello storefront fuori dal monolite, lavorando con React, Vue e GraphQL.', - 'Ho ricostruito le fondamenta dello storefront mentre l’azienda cresceva da circa 20 a più di 150 persone.', - 'Ho costruito esperienze per campagne ad alto traffico, su una piattaforma che ha piantato milioni di alberi.', - 'Nell’ultimo anno ho costruito da zero il frontend degli strumenti back-office interni, lavorando a stretto contatto con il team backend e con le persone che li usavano per le operazioni forestali.', + 'Nel team frontend ho lavorato alla migrazione dello storefront fuori dal monolite, usando React, Vue e GraphQL.', + 'Ho contribuito a ricostruire lo storefront mentre l’azienda cresceva da circa 20 a più di 150 persone.', + 'Ho sviluppato le interfacce per campagne ad alto traffico. Mi confrontavo regolarmente con il team di design e con quello del design system per integrare le richieste e rispettare i mockup e gli standard condivisi.', + 'Negli ultimi due anni ho sviluppato da zero il frontend degli strumenti back-office interni. Ero l’unico frontend developer del progetto e lavoravo insieme al team backend e alle persone che usavano quegli strumenti per le operazioni forestali.', ], }, { @@ -162,25 +177,23 @@ export const it: Copy = { context: 'E-commerce di lusso', tags: ['ASP', 'jQuery', 'React', 'Webpack'], paragraphs: [ - 'Ho modernizzato una piattaforma e-commerce di lusso live senza un rewrite completo, portandola da ASP legacy e jQuery a React con una toolchain frontend basata su Webpack.', - 'Ho continuato a rilasciare funzionalità mentre la migrazione era in corso e il negozio di produzione restava online.', + 'Ho modernizzato un e-commerce di lusso mentre era in produzione. Ho sostituito gradualmente ASP e jQuery con React e una nuova configurazione Webpack.', + 'Durante la migrazione ho continuato a sviluppare nuove funzionalità senza fermare il negozio.', 'Ho lavorato sia sullo storefront sia sul CMS interno usato per gli ordini e per la gestione quotidiana del negozio.', - 'Ho sostituito gradualmente il frontend legacy mentre cambiavo l’architettura sottostante.', ], }, { years: '2007 · 2017', place: 'FIGLINE VALDARNO, IT', - title: 'Head of Web · Full-Stack Designer', + title: 'Head of Web · Designer & Developer', company: 'Easysystem', context: 'Agenzia web e servizi IT', tags: ['Design', 'Web', 'Backend', 'Infrastruttura'], paragraphs: [ - 'Sono entrato in Easysystem come designer e ho finito per occuparmi di quasi tutta la parte tecnica dei progetti: sviluppo web, sistemi backend e infine infrastruttura on-premise.', + 'Sono entrato come designer. Negli anni ho lavorato anche sullo sviluppo web, sui sistemi backend e sull’infrastruttura installata presso i clienti.', 'Ho disegnato da zero siti, interfacce, identità visive e materiali stampati per i progetti dei clienti.', 'Ho costruito e mantenuto applicazioni frontend e backend.', 'Ho installato server, reti e firewall direttamente nelle aziende dei clienti, comprese le manifatture del distretto toscano della pelle e della moda.', - 'Ho seguito i progetti dai primi concetti di design allo sviluppo e, in alcuni casi, fino all’infrastruttura su cui giravano.', ], }, ], @@ -190,7 +203,7 @@ export const it: Copy = { name: 'Lumina', kind: 'FANTASY MUD', description: - 'Un MUD fantasy costruito end to end, dal backend Node.js/MongoDB al client React e all’infrastruttura Cloudflare.', + 'Un MUD fantasy che ho sviluppato interamente: backend Node.js/MongoDB, client React e infrastruttura Cloudflare.', tags: ['Node.js', 'MongoDB', 'React', 'Cloudflare'], }, { @@ -203,9 +216,6 @@ export const it: Copy = { }, ], - workClosing: - 'Ho cominciato dal design, sono passato dal frontend, dal backend e dall’infrastruttura, e sono arrivato a lavorare sulla struttura condivisa da molti team. La scala è cambiata, l’abitudine no: capire come sta insieme il sistema e poi correggere il punto che continua a far tornare lo stesso problema.', - footer: { built: 'Scritto, disegnato e messo online da me.', }, @@ -213,9 +223,9 @@ export const it: Copy = { nowBadge: 'IN CORSO', contact: { - invite: 'Scrivimi quando vuoi, anche solo per parlare.', - inviteMore: - 'Una conversazione nuova non mi ha mai dato fastidio, e una proposta che mi incuriosisce ancora meno.', + invite: + 'Hey! Se ti va di parlare di piattaforme frontend, developer experience o semplicemente di scambiarci due idee, scrivimi.', + inviteMore: 'Non serve una proposta formale: anche un messaggio al volo va benissimo.', cv: 'CV (PDF)', github: 'GitHub', linkedin: 'LinkedIn', diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 92de1c0..2d19fd9 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -144,13 +144,6 @@ export type Copy = { projects: ProjectEntry[]; - /** - * HTML: the line that closes the career, after the four entries. It only - * works there, because it is about what the steps add up to, and a reader - * cannot be told that before being shown them. - */ - workClosing: string; - /** Small print at the very bottom. */ footer: { built: string; diff --git a/src/layouts/layout.astro b/src/layouts/layout.astro index 057dca1..3faeee5 100644 --- a/src/layouts/layout.astro +++ b/src/layouts/layout.astro @@ -33,7 +33,7 @@ const fontHref = - + {meta.title} diff --git a/tests/e2e/homepage.spec.ts b/tests/e2e/homepage.spec.ts index 4216e2b..42d408b 100644 --- a/tests/e2e/homepage.spec.ts +++ b/tests/e2e/homepage.spec.ts @@ -146,12 +146,6 @@ for (const locale of ['en', 'it'] as const) { await expect(card.locator('.duty')).toHaveCount(job.paragraphs.length); await expect(card.locator('.chip')).toHaveCount(job.tags.length); } - - // The closing line only means anything after the entries, so it has to be - // the last thing in the section, not a lead over it. - const closing = page.locator('.work .closing'); - await expect(closing).toHaveText(plain(copy.workClosing)); - expect(await closing.evaluate((el) => el === el.parentElement?.lastElementChild)).toBe(true); }); test('closes on an invitation, with the address behind the mail icon', async ({ page }) => { @@ -197,18 +191,16 @@ for (const locale of ['en', 'it'] as const) { }); /* - * The architecture is named once, as the thing he governs. In the work - * history that is evidence of scale. In the hero or in how-I-work it would - * read as the only thing he does, which is why it was cut the first time. - * The rule was never the word, it was the position. + * The hero now opens with the scale of the current platform instead of a + * general claim. The focus and work sections then add the technical detail. */ - test('keeps the architecture out of the hero and names it where it adds evidence', async ({ - page, - }) => { + test('opens with platform scale and adds architecture detail later', async ({ page }) => { await page.goto(home); const architecture = /micro-?frontend|monorepo/i; - await expect(page.locator('.hero').getByText(architecture)).toHaveCount(0); + await expect(page.locator('.hero')).toContainText('16'); + await expect(page.locator('.hero')).toContainText('12'); + await expect(page.locator('.hero').getByText(architecture)).toHaveCount(1); await expect(page.locator('.approach').getByText(architecture)).toHaveCount(0); await expect(page.locator('.focus').getByText(architecture).first()).toBeVisible(); await expect(page.locator('.work').getByText(architecture).first()).toBeVisible(); From 722e160a3f91378f88af4adf519e2774099840b5 Mon Sep 17 00:00:00 2001 From: Andrea Lisi Date: Fri, 21 Aug 2026 11:39:53 +0200 Subject: [PATCH 2/2] content: publish bilingual CV and final copy --- public/andrea-lisi-cv-it.pdf | Bin 0 -> 8353 bytes public/andrea-lisi-cv.pdf | Bin 10560 -> 8118 bytes src/components/contact.astro | 3 ++- src/components/hero.astro | 3 ++- src/i18n/en.ts | 4 ++-- src/i18n/it.ts | 4 ++-- tests/e2e/homepage.spec.ts | 5 +++-- 7 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 public/andrea-lisi-cv-it.pdf diff --git a/public/andrea-lisi-cv-it.pdf b/public/andrea-lisi-cv-it.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6d0f09fc39d89efca4522c888bde9993b8dd15fe GIT binary patch literal 8353 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 M`YoRys-tiJ1@IqlEC2ui literal 0 HcmV?d00001 diff --git a/public/andrea-lisi-cv.pdf b/public/andrea-lisi-cv.pdf index b2970acfe63c5862d595b1f30edd51fa406f339d..460204de1d0cf896eb5ebb38a21c8c959ca006c4 100644 GIT binary patch literal 8118 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 W#1=Ds|I{vqqFJsB2=dDK$NvHex%trm diff --git a/src/components/contact.astro b/src/components/contact.astro index decc08c..4146fa6 100644 --- a/src/components/contact.astro +++ b/src/components/contact.astro @@ -11,6 +11,7 @@ type Props = { const { locale } = Astro.props; const copy = getCopy(locale); const head = copy.sections.contact; +const cvFile = locale === 'it' ? 'andrea-lisi-cv-it.pdf' : 'andrea-lisi-cv.pdf'; ---

@@ -22,7 +23,7 @@ const head = copy.sections.contact;
- {hero.buttons.cv} + {hero.buttons.cv}