Skip to content

Commit 825b47d

Browse files
jmacotclaude
andcommitted
Enhance landing page: SVG icons, search, filter animations, accessibility
- Add Open Graph + Twitter Card meta tags for social sharing - Add preconnect hints for Google Fonts performance - Add dynamic theme-color meta tag (adapts to dark/light mode) - Add prefers-reduced-motion media query for accessibility - Add aria-label on theme toggle with dynamic updates - Hide header glows on mobile for cleaner rendering - Replace all emoji card icons with monochrome inline SVGs - Add count badges to filter buttons (e.g. TODOS 8) - Add smooth fade animation when filtering cards - Add text search input with accent-insensitive matching - Remove Deformidades Coronales card (superseded by Knee Align) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 76b936e commit 825b47d

1 file changed

Lines changed: 151 additions & 32 deletions

File tree

index.html

Lines changed: 151 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@
77
<link rel="icon" type="image/svg+xml" href="icon.svg" />
88
<link rel="apple-touch-icon" href="icon.svg" />
99
<meta name="description" content="Herramientas clínicas para Cirugía Ortopédica y Traumatología" />
10+
<meta name="theme-color" id="meta-theme" content="#0f172a" />
11+
12+
<!-- Open Graph -->
13+
<meta property="og:title" content="Herramientas · jmacot" />
14+
<meta property="og:description" content="Aplicaciones web de uso clínico y administrativo para Cirugía Ortopédica y Traumatología" />
15+
<meta property="og:type" content="website" />
16+
<meta property="og:url" content="https://jmacot.github.io/" />
17+
<meta property="og:image" content="https://jmacot.github.io/icon.svg" />
18+
<meta name="twitter:card" content="summary" />
19+
20+
<!-- Fonts -->
21+
<link rel="preconnect" href="https://fonts.googleapis.com" />
22+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
1023
<link href="https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@400;500&family=DM+Sans:wght@300;400;500&display=swap" rel="stylesheet" />
1124
<style>
1225
:root {
@@ -185,6 +198,44 @@
185198
color: white;
186199
}
187200

201+
.filter-count {
202+
font-size: 9px;
203+
font-weight: 600;
204+
opacity: 0.6;
205+
margin-left: 2px;
206+
}
207+
208+
/* ── SEARCH ── */
209+
.search-wrap {
210+
position: relative;
211+
margin-bottom: 24px;
212+
}
213+
.search-icon {
214+
position: absolute;
215+
left: 16px; top: 50%;
216+
transform: translateY(-50%);
217+
color: var(--ink-muted);
218+
pointer-events: none;
219+
}
220+
#buscar {
221+
width: 100%;
222+
padding: 12px 16px 12px 44px;
223+
font-family: 'DM Sans', sans-serif;
224+
font-size: 14px;
225+
color: var(--ink);
226+
background: var(--surface);
227+
border: 1.5px solid var(--border);
228+
border-radius: 999px;
229+
outline: none;
230+
box-shadow: var(--shadow-sm);
231+
transition: border-color 0.2s, box-shadow 0.2s;
232+
}
233+
#buscar:focus {
234+
border-color: var(--ink-muted);
235+
box-shadow: 0 0 0 3px rgb(15 23 42 / 0.06);
236+
}
237+
#buscar::placeholder { color: var(--ink-muted); opacity: 0.6; }
238+
188239
.filter-btn[data-tag="todos"].active {
189240
background: var(--ink);
190241
border-color: var(--ink);
@@ -244,6 +295,16 @@
244295
display: none;
245296
}
246297

298+
.card.fading-out {
299+
opacity: 0;
300+
transform: translateY(8px) scale(0.97);
301+
}
302+
303+
.card.fading-in {
304+
opacity: 0;
305+
transform: translateY(8px);
306+
}
307+
247308
.card:hover {
248309
transform: translateY(-6px);
249310
box-shadow: var(--shadow-hover);
@@ -266,8 +327,8 @@
266327
display: flex;
267328
align-items: center;
268329
justify-content: center;
269-
font-size: 22px;
270330
}
331+
.card-icon svg { width: 22px; height: 22px; flex-shrink: 0; }
271332

272333
.card-meta {
273334
display: flex;
@@ -474,17 +535,25 @@
474535

475536
@media (max-width: 600px) {
476537
header { padding: 40px 24px 36px; }
538+
header::before, header::after { display: none; }
477539
main { padding: 40px 20px 60px; }
478540
.grid { grid-template-columns: 1fr; }
479541
.filters { gap: 6px; }
480542
.filter-btn { font-size: 10px; padding: 6px 12px; }
481543
.theme-toggle { width: 34px; height: 34px; font-size: 1rem; top: 10px; right: 10px; }
482544
}
545+
546+
@media (prefers-reduced-motion: reduce) {
547+
*, *::before, *::after {
548+
animation-duration: 0.01ms !important;
549+
transition-duration: 0.01ms !important;
550+
}
551+
}
483552
</style>
484553
</head>
485554
<body>
486555
<header>
487-
<button class="theme-toggle" id="btn-theme" title="Cambiar tema"></button>
556+
<button class="theme-toggle" id="btn-theme" title="Cambiar tema" aria-label="Cambiar a modo oscuro"></button>
488557
<div class="header-label">Herramientas clínicas</div>
489558
<h1>Herramientas<br><em>para Cirugía Ortopédica y Traumatología</em></h1>
490559
<p>Aplicaciones web de uso clínico y administrativo para cirugía ortopédica.</p>
@@ -507,12 +576,17 @@ <h1>Herramientas<br><em>para Cirugía Ortopédica y Traumatología</em></h1>
507576
<button class="filter-btn" data-tag="administracion" onclick="filtrar('administracion')">Administración</button>
508577
</div>
509578

579+
<div class="search-wrap">
580+
<svg class="search-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
581+
<input type="search" id="buscar" placeholder="Buscar herramienta..." autocomplete="off" />
582+
</div>
583+
510584
<div class="grid">
511585

512586
<!-- PLANTILLAS QUIRÚRGICAS -->
513587
<a class="card" data-tag="quirofano" href="https://jmacot.github.io/plantillas-qx" target="_blank" style="animation-delay: 0s">
514588
<div class="card-meta">
515-
<div class="card-icon">📋</div>
589+
<div class="card-icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--c-quirofano)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/></svg></div>
516590
<div class="card-arrow">
517591
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
518592
</div>
@@ -527,7 +601,7 @@ <h2>Plantillas Quirúrgicas COT</h2>
527601
<!-- PROTOCOLOS REHABILITACION -->
528602
<a class="card" data-tag="pacientes" href="https://jmacot.github.io/rehabilitacion-cot" target="_blank" style="animation-delay: 0.05s">
529603
<div class="card-meta">
530-
<div class="card-icon" style="--card-icon-bg: var(--c-pacientes-light)">
604+
<div class="card-icon">
531605
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--c-pacientes)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg>
532606
</div>
533607
<div class="card-arrow">
@@ -544,7 +618,7 @@ <h2>Protocolos y Ejercicios</h2>
544618
<!-- KNEE ALIGN -->
545619
<a class="card" data-tag="planificacion" href="https://jmacot.github.io/knee-align" target="_blank" style="animation-delay: 0.1s">
546620
<div class="card-meta">
547-
<div class="card-icon">📐</div>
621+
<div class="card-icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--c-planificacion)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg></div>
548622
<div class="card-arrow">
549623
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
550624
</div>
@@ -559,7 +633,7 @@ <h2>Knee Align</h2>
559633
<!-- PLANNING COT -->
560634
<a class="card" data-tag="administracion" href="https://jmacot.github.io/planning-cot" target="_blank" style="animation-delay: 0.15s">
561635
<div class="card-meta">
562-
<div class="card-icon">📊</div>
636+
<div class="card-icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--c-administracion)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 16V12"/><path d="M12 16V8"/><path d="M16 16v-2"/></svg></div>
563637
<div class="card-arrow">
564638
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
565639
</div>
@@ -574,7 +648,7 @@ <h2>Analizador de Planning</h2>
574648
<!-- MATERIAL EXTERNO -->
575649
<a class="card" data-tag="documentacion" href="https://jmacot.github.io/Material-Externo" target="_blank" style="animation-delay: 0.2s">
576650
<div class="card-meta">
577-
<div class="card-icon">🔩</div>
651+
<div class="card-icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--c-documentacion)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg></div>
578652
<div class="card-arrow">
579653
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
580654
</div>
@@ -589,7 +663,7 @@ <h2>Solicitud de Material Externo</h2>
589663
<!-- CALCULADORA GUARDIAS -->
590664
<a class="card" data-tag="administracion" href="https://jmacot.github.io/calculadora-guardias" target="_blank" style="animation-delay: 0.25s">
591665
<div class="card-meta">
592-
<div class="card-icon">🏥</div>
666+
<div class="card-icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--c-administracion)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21h18"/><path d="M5 21V7l8-4v18"/><path d="M19 21V11l-6-4"/><path d="M9 9h1"/><path d="M9 13h1"/><path d="M9 17h1"/></svg></div>
593667
<div class="card-arrow">
594668
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
595669
</div>
@@ -604,7 +678,7 @@ <h2>Calculadora de Guardias</h2>
604678
<!-- CONSENTIMIENTOS INFORMADOS -->
605679
<a class="card" data-tag="documentacion" href="https://jmacot.github.io/consentimientos" target="_blank" style="animation-delay: 0.3s">
606680
<div class="card-meta">
607-
<div class="card-icon">📝</div>
681+
<div class="card-icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--c-documentacion)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg></div>
608682
<div class="card-arrow">
609683
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
610684
</div>
@@ -619,7 +693,7 @@ <h2>Consentimientos Informados</h2>
619693
<!-- CPAK PLANNER -->
620694
<a class="card" data-tag="planificacion" href="https://jmacot.github.io/CPAK" target="_blank" style="animation-delay: 0.35s">
621695
<div class="card-meta">
622-
<div class="card-icon">🦴</div>
696+
<div class="card-icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--c-planificacion)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg></div>
623697
<div class="card-arrow">
624698
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
625699
</div>
@@ -631,25 +705,10 @@ <h2>CPAK Planner</h2>
631705
</div>
632706
</a>
633707

634-
<!-- DEFORMIDADES CORONALES -->
635-
<a class="card" data-tag="planificacion" href="https://jmacot.github.io/deformidad-coronal" target="_blank" style="animation-delay: 0.35s">
636-
<div class="card-meta">
637-
<div class="card-icon">📐</div>
638-
<div class="card-arrow">
639-
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M14 5l7 7m0 0l-7 7m7-7H3"/></svg>
640-
</div>
641-
</div>
642-
<div class="card-content">
643-
<div class="card-tag">Planificación</div>
644-
<h2>Deformidades Coronales</h2>
645-
<p>Analisis de alineacion coronal del miembro inferior con el algoritmo AKUMA. Calcula componentes de deformidad, diagnostico diferencial (HTO/DFO/DLO/UKA) y planificacion de correccion.</p>
646-
</div>
647-
</a>
648-
649708
<!-- PLACEHOLDER -->
650709
<div class="card placeholder" data-tag="none" style="animation-delay: 0.45s">
651710
<div class="card-meta">
652-
<div class="card-icon"></div>
711+
<div class="card-icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></div>
653712
</div>
654713
<div class="card-content">
655714
<div class="card-tag">En proceso</div>
@@ -669,32 +728,92 @@ <h2>Próximamente</h2>
669728
// ─────────────────────────────────────────────
670729
// FILTROS
671730
// ─────────────────────────────────────────────
731+
const allCards = document.querySelectorAll('.card:not(.placeholder)');
732+
733+
let filterTimer;
672734
function filtrar(tag) {
735+
clearTimeout(filterTimer);
736+
document.getElementById('buscar').value = '';
673737
document.querySelectorAll('.filter-btn').forEach(btn => {
674738
btn.classList.toggle('active', btn.dataset.tag === tag);
675739
});
676-
document.querySelectorAll('.card').forEach(card => {
677-
if (tag === 'todos') {
678-
card.classList.remove('hidden');
679-
} else {
680-
const cardTag = card.dataset.tag;
681-
card.classList.toggle('hidden', cardTag !== tag && cardTag !== 'none');
740+
const cards = document.querySelectorAll('.card');
741+
// Fade out cards that will hide
742+
cards.forEach(card => {
743+
const cardTag = card.dataset.tag;
744+
const shouldShow = tag === 'todos' || cardTag === tag || cardTag === 'none';
745+
if (!shouldShow && !card.classList.contains('hidden')) {
746+
card.classList.add('fading-out');
682747
}
683748
});
749+
// After fade out, toggle display and fade in new ones
750+
filterTimer = setTimeout(() => {
751+
cards.forEach(card => {
752+
const cardTag = card.dataset.tag;
753+
const shouldShow = tag === 'todos' || cardTag === tag || cardTag === 'none';
754+
card.classList.remove('fading-out');
755+
if (shouldShow) {
756+
card.classList.remove('hidden');
757+
card.classList.add('fading-in');
758+
requestAnimationFrame(() => requestAnimationFrame(() => {
759+
card.classList.remove('fading-in');
760+
}));
761+
} else {
762+
card.classList.add('hidden');
763+
}
764+
});
765+
}, 200);
766+
}
767+
768+
// Inject count badges
769+
(function initCounts() {
770+
document.querySelectorAll('.filter-btn').forEach(btn => {
771+
const tag = btn.dataset.tag;
772+
const count = tag === 'todos'
773+
? allCards.length
774+
: document.querySelectorAll(`.card[data-tag="${tag}"]:not(.placeholder)`).length;
775+
btn.insertAdjacentHTML('beforeend', ` <span class="filter-count">${count}</span>`);
776+
});
777+
})();
778+
779+
// ─────────────────────────────────────────────
780+
// BUSQUEDA
781+
// ─────────────────────────────────────────────
782+
function normalizar(str) {
783+
return str.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
684784
}
685785

786+
const inputBuscar = document.getElementById('buscar');
787+
inputBuscar.addEventListener('input', () => {
788+
const q = normalizar(inputBuscar.value.trim());
789+
const activeTag = document.querySelector('.filter-btn.active')?.dataset.tag || 'todos';
790+
document.querySelectorAll('.card').forEach(card => {
791+
if (card.classList.contains('placeholder')) return;
792+
const title = normalizar(card.querySelector('h2')?.textContent || '');
793+
const desc = normalizar(card.querySelector('p')?.textContent || '');
794+
const matchSearch = !q || title.includes(q) || desc.includes(q);
795+
const matchTag = activeTag === 'todos' || card.dataset.tag === activeTag;
796+
card.classList.toggle('hidden', !(matchSearch && matchTag));
797+
});
798+
});
799+
686800
// ─────────────────────────────────────────────
687801
// DARK MODE
688802
// ─────────────────────────────────────────────
689803
const btnTheme = document.getElementById('btn-theme');
690804

691805
function applyTheme(dark) {
806+
const metaTheme = document.getElementById('meta-theme');
692807
if (dark) {
693808
document.documentElement.setAttribute('data-theme', 'dark');
694809
btnTheme.textContent = '\u2600\uFE0F';
810+
btnTheme.setAttribute('aria-label', 'Cambiar a modo claro');
811+
metaTheme.setAttribute('content', '#152238');
695812
} else {
696813
document.documentElement.removeAttribute('data-theme');
697814
btnTheme.textContent = '\uD83C\uDF19';
815+
btnTheme.setAttribute('aria-label', 'Cambiar a modo oscuro');
816+
metaTheme.setAttribute('content', '#0f172a');
698817
}
699818
}
700819

0 commit comments

Comments
 (0)