Ir al contenido

Guía de componentes

Esta página muestra cada componente funcionando, seguido del código que lo genera. Todos funcionan sin servidor: el portal es HTML estático.

Las clases se escriben en MDX (Markdown con componentes). Importa los componentes al inicio del archivo, después del frontmatter:

import { Tabs, TabItem, Steps } from '@astrojs/starlight/components';
import Ejercicio from '~/components/Ejercicio.astro';
import Pregunta from '~/components/Pregunta.astro';
import Descarga from '~/components/Descarga.astro';
import Sandbox from '~/components/Sandbox.astro';
import Excalidraw from '~/components/Excalidraw.astro';

Escribe un bloque de código con el lenguaje mermaid. El diagrama se adapta al modo claro u oscuro, se puede ampliar y descargar como SVG. El atributo title es opcional y se usa como pie de figura y nombre del archivo.

flowchart LR
    U([Usuario]) --> CDN[CDN / Proxy]
    CDN --> FE[Frontend]
    FE -->|HTTP/JSON| API[API]
    API --> DB[(Base de datos)]
    API -.->|eventos| Q[[Cola]]
    Q -.-> W[Worker]
Arquitectura de una aplicación web típica
```mermaid title="Arquitectura de una aplicación web típica"
flowchart LR
U([Usuario]) --> CDN[CDN / Proxy]
CDN --> FE[Frontend]
FE -->|HTTP/JSON| API[API]
API --> DB[(Base de datos)]
```

Mermaid admite diagramas de secuencia, clases, estados, entidad-relación, Gantt y más: consulta la documentación de Mermaid. La librería solo se descarga en las páginas que contienen diagramas.

erDiagram
    ESTUDIANTE ||--o{ INSCRIPCION : realiza
    CURSO ||--o{ INSCRIPCION : recibe
    ESTUDIANTE { int id string nombre }
    CURSO { int id string titulo }
    INSCRIPCION { date fecha float nota }
Modelo entidad-relación

Para diagramas dibujados a mano: se muestra el SVG exportado y se ofrece la fuente .excalidraw, que cualquiera puede abrir y editar en excalidraw.com.

Estados de un ADR: Propuesto, Aceptado, Rechazado, Obsoleto y Reemplazado
Ciclo de vida de un ADRSVGFuente .excalidraw
<Excalidraw
svg="/diagramas/arquitectura-software/ciclo-adr.svg"
fuente="/diagramas/arquitectura-software/ciclo-adr.excalidraw"
alt="Descripción del diagrama para lectores de pantalla"
titulo="Ciclo de vida de un ADR"
/>

Resaltado de sintaxis, botón de copiar, título de archivo, líneas destacadas y diferencias (con Expressive Code):

src/servicios/inscripciones.ts
export async function inscribir(estudianteId: string, cursoId: string) {
const curso = await cursos.buscar(cursoId);
if (curso.cupos === 0) {
throw new Error('El curso no tiene cupos disponibles');
}
await db.insert({ estudianteId, cursoId });
await db.insert({ estudianteId, cursoId, fecha: new Date() });
}
```ts title="src/servicios/inscripciones.ts" {3-4} ins={7} del={6}
…
```

Con syncKey, todas las pestañas de la página (y del sitio) recuerdan el lenguaje elegido.

const suma = (a, b) => a + b;
<Tabs syncKey="lenguaje">
<TabItem label="JavaScript" icon="seti:javascript">
```js
const suma = (a, b) => a + b;
```
</TabItem>
<TabItem label="Python" icon="seti:python">
…
</TabItem>
</Tabs>
:::note[Concepto clave]
Texto en **Markdown**.
:::

Los tipos son note, tip, caution y danger. El título entre corchetes es opcional.

  1. Instala las dependencias:

    Ventana de terminal
    npm install
  2. Inicia el servidor de desarrollo.

  3. Abre el navegador en http://localhost:4321.

<Steps>
1. Instala las dependencias:
```bash
npm install
```
2. Inicia el servidor de desarrollo.
</Steps>

Para estructuras de proyecto, usa <FileTree>:

  • Directoriosrc/
    • Directoriocomponentes/
      • Boton.tsx
    • index.ts archivo destacado
  • package.json
EjercicioInvierte una cadenabásico⏱ 10 min

Escribe una función invertir(texto) que devuelva el texto al revés, sin usar reverse().

Ver solución
function invertir(texto) {
let resultado = '';
for (const caracter of texto) resultado = caracter + resultado;
return resultado;
}
<Ejercicio titulo="Invierte una cadena" nivel="básico" duracion="10 min">
Enunciado en **Markdown**.
<Fragment slot="solucion">
Solución (opcional), plegada por defecto.
</Fragment>
</Ejercicio>

Propiedades: titulo (obligatorio), nivel (básico, intermedio, avanzado), duracion y tipo (Ejercicio, Reto, Práctica).

Plantilla de ADRMarkdownMD · 1 KB Archivos del laboratorioZIPZIP · 1 KB Plantilla de hoja de vidaPDFPDF · 25 KB Cliente HTTPCódigo fuenteMJS · 722 B
<Descarga href="/recursos/<materia>/archivo.pdf" titulo="Título visible" descripcion="Opcional" />

El tipo y el tamaño se calculan solos. Si el archivo no existe en public/, la compilación falla: así no se publican enlaces rotos.

Los ejemplos se ejecutan en un iframe aislado (sandbox): pueden usar JavaScript, pero no acceden a cookies, almacenamiento ni a la página del portal. Debajo se ofrece el código fuente.

Anatomía de una URLDescargar
Ver código fuente (anatomia-url.html)
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>Anatomía de una URL</title>
<style>
body { font: 15px/1.5 system-ui, sans-serif; margin: 0; padding: 1.25rem; color: #1b1d26; background: #fff; }
label { font-weight: 600; display: block; margin-bottom: .35rem; }
input { width: 100%; box-sizing: border-box; padding: .6rem .75rem; font: 14px ui-monospace, monospace; border: 1px solid #c7cad3; border-radius: .5rem; }
input:focus { outline: 2px solid #3f47c6; border-color: transparent; }
dl { display: grid; grid-template-columns: max-content 1fr; gap: .4rem 1rem; margin: 1.25rem 0 0; }
dt { font-weight: 600; color: #3f47c6; }
dd { margin: 0; font-family: ui-monospace, monospace; word-break: break-all; }
.vacio { color: #8a8f9c; font-style: italic; font-family: system-ui, sans-serif; }
.error { color: #c0392b; margin-top: 1rem; }
.nota { font-size: 13px; color: #5d6270; margin-top: 1rem; }
</style>
</head>
<body>
<label for="url">Escribe una URL</label>
<input id="url" value="https://ejemplo.edu:8443/cursos/web?unidad=2&amp;tema=http#ejercicios" spellcheck="false" autocomplete="off">
<div id="salida" aria-live="polite"></div>
<p class="nota">El análisis usa la API estándar <code>URL</code> del navegador.</p>
<script>
const partes = [
['Esquema', u => u.protocol.replace(':', '')],
['Host', u => u.hostname],
['Puerto', u => u.port || `(por defecto: ${u.protocol === 'https:' ? 443 : 80})`],
['Ruta', u => u.pathname],
['Consulta', u => [...u.searchParams].map(([k, v]) => `${k} = ${v}`).join('\n')],
['Fragmento', u => u.hash.slice(1) && `${u.hash.slice(1)} ← no se envía al servidor`],
];
const entrada = document.getElementById('url');
const salida = document.getElementById('salida');
function analizar() {
try {
const u = new URL(entrada.value);
salida.innerHTML = '<dl>' + partes.map(([nombre, f]) => {
const valor = f(u);
const dd = document.createElement('dd');
if (valor) { dd.textContent = valor; dd.style.whiteSpace = 'pre-line'; }
else { dd.textContent = '(vacío)'; dd.className = 'vacio'; }
return `<dt>${nombre}</dt>${dd.outerHTML}`;
}).join('') + '</dl>';
} catch {
salida.innerHTML = '<p class="error">No es una URL válida. ¿Incluiste el esquema (https://)?</p>';
}
}
entrada.addEventListener('input', analizar);
analizar();
</script>
</body>
</html>
<Sandbox src="/ejemplos/<materia>/ejemplo.html" titulo="Título" alto={400} />

Los archivos van en public/ejemplos/<materia>/. Usa codigo={false} para ocultar el código fuente.

Autoevaluación¿Qué componente usarías para mostrar los pasos de un laboratorio?

<Pregunta
enunciado="¿Qué componente usarías para mostrar los pasos de un laboratorio?"
opciones={['<Tabs>', '<Steps>', '<Descarga>', '<Sandbox>']}
correcta={1}
explicacion="Se muestra al acertar."
/>

correcta es la posición de la respuesta empezando en 0. La corrección ocurre en el navegador y no se guarda nada: es una herramienta de autoevaluación, no de calificación (la respuesta es visible en el código de la página).

  • Insignias: Nuevo Opcional Avanzado
  • Tarjetas de enlace: