Skip to content

Commit 62580cd

Browse files
committed
feat: hero "la mesa de trabajo a la hora dorada"
- Rehacer el hero de la home como primera carta de presentación, con composición asimétrica sobre una rejilla de 12 columnas: titular fragmentado en tres voces y sangrías, y una ventana de código desfasada en diagonal. - Escribir el snippet en vivo (resaltado de sintaxis hecho en build, sin JS de librerías) y "compilar" al terminar: el sol tras la ventana se enciende, la luz de estado vira a turquesa y la salida reporta 200 OK. - Sembrar decoradores de mesa de trabajo (sello giratorio, stickers torcidos, coordenadas al margen, línea de horizonte) y microinteracciones con propósito con entrada escalonada; todo respeta prefers-reduced-motion. - Ampliar el hero en el esquema i18n y ambos idiomas (snippet, título segmentado, estados de compilación, salida y chips). - Actualizar CHANGELOG.md; elevar la versión a 0.10.0. Corrige el responsive del hero: se saca de u-grid y se acota con su propio contenedor, de modo que la ventana de código ya no desborda en móvil (scroll horizontal contenido dentro de la ventana); el titular en gradiente deja de recortar los descendentes.
1 parent 27f8a10 commit 62580cd

7 files changed

Lines changed: 961 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,15 @@ Todos los cambios notables en este proyecto serán documentados en este archivo
205205

206206
### Corregido
207207

208-
- **CI**: dependencia `libasound` ajustada para compatibilidad con la imagen reciente de Ubuntu en el runner de despliegue.
208+
- **CI**: dependencia `libasound` ajustada para compatibilidad con la imagen reciente de Ubuntu en el runner de despliegue.
209+
210+
## [0.10.0] - 2026-07-11
211+
212+
### Añadido
213+
214+
- **Hero de la home «la mesa de trabajo a la hora dorada»** (`src/components/pages/Home.astro` + `src/styles/home.css`): la primera cara del sitio, con composición asimétrica sobre una rejilla de 12 columnas. Titular fragmentado en tres voces y tres sangrías (mono → display en gradiente → remate con cursor que respira) y una ventana de código desfasada en diagonal donde un snippet TypeScript se escribe en vivo (resaltado de sintaxis hecho en build, sin JS de librerías) y, al terminar, «compila»: el sol tras la ventana se enciende, la luz de estado vira a turquesa y la salida reporta `200 OK`. Decoradores de mesa de trabajo — sello circular giratorio, stickers de código pegados torcidos, coordenadas de Cartagena al margen y línea de horizonte —, microinteracciones con propósito y entrada escalonada; todo respeta `prefers-reduced-motion`.
215+
- **`src/data/i18n.ts`** y **`src/data/locales/{es,en}.ts`**: el `hero` crece con el snippet de código, el título segmentado, los estados de compilación, la salida del build y los chips.
216+
217+
### Corregido
218+
219+
- **Responsive del hero**: se saca el hero de `u-grid` y se acota con su propio contenedor, de modo que la ventana de código ya no desborda en móvil (su scroll horizontal queda contenido dentro de la ventana). El titular en gradiente deja de recortar los descendentes (la «g» de «digital»).

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ctg-code",
3-
"version": "0.9.0",
3+
"version": "0.10.0",
44
"dependencies": {
55
"@astrojs/partytown": "^2.1.7",
66
"@fontsource-variable/inter": "^5.2.8",

src/components/pages/Home.astro

Lines changed: 197 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
* Los archivos de ruta (pages/es/index.astro, pages/en/index.astro) solo
66
* eligen el idioma; toda la estructura, textos (vía locales) y estilos viven
77
* aquí una sola vez.
8+
*
9+
* HERO — "compilar el atardecer": un snippet se escribe en vivo en la ventana
10+
* de código y, al terminar, el sol tras ella se enciende (data-compiled).
811
*/
912
import Layout from "../../layouts/Layout.astro";
1013
import Navbar from "../global/Navbar.astro";
@@ -20,20 +23,149 @@ interface Props {
2023
const { lang } = Astro.props;
2124
const t = locales[lang];
2225
const title = `CTG Code — ${t.hero.title}`;
26+
27+
/**
28+
* Mini-resaltador de sintaxis (build-time, cero JS en cliente).
29+
* El código "atardece": keywords coral, strings ámbar, números turquesa.
30+
*/
31+
type Token = { t: "kw" | "cls" | "fn" | "str" | "num" | "com" | "pl"; v: string };
32+
33+
const SPLIT =
34+
/("[^"]*"|\/\/.*$|\.\w+(?=\()|-?\d+\.\d+|-?\d+|\b(?:import|from|const|new|export|true|false)\b|\bCTGCode\b)/;
35+
36+
function tokenize(line: string): Token[] {
37+
return line
38+
.split(SPLIT)
39+
.filter((s) => s !== undefined && s !== "")
40+
.map((s): Token => {
41+
if (s.startsWith('"')) return { t: "str", v: s };
42+
if (s.startsWith("//")) return { t: "com", v: s };
43+
if (s.startsWith(".")) return { t: "fn", v: s };
44+
if (/^-?\d/.test(s)) return { t: "num", v: s };
45+
if (s === "CTGCode") return { t: "cls", v: s };
46+
if (/^(import|from|const|new|export|true|false)$/.test(s))
47+
return { t: "kw", v: s };
48+
return { t: "pl", v: s };
49+
});
50+
}
51+
52+
const codeLines = t.hero.code.map((line) => tokenize(line));
2353
---
2454

2555
<Layout title={title} description={t.hero.description} pageName="home">
2656
<Navbar lang={lang} />
2757

2858
<main>
29-
<section class="hero section u-grid" data-tone="night">
30-
<div class="hero-body">
31-
<p class="eyebrow">{t.hero.eyebrow}</p>
32-
<h1>{t.hero.title}</h1>
33-
<p class="hero-lead">{t.hero.description}</p>
34-
<div class="cluster">
35-
<CTA href="#contact">{t.nav.cta}</CTA>
59+
<section class="hero" data-tone="night">
60+
<div class="hero-stage">
61+
<!-- El mensaje: titular fragmentado en tres voces -->
62+
<div class="hero-copy">
63+
<p class="eyebrow hero-eyebrow">{t.hero.eyebrow}</p>
64+
<h1 class="hero-title">
65+
<span class="hero-t1">{t.hero.titlePre}</span>
66+
<span class="hero-t2 text-sunset">{t.hero.titleMark}</span>
67+
<span class="hero-t3"
68+
>{t.hero.titlePost}<span
69+
class="hero-title-caret"
70+
aria-hidden="true">_</span
71+
></span
72+
>
73+
</h1>
74+
<p class="hero-lead">{t.hero.description}</p>
75+
76+
<div class="hero-actions">
77+
<CTA href="#contact">{t.nav.cta}</CTA>
78+
<a class="hero-more" href="#services">{t.hero.secondary}</a>
79+
</div>
3680
</div>
81+
82+
<!-- Sticker suelto sobre la mesa -->
83+
<span class="hero-chip hero-chip--a" aria-hidden="true"
84+
>{t.hero.chipA}</span
85+
>
86+
87+
<!-- La fragua: el código que compila el atardecer (decorativo) -->
88+
<div class="hero-forge" aria-hidden="true">
89+
<span class="hero-sun">
90+
<svg viewBox="46 46 108 108" xmlns="http://www.w3.org/2000/svg">
91+
<g fill="currentColor">
92+
<path d="M100 50 L107 66 L93 66 Z" />
93+
<path d="M100 50 L107 66 L93 66 Z" transform="rotate(45 100 100)" />
94+
<path d="M100 50 L107 66 L93 66 Z" transform="rotate(90 100 100)" />
95+
<path d="M100 50 L107 66 L93 66 Z" transform="rotate(135 100 100)" />
96+
<path d="M100 50 L107 66 L93 66 Z" transform="rotate(180 100 100)" />
97+
<path d="M100 50 L107 66 L93 66 Z" transform="rotate(225 100 100)" />
98+
<path d="M100 50 L107 66 L93 66 Z" transform="rotate(270 100 100)" />
99+
<path d="M100 50 L107 66 L93 66 Z" transform="rotate(315 100 100)" />
100+
</g>
101+
<circle cx="100" cy="100" r="22" fill="none" stroke="currentColor" stroke-width="8" />
102+
</svg>
103+
</span>
104+
105+
<div class="hero-window">
106+
<div class="hero-window-head">
107+
<span><span class="hero-braces">&#123; &#125;</span> {t.hero.file}</span>
108+
<span class="hero-status">
109+
<span class="hero-dot"></span>
110+
<span class="hero-status-building">{t.hero.statusBuilding}</span>
111+
<span class="hero-status-ready">{t.hero.statusReady}</span>
112+
</span>
113+
</div>
114+
115+
<pre class="hero-code" data-hero-code><code>{codeLines.map((tokens) => (
116+
<span class="hero-line">{tokens.map((tok) => (
117+
<span class={`tk-${tok.t}`}>{tok.v}</span>
118+
))}</span>
119+
))}</code></pre>
120+
121+
<span class="hero-output">{t.hero.output}</span>
122+
</div>
123+
124+
<!-- Sticker pegado al marco de la ventana -->
125+
<span class="hero-chip hero-chip--b">{t.hero.chipB}</span>
126+
</div>
127+
128+
<!-- El sello del estudio: gira sin prisa, se detiene a saludar -->
129+
<div class="hero-stamp" aria-hidden="true">
130+
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
131+
<defs>
132+
<path
133+
id="hero-stamp-arc"
134+
d="M50,50 m-38,0 a38,38 0 1,1 76,0 a38,38 0 1,1 -76,0"
135+
></path>
136+
</defs>
137+
<circle
138+
class="hero-stamp-ring"
139+
cx="50"
140+
cy="50"
141+
r="46"
142+
fill="none"
143+
stroke-width="1"></circle>
144+
<text class="hero-stamp-text">
145+
<textPath href="#hero-stamp-arc">
146+
CTG CODE · CARTAGENA · 10.42° N · 75.55° W ·
147+
</textPath>
148+
</text>
149+
<circle
150+
class="hero-stamp-sun"
151+
cx="50"
152+
cy="50"
153+
r="9"
154+
fill="none"
155+
stroke-width="3.5"></circle>
156+
<circle class="hero-stamp-core" cx="50" cy="50" r="2.4"></circle>
157+
</svg>
158+
</div>
159+
160+
<p class="hero-scroll">
161+
{t.hero.scroll}
162+
<span class="hero-scroll-arrow" aria-hidden="true">↓</span>
163+
</p>
164+
165+
<!-- Coordenadas al margen, como en una carta náutica -->
166+
<span class="hero-coords" aria-hidden="true"
167+
>10.4236° N — 75.5518° W</span
168+
>
37169
</div>
38170
</section>
39171

@@ -54,3 +186,61 @@ const title = `CTG Code — ${t.hero.title}`;
54186

55187
<Footer lang={lang} />
56188
</Layout>
189+
190+
<script>
191+
/**
192+
* El código se escribe en vivo, con ritmo humano (pausas al final de línea),
193+
* y al terminar la sección "compila": el sol se enciende, la luz de estado
194+
* vira a turquesa y la salida reporta 200 OK. Con prefers-reduced-motion no
195+
* se teclea nada: el hero aparece ya compilado.
196+
*/
197+
const hero = document.querySelector<HTMLElement>(".hero");
198+
const code = hero?.querySelector<HTMLElement>("[data-hero-code] code");
199+
200+
if (hero && code) {
201+
const compile = () => hero.setAttribute("data-compiled", "");
202+
203+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
204+
compile();
205+
} else {
206+
// Recolecta los nodos de texto del código y vacíalos para teclearlos.
207+
const walker = document.createTreeWalker(code, NodeFilter.SHOW_TEXT);
208+
const nodes: Text[] = [];
209+
while (walker.nextNode()) nodes.push(walker.currentNode as Text);
210+
const texts = nodes.map((n) => n.nodeValue ?? "");
211+
nodes.forEach((n) => (n.nodeValue = ""));
212+
213+
const caret = document.createElement("span");
214+
caret.className = "hero-caret";
215+
caret.setAttribute("aria-hidden", "true");
216+
217+
let i = 0;
218+
let j = 0;
219+
220+
const step = () => {
221+
if (i >= nodes.length) {
222+
caret.remove();
223+
compile();
224+
return;
225+
}
226+
227+
const node = nodes[i];
228+
const text = texts[i];
229+
j++;
230+
node.nodeValue = text.slice(0, j);
231+
node.parentNode?.insertBefore(caret, node.nextSibling);
232+
233+
let delay = 12 + Math.random() * 26;
234+
if (j >= text.length) {
235+
// Pausa breve al cerrar cada tramo: el ritmo de quien piensa.
236+
if (text.length > 1) delay += 90;
237+
i++;
238+
j = 0;
239+
}
240+
setTimeout(step, delay);
241+
};
242+
243+
setTimeout(step, 600);
244+
}
245+
}
246+
</script>

src/data/i18n.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,19 @@ export type LocaleSchema = {
1313
hero: {
1414
eyebrow: string;
1515
title: string;
16+
titlePre: string;
17+
titleMark: string;
18+
titlePost: string;
1619
description: string;
20+
file: string;
21+
code: string[];
22+
statusBuilding: string;
23+
statusReady: string;
24+
output: string;
25+
secondary: string;
26+
scroll: string;
27+
chipA: string;
28+
chipB: string;
1729
};
1830
notFound: {
1931
status: string;

src/data/locales/en.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,31 @@ const en: LocaleSchema = {
1212
hero: {
1313
eyebrow: 'Software studio · Cartagena',
1414
title: 'Building the digital future from the Caribbean',
15+
titlePre: 'Building ',
16+
titleMark: 'the digital future ',
17+
titlePost: 'from the Caribbean',
1518
description: 'Custom software development and high-performance web solutions, from Cartagena to the world.',
19+
file: 'future.ts',
20+
code: [
21+
'import { sun, sea } from "@caribbean/cartagena";',
22+
'',
23+
'const studio = new CTGCode({',
24+
' origin: [10.4236, -75.5518],',
25+
' goldenHour: true,',
26+
'});',
27+
'',
28+
'export const future = studio',
29+
' .design({ handcrafted: true })',
30+
' .build({ tailorMade: true })',
31+
' .deploy("→ to the world");',
32+
],
33+
statusBuilding: 'compiling…',
34+
statusReady: 'live',
35+
output: '→ 200 OK · future deployed in 47ms',
36+
secondary: 'see services',
37+
scroll: '$ scroll --seaward',
38+
chipA: 'goldenHour: true',
39+
chipB: '// handcrafted, tailor-made',
1640
},
1741
notFound: {
1842
status: 'error 404 · route not found',

src/data/locales/es.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,31 @@ const es: LocaleSchema = {
1212
hero: {
1313
eyebrow: 'Estudio de software · Cartagena',
1414
title: 'Construyendo el futuro digital desde el Caribe',
15+
titlePre: 'Construyendo ',
16+
titleMark: 'el futuro digital ',
17+
titlePost: 'desde el Caribe',
1518
description: 'Desarrollo de software a la medida y soluciones web de alto rendimiento desde Cartagena para el mundo.',
19+
file: 'futuro.ts',
20+
code: [
21+
'import { sol, mar } from "@caribe/cartagena";',
22+
'',
23+
'const estudio = new CTGCode({',
24+
' origen: [10.4236, -75.5518],',
25+
' horaDorada: true,',
26+
'});',
27+
'',
28+
'export const futuro = estudio',
29+
' .diseñar({ aMano: true })',
30+
' .construir({ aMedida: true })',
31+
' .desplegar("→ para el mundo");',
32+
],
33+
statusBuilding: 'compilando…',
34+
statusReady: 'en línea',
35+
output: '→ 200 OK · futuro desplegado en 47ms',
36+
secondary: 'ver servicios',
37+
scroll: '$ scroll --hacia-el-mar',
38+
chipA: 'horaDorada: true',
39+
chipB: '// a mano, a medida',
1640
},
1741
notFound: {
1842
status: 'error 404 · ruta no encontrada',

0 commit comments

Comments
 (0)