Informe de la Práctica 06 Introducción a Nextra

Sobre la Práctica.

En esta práctica hemos realizado una introducción a otro SSG como lo es Nextra.

Nextra es un SSG basado en Next.js, que nos permite crear sitios web estáticos de forma sencilla y rápida. Para introducirnos con algunas características se ha creado un total de siete páginas, cada una con un contenido y características diferentes.

Bleed

Para la implementación de bleed hemos creado un componente Bleed en el archivo components/Bleed.jsx:

import React from 'react';
import styles from './Bleed.module.css';
 
const Bleed = ({ children }) => {
  return <div className={styles.bleed}>{children}</div>;
};
 
export default Bleed;

También, creando un archivo de estilo bleed.css para darle estilo al componente:

.bleed {
  width: 100%;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}
 
.bleed img {
  width: 100%;
  height: auto;
}

Y lo usamos en nuestra página sopapollo.mdx:

<Bleed>
  <img src="/images/sopa-de-pollo.jpg" alt="Imagen de una sopa de Pollo" />
</Bleed>

Callout

La segunda página que hemos creado es la página Sopa de Zanahoria, en la que hemos utilizado la propiedad callout de Nextra para mostrar un mensaje destacado. Para ello, hemos creado un componente Callout en el archivo components/Callout.jsx:

import styles from '../styles/Callout.module.css'
 
export default function Callout({ children, type = 'info', emoji }) {
  return (
    <div className={`${styles.callout} ${styles[type]}`}>
      {emoji && <span className={styles.emoji}>{emoji}</span>}
      {children}
    </div>
  )
}

Definimos un fichero de estilo styles/Callout.module.css para darle estilo al componente:

.callout {
  padding: 1rem;
  border-radius: 0.5rem;
  margin: 1rem 0;
}
 
.callout.info {
  background-color: #d1ecf1;
  border: 1px solid #bee5eb;
  color: #0c5460;
}
 
.callout.warning {
  background-color: #fff3cd;
  border: 1px solid #ffeeba;
  color: #856404;
}
 
.emoji {
  margin-right: 0.5rem;
}

Y lo usamos en nuestra página sopazanahoria.mdx:

<Callout type="info" emoji="ℹ️">
  **Dato curioso**: La zanahoria solía ser de colores morados y rojos antes de que la variedad naranja se volviera dominante.
</Callout>

Cards

Para la implementación de Cards hemos creado un componente Cards en el archivo components/Cards.jsx:

import React from 'react';
import cn from 'clsx';
import NextLink from 'next/link';
import PropTypes from 'prop-types';
 
function Card({ children, title, arrow, href, ...props }) {
  return (
    <NextLink
      href={href}
      className={cn(
        'nextra-focus nextra-card _group _flex _flex-col _justify-start _overflow-hidden _rounded-lg _border _border-gray-200',
        '_text-current _no-underline dark:_shadow-none',
        'hover:_shadow-gray-100 dark:hover:_shadow-none _shadow-gray-100',
        'active:_shadow-sm active:_shadow-gray-200',
        '_transition-all _duration-200 hover:_border-gray-300',
        children
          ? '_bg-gray-100 _shadow dark:_border-neutral-700 dark:_bg-neutral-800 dark:_text-gray-50 hover:_shadow-lg dark:hover:_border-neutral-500 dark:hover:_bg-neutral-700'
          : '_bg-transparent _shadow-sm dark:_border-neutral-800 hover:_bg-slate-50 hover:_shadow-md dark:hover:_bg-neutral-900'
      )}
      {...props}
    >
      {children}
      <span
        className={cn(
          '_flex _font-semibold _items-center _gap-2 _p-4 _text-gray-700 hover:_text-gray-900',
          arrow &&
            'after:_content-["→"] after:_transition-transform after:_duration-75 after:group-hover:_translate-x-0.5',
          children
            ? 'dark:_text-gray-300 dark:hover:_text-gray-100'
            : 'dark:_text-neutral-200 dark:hover:_text-neutral-50'
        )}
      >
        {title}
      </span>
    </NextLink>
  );
}
 
Card.propTypes = {
  title: PropTypes.string.isRequired,
  arrow: PropTypes.bool,
  href: PropTypes.string.isRequired,
  children: PropTypes.node,
};
 
function Cards({ children, num = 3, className, style, ...props }) {
  return (
    <div
      className={cn(
        'nextra-cards _mt-4 _gap-4 _grid',
        '_not-prose', // for nextra-theme-blog
        className
      )}
      {...props}
      style={{
        ...style,
        ['--rows']: num
      }}
    >
      {children}
    </div>
  );
}
 
Cards.propTypes = {
  children: PropTypes.node.isRequired,
  num: PropTypes.number,
  className: PropTypes.string,
  style: PropTypes.object,
};
 
export { Cards, Card };

Y luego lo usamos en nuestra página sopazanahoria.mdx:

# Accesos directos (Cards)
 
<Cards>
  <Card title="¿Qué son los Callout?" href="https://nextra.site/docs/guide/built-ins/callout" />
  <Card title="¿Qué son los Tabs?" href="https://nextra.site/docs/guide/built-ins/tabs" />
  <Card title="¿Qué son los Steps?" href="https://nextra.site/docs/guide/built-ins/steps" />
</Cards>

Las cards son una forma de mostrar información de manera visual y atractiva, y son muy útiles para presentar enlaces o accesos directos en una página web.

EvaluarExpresion

EvaluarExpresion hace referencia a la interpolación de expresiones en javascript. Para ello, en el archivo sopadecebolla hemos definido un bloque de ejemplo de pseudocódigo en javascript:

// Ejemplo de interpolación de expresión
let tiempoCoccion = 30;
let tiempoTotal = tiempoCoccion * 2;
console.log(`El tiempo total de cocción es de ${tiempoTotal} minutos.`);

La parte interesante de este bloque es la interpolación de la expresión ${tiempoTotal}, que nos permite evaluar la expresión y mostrar el resultado en el mensaje.

CodeExtension

En la página sopacebolla hemos utilizado la propiedad codeExtension de Nextra para mostrar un bloque de código con posibilidad a copiar el código, mostrar el número de líneas, mostrar un nombre del bloque de código y remarcar una línea en específico. Accede al bloque aquí CodeExtension.js

File Trees

Para mostrar la estructura de directorios de nuestro proyecto hemos creado un componente FileTree en el archivo components/FileTree.jsx:

// components/FileTree.jsx
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import styles from '../styles/FileTree.module.css';
 
const Folder = ({ name, children, defaultOpen }) => {
  const [isOpen, setIsOpen] = useState(defaultOpen);
 
  const toggleOpen = () => {
    setIsOpen(!isOpen);
  };
 
  return (
    <div className={styles.folder}>
      <div className={styles.folderName} onClick={toggleOpen}>
        {isOpen ? '📂' : '📁'} {name}
      </div>
      {isOpen && <div className={styles.folderContent}>{children}</div>}
    </div>
  );
};
 
Folder.propTypes = {
  name: PropTypes.string.isRequired,
  children: PropTypes.node,
  defaultOpen: PropTypes.bool,
};
 
const File = ({ name }) => {
  return <div className={styles.file}>📄 {name}</div>;
};
 
File.propTypes = {
  name: PropTypes.string.isRequired,
};
 
const FileTree = ({ children }) => {
  return <div className={styles.fileTree}>{children}</div>;
};
 
FileTree.Folder = Folder;
FileTree.File = File;
 
FileTree.propTypes = {
  children: PropTypes.node.isRequired,
};
 
export default FileTree;

Definiendo también un archivo de estilos styles/FileTree.module.css para darle estilo al componente:

/* components/FileTree.module.css */
.fileTree {
  font-family: Arial, sans-serif;
  font-size: 14px;
}
 
.folder {
  margin-left: 20px;
}
 
.folderName {
  cursor: pointer;
  font-weight: bold;
}
 
.folderContent {
  margin-left: 20px;
}
 
.file {
  margin-left: 20px;
}

Y lo usamos en nuestra página sopacebolla.mdx:

## Ejemplo de File Tree
<FileTree>
  <FileTree.Folder name="pages" defaultOpen>
    <FileTree.File name="_meta.json" />
    <FileTree.File name="caldoverde.mdx" />
    <FileTree.File name="gachas.mdx" />
    <FileTree.File name="sancocho.mdx" />
    <FileTree.File name="sopacebolla.mdx" />
    <FileTree.File name="sopazanahoria.mdx" />
    <FileTree.File name="sopapollo.mdx" />
  <FileTree.Folder name="syles" defaultOpen>
    <FileTree.File name="bleed.css" />
    <FileTree.File name="Callout.module.css" />
    </FileTree.Folder>
  </FileTree.Folder>
</FileTree>

Los File Trees son una forma visual y clara de mostrar la estructura de directorios de un proyecto, y son muy útiles para documentar y compartir información sobre la organización de los archivos.

Frontmatter

En la página sancocho hemos definido un frontmatter, y luego en la página hemos accedido a los datos del frontmatter para mostrar el título y la descripción de la página:

---
title: Sancocho
description: Conoce la historia y los beneficios del sancocho, un plato tradicional en todo el mundo.
date: 2024-11-10
tags:
  - comida
  - recetas
  - sopa
---
 
## Ejemplo de acceso al Frontmatter
 
Estoy accediendo al Frontmatter para mostrar el título de la página: **{frontMatter.title}**, la fecha de publicación: **{frontMatter.date}** y las etiquetas: **{frontMatter.tags.join(', ')}** y la descripción: **{frontMatter.description}**.

El frontmatter es una forma de añadir metadatos a las páginas MDX, como el título, la descripción, la fecha y las etiquetas, y es muy útil para organizar y estructurar el contenido de un sitio web.

Heroicons.

Para añadir iconos a nuestra página hemos utilizado Heroicons, una librería de iconos para React. Definimos un componente IconButton en el archivo components/IconButton.jsx:

const IconButton = ({ label, href }) => {
  return (
    <a href={href} className="flex items-center space-x-2 p-2 border rounded">
      <MailIcon className="h-5 w-5 text-gray-500" />
      <span>{label}</span>
    </a>
  )
}
 
export default IconButton

Y lo usamos en nuestra página index.mdx:

Si tiene alguna duda con respecto a mi desarrollo de la práctica, 
no dude en ponerse en contacto conmigo a través de mi correo electrónico.
 
<IconButton label="Enviar un correo" href="mailto:alu0101443126@gmail.com" />

iframe

Para añadir un video de youtube hemos utilizado la propiedad iframe de Nextra. Para ello, simplemente añadimos el código del iframe en nuestra página sancocho.mdx:

export const YourVideoID = "Ep2vKrAARHI"
 
## Video sobre el Sancocho
 
Aquí tienes un video sobre cómo preparar un sancocho:
 
<iframe 
    width="560" 
    height="315" 
    src="https://www.youtube.com/embed/Ep2vKrAARHI" 
    title="YouTube video player" 
    frameBorder="0" 
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" 
    allowFullScree
>
</iframe>

Markdown import

Para esta tarea mostraré un ejemplo de una lista de tareas. Que ejemplifica el uso de la propiedad de import de markdown.

  • Bleed
  • Callout
  • Cards
  • Code Extensions
  • Expression Evaluation
  • File Trees
  • frontmatter
  • Heroicons
  • iframe
  • Markdown import
  • Mermaid
  • Using Components (MDX)
  • useConfig

Mermaid

En la página sancocho.mdx hemos utilizado la propiedad Mermaid de Nextra para mostrar un diagrama de flujo. Primero definimos el archivo components/Mermaid.jsx:

const Mermaid = ({ chart }) => {
  useEffect(() => {
    mermaid.initialize({ startOnLoad: true });
    mermaid.contentLoaded();
  }, [chart]);
 
  return <div className="mermaid">{chart}</div>;
};
 
export default Mermaid;

Y lo usamos en nuestra página caldoverde.mdx:

# Diagrama de flujo con Mermaid
 
<Mermaid chart={`
graph TD;
    CalentarAceite-->SofreirCebollaYAjo;
    SofreirCebollaYAjo-->AgregarPapasYAgua;
    AgregarPapasYAgua-->CocinarHastaTiernas;
    CocinarHastaTiernas-->CocinarChorizo;
    CocinarChorizo-->HacerPure;
    HacerPure-->AgregarColRizada;
    AgregarColRizada-->CocinarCol;
    CocinarCol-->AgregarChorizo;
    AgregarChorizo-->SazonarYServir;
`} />
 

Using Components (MDX)

La mayoría de las características sino todas, han sido implementadas utilizando componentes de React, lo que nos permite reutilizar código y mantener una estructura limpia y organizada. Estos componentes se encuentran en el directorio components y se importan en las páginas MDX según sea necesario. Los componentes son una forma poderosa de extender las capacidades de Nextra y personalizar el contenido de las páginas.

useConfig

Para acceder a la configuración de Nextra hemos utilizado el hook useConfig, que nos permite acceder a la configuración global del sitio. Por ejemplo, hemos utilizado useConfig para acceder al título del sitio en el componente Header:

import useConfig from 'use-config';
 
const Header = () => {
  const { title } = useConfig();
 
  return (
    <header>
      <h1>{title}</h1>
    </header>
  );
};
 
export default Header;

Conclusiones

En esta práctica hemos aprendido a utilizar Nextra para generar sitios web estáticos con Next.js. Nextra es una herramienta muy potente y fácil de usar que permite crear sitios web estáticos de forma rápida y sencilla. Además, hemos explorado algunas de las características más interesantes de Nextra, como la propiedad bleed, callout, cards, evaluarExpresion, codeExtension, fileTrees, frontmatter, heroicons, iframe, markdown import, mermaid y useConfig. Estas características nos permiten personalizar el contenido de las páginas y añadir funcionalidades avanzadas a nuestro sitio web.