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
4 changes: 2 additions & 2 deletions scripts/verify-prerender.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ const expectedRoutes = [
lang: "es",
title: "Servicios de desarrollo web y software a medida | Rodrigo Opalo",
description:
"Desarrollo sitios web profesionales, sistemas a medida y automatizaciones para empresas. Trabajo directo conmigo, en español e inglés, desde South Florida y en remoto para Estados Unidos y Latinoamérica.",
"Desarrollo sitios web profesionales, sistemas a medida y automatizaciones para empresas. Trabajo directo conmigo, en español e inglés, desde el sur de Florida y en remoto para Estados Unidos y Latinoamérica.",
content: "Sitios web, sistemas y automatización para empresas.",
metadataHash:
"270c8a9a1de7606d8bf4e9d6c09cda7fbb8757b1c42174417cbcc56f425fa21d",
"c028ef27f8fadbcb764f357884105455a8c3fa625c78f14ba2f2b70aa4d9d71c",
noJavaScriptMarkers: 1,
},
{
Expand Down
29 changes: 29 additions & 0 deletions src/Components/services/EditorialNote.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { ReactNode } from "react";

type EditorialNoteTone = "dark" | "light";

interface EditorialNoteProps {
children: ReactNode;
className?: string;
tone?: EditorialNoteTone;
}

const TONE_CLASSNAME: Record<EditorialNoteTone, string> = {
dark: "border-primary/60 text-gray-300/90",
light: "border-primary-on-light/70 text-gray-800",
};

export default function EditorialNote({
children,
className = "",
tone = "dark",
}: EditorialNoteProps) {
return (
<p
data-editorial-note
className={`border-l pl-5 text-base leading-relaxed ${TONE_CLASSNAME[tone]} ${className}`}
>
{children}
</p>
);
}
47 changes: 47 additions & 0 deletions src/Components/services/ServiceDirectoryItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Link } from "react-router-dom";

interface ServiceDirectoryItemProps {
bordered?: boolean;
ctaLabel: string;
description: string;
title: string;
to: string;
}

export default function ServiceDirectoryItem({
bordered = false,
ctaLabel,
description,
title,
to,
}: ServiceDirectoryItemProps) {
return (
<Link
to={to}
data-interaction-contract="service-directory-row"
data-service-directory-item
className={`group grid min-h-[44px] grid-cols-[minmax(0,1fr)_auto] gap-6 py-7 text-white no-underline transition-colors active:bg-white/[0.03] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary sm:py-8 ${
bordered ? "border-t border-white/15" : ""
}`}
>
<div className="flex flex-col">
<h3 className="text-xl font-semibold tracking-tight transition-colors group-hover:text-primary group-active:text-primary group-focus-visible:text-primary sm:text-2xl">
{title}
</h3>
<p className="mt-3 max-w-[40rem] text-base leading-relaxed text-gray-300/90">
{description}
</p>
<span className="mt-5 text-xs font-semibold uppercase tracking-[0.12em] text-primary transition-colors group-hover:text-primary group-active:text-primary group-focus-visible:text-primary">
{ctaLabel}
</span>
</div>
<span
aria-hidden="true"
data-service-directory-arrow
className="mt-1 text-xl leading-none text-gray-500 transition-[color,transform] duration-200 group-hover:translate-x-0.5 group-hover:text-primary group-active:translate-x-0.5 group-active:text-primary group-focus-visible:translate-x-0.5 group-focus-visible:text-primary motion-reduce:transform-none motion-reduce:transition-none"
>
</span>
</Link>
);
}
2 changes: 1 addition & 1 deletion src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ export const en = {
text: "In development: website and e-commerce with subscriptions, an admin panel, and inventory management.",
},
],
note: "Each project had a different scope; the portfolio details the role and technology behind each one.",
note: "Each project had a different scope. In the portfolio, you can see the role and technologies used in each one.",
portfolioLink: "See these projects in the portfolio",
},
method: {
Expand Down
10 changes: 5 additions & 5 deletions src/i18n/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export const es = {
seo: {
title: "Servicios de desarrollo web y software a medida | Rodrigo Opalo",
description:
"Desarrollo sitios web profesionales, sistemas a medida y automatizaciones para empresas. Trabajo directo conmigo, en español e inglés, desde South Florida y en remoto para Estados Unidos y Latinoamérica.",
"Desarrollo sitios web profesionales, sistemas a medida y automatizaciones para empresas. Trabajo directo conmigo, en español e inglés, desde el sur de Florida y en remoto para Estados Unidos y Latinoamérica.",
},
header: {
eyebrow: "Servicios",
Expand Down Expand Up @@ -173,7 +173,7 @@ export const es = {
},
coverage: {
title: "Dónde trabajo",
text: "Estoy basado en South Florida y trabajo con empresas de Miami y del resto de Estados Unidos, y en remoto con Latinoamérica. La comunicación puede ser en español o en inglés durante todo el proyecto.",
text: "Trabajo desde el sur de Florida con empresas de Miami y del resto de Estados Unidos, y también de forma remota con clientes de Latinoamérica. La comunicación puede ser en español o en inglés durante todo el proyecto.",
},
proof: {
title: "Trabajo real, no promesas",
Expand Down Expand Up @@ -260,7 +260,7 @@ export const es = {
text: "Proyecto en desarrollo: sitio web y e-commerce con suscripciones, panel administrativo y gestión de stock.",
},
],
note: "Cada proyecto tuvo un alcance distinto; en el portfolio está el detalle de rol y tecnología de cada uno.",
note: "Cada proyecto tuvo un alcance distinto. En el portfolio podés ver el rol y las tecnologías utilizadas en cada uno.",
portfolioLink: "Ver estos proyectos en el portfolio",
},
method: {
Expand All @@ -274,7 +274,7 @@ export const es = {
},
coverage: {
title: "Miami y trabajo remoto",
text: "Trabajo desde South Florida con empresas de Miami y del resto de Estados Unidos, y en remoto con Latinoamérica, en español e inglés.",
text: "Trabajo desde el sur de Florida con empresas de Miami y del resto de Estados Unidos, y también de forma remota con clientes de Latinoamérica. La comunicación puede ser en español o en inglés durante todo el proyecto.",
},
cta: {
title: "¿Tu empresa necesita un sitio que trabaje?",
Expand Down Expand Up @@ -364,7 +364,7 @@ export const es = {
},
coverage: {
title: "Dónde y cómo trabajo",
text: "Desde South Florida, con empresas de Miami y de Estados Unidos, y en remoto con Latinoamérica. El trabajo es directo conmigo, en español o en inglés, durante todo el proyecto.",
text: "Trabajo desde el sur de Florida con empresas de Miami y del resto de Estados Unidos, y también de forma remota con clientes de Latinoamérica. La comunicación puede ser en español o en inglés durante todo el proyecto.",
},
cta: {
title: "¿Tu operación necesita su propio sistema?",
Expand Down
15 changes: 11 additions & 4 deletions src/pages/BusinessWebsitesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Link } from "react-router-dom";
import { useLanguage } from "../i18n/useLanguage";
import translations from "../i18n";
import { getLocalizedPath } from "../routes/siteRoutes";
import EditorialNote from "../Components/services/EditorialNote";

export default function BusinessWebsitesPage() {
const { language } = useLanguage();
Expand All @@ -24,7 +25,10 @@ export default function BusinessWebsitesPage() {
<p className="mt-10 text-xs font-semibold uppercase leading-snug tracking-[0.14em] text-primary sm:text-sm">
{page.header.eyebrow}
</p>
<h1 className="mt-6 max-w-[52rem] text-4xl font-semibold leading-tight tracking-tight text-white sm:text-5xl">
<h1
data-hero-width="business-websites"
className="mt-6 max-w-[52rem] text-4xl font-semibold leading-tight tracking-tight text-white sm:text-5xl lg:max-w-[64rem]"
>
{page.header.title}
</h1>
<p className="mt-5 max-w-[44rem] text-lg leading-relaxed text-gray-200 sm:text-xl">
Expand Down Expand Up @@ -89,9 +93,12 @@ export default function BusinessWebsitesPage() {
</div>
))}
</div>
<p className="mt-8 max-w-[44rem] text-base leading-relaxed text-gray-700">
<EditorialNote
tone="light"
className="mt-8 max-w-[44rem] lg:max-w-none"
>
{page.cases.note}
</p>
</EditorialNote>
<Link
to={getLocalizedPath("portfolio", language)}
className="mt-5 inline-flex min-h-[44px] items-center text-xs font-semibold uppercase tracking-[0.12em] text-primary-on-light no-underline transition-colors hover:text-primary-on-light-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-on-light focus-visible:ring-offset-4 focus-visible:ring-offset-neutral"
Expand Down Expand Up @@ -145,7 +152,7 @@ export default function BusinessWebsitesPage() {
className="border-b border-white/10 px-4 py-12 sm:px-6 sm:py-24"
>
<div className="mx-auto max-w-6xl">
<div className="max-w-[48rem]">
<div className="max-w-[48rem] lg:max-w-[64rem]">
<h2
id="business-websites-coverage"
className="text-3xl font-semibold leading-tight tracking-tight text-white sm:text-4xl"
Expand Down
4 changes: 2 additions & 2 deletions src/pages/CustomSoftwarePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export default function CustomSoftwarePage() {
</section>

<div className="border-b border-white/10 px-4 py-12 sm:px-6 sm:py-24">
<div className="mx-auto grid max-w-6xl gap-10 lg:grid-cols-2 lg:gap-20">
<div className="mx-auto grid max-w-6xl gap-10 lg:max-w-7xl lg:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)] lg:gap-20">
<section
aria-labelledby="custom-software-crosslink"
className="border-t border-white/15 pt-7"
Expand Down Expand Up @@ -146,7 +146,7 @@ export default function CustomSoftwarePage() {
>
{page.coverage.title}
</h2>
<p className="mt-5 max-w-[36rem] text-base leading-relaxed text-gray-300/90 sm:text-lg">
<p className="mt-5 max-w-[36rem] text-base leading-relaxed text-gray-300/90 sm:text-lg lg:max-w-none">
{page.coverage.text}
</p>
</section>
Expand Down
87 changes: 24 additions & 63 deletions src/pages/ServicesHubPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import { Link } from "react-router-dom";
import { useLanguage } from "../i18n/useLanguage";
import translations from "../i18n";
import { getLocalizedPath } from "../routes/siteRoutes";

const SERVICE_ROW_CLASSNAME =
"group grid min-h-[44px] grid-cols-[minmax(0,1fr)_auto] gap-6 py-7 text-white no-underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary sm:py-8";
import EditorialNote from "../Components/services/EditorialNote";
import ServiceDirectoryItem from "../Components/services/ServiceDirectoryItem";

export default function ServicesHubPage() {
const { language } = useLanguage();
Expand Down Expand Up @@ -53,9 +52,9 @@ export default function ServicesHubPage() {
</div>
))}
</div>
<p className="mt-8 max-w-[44rem] border-l border-primary/60 pl-5 text-base leading-relaxed text-gray-300/90">
<EditorialNote className="mt-8 max-w-[44rem] lg:max-w-[64rem]">
{page.choose.note}
</p>
</EditorialNote>
</div>
</section>

Expand All @@ -71,64 +70,26 @@ export default function ServicesHubPage() {
{page.directory.title}
</h2>
<div className="mt-8 border-y border-white/15 sm:mt-12">
<Link
<ServiceDirectoryItem
to={getLocalizedPath("business-websites", language)}
className={SERVICE_ROW_CLASSNAME}
>
<div className="flex flex-col">
<h3 className="text-xl font-semibold tracking-tight transition-colors group-hover:text-primary group-focus-visible:text-primary sm:text-2xl">
{page.directory.web.title}
</h3>
<p className="mt-3 max-w-[40rem] text-base leading-relaxed text-gray-300/90">
{page.directory.web.text}
</p>
<span className="mt-5 text-xs font-semibold uppercase tracking-[0.12em] text-primary">
{page.directory.web.linkLabel}
</span>
</div>
<span
aria-hidden="true"
className="mt-1 text-xl leading-none text-gray-500 transition-[color,transform] duration-200 group-hover:translate-x-0.5 group-hover:text-primary group-focus-visible:translate-x-0.5 group-focus-visible:text-primary motion-reduce:transform-none motion-reduce:transition-none"
>
</span>
</Link>
<Link
title={page.directory.web.title}
description={page.directory.web.text}
ctaLabel={page.directory.web.linkLabel}
/>
<ServiceDirectoryItem
bordered
to={getLocalizedPath("custom-software", language)}
className={`${SERVICE_ROW_CLASSNAME} border-t border-white/15`}
>
<div className="flex flex-col">
<h3 className="text-xl font-semibold tracking-tight transition-colors group-hover:text-primary group-focus-visible:text-primary sm:text-2xl">
{page.directory.systems.title}
</h3>
<p className="mt-3 max-w-[40rem] text-base leading-relaxed text-gray-300/90">
{page.directory.systems.text}
</p>
<span className="mt-5 text-xs font-semibold uppercase tracking-[0.12em] text-primary">
{page.directory.systems.linkLabel}
</span>
</div>
<span
aria-hidden="true"
className="mt-1 text-xl leading-none text-gray-500 transition-[color,transform] duration-200 group-hover:translate-x-0.5 group-hover:text-primary group-focus-visible:translate-x-0.5 group-focus-visible:text-primary motion-reduce:transform-none motion-reduce:transition-none"
>
</span>
</Link>
<div className="border-t border-white/15 py-7 sm:py-8">
<h3 className="text-xl font-semibold tracking-tight text-white sm:text-2xl">
{page.directory.automation.title}
</h3>
<p className="mt-3 max-w-[40rem] text-base leading-relaxed text-gray-300/90">
{page.directory.automation.text}
</p>
<Link
to={`${homePath}#contacto`}
className="mt-5 inline-flex min-h-[44px] items-center text-xs font-semibold uppercase tracking-[0.12em] text-primary no-underline transition-colors hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-4 focus-visible:ring-offset-black"
>
{page.directory.automation.linkLabel}
</Link>
</div>
title={page.directory.systems.title}
description={page.directory.systems.text}
ctaLabel={page.directory.systems.linkLabel}
/>
<ServiceDirectoryItem
bordered
to={`${homePath}#contacto`}
title={page.directory.automation.title}
description={page.directory.automation.text}
ctaLabel={page.directory.automation.linkLabel}
/>
</div>
</div>
</section>
Expand Down Expand Up @@ -161,7 +122,7 @@ export default function ServicesHubPage() {
</section>

<div className="border-b border-white/10 px-4 py-12 sm:px-6 sm:py-24">
<div className="mx-auto grid max-w-6xl gap-10 lg:grid-cols-2 lg:gap-20">
<div className="mx-auto grid max-w-6xl gap-10 lg:max-w-7xl lg:grid-cols-[minmax(0,1.15fr)_minmax(0,0.85fr)] lg:gap-20">
<section
aria-labelledby="services-coverage"
className="border-t border-white/15 pt-7"
Expand All @@ -172,7 +133,7 @@ export default function ServicesHubPage() {
>
{page.coverage.title}
</h2>
<p className="mt-5 max-w-[36rem] text-base leading-relaxed text-gray-300/90 sm:text-lg">
<p className="mt-5 max-w-[36rem] text-base leading-relaxed text-gray-300/90 sm:text-lg lg:max-w-none">
{page.coverage.text}
</p>
</section>
Expand Down
Loading
Loading