Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/abisai7/diccionario-chapin/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Chapinismos uses Astro components (.astro files) for building the UI. Components are highly reusable, support props, and can include scoped styles and client-side scripts.

Component Architecture

Component Categories

components/
├── Layout Components      # Header, Footer, Navigation
├── Feature Components     # SearchBox, WordCard, Ticker
├── Home Components        # Homepage sections
├── Icon Components        # SVG icons
└── Schema Components      # JSON-LD structured data

Core Components

Header Component

Main site header with logo, navigation, language switcher, and theme toggle.
---
import { getLangFromUrl } from "../utils/i18n";
import Logo from "./icons/Logo.astro";
import LanguageSwitcher from "./LanguageSwitcher.astro";
import Navigation from "./Navigation.astro";
import MobileMenu from "./MobileMenu.astro";
import ThemeToggle from "./ThemeToggle.astro";

const lang = getLangFromUrl(Astro.url);
const prefix = lang === "en" ? "/en" : "/es";
---

<header class="relative mx-auto max-w-[1100px] px-4 py-4" transition:persist>
  <div class="flex items-center justify-between gap-4">
    <a href={`${prefix}/`} class="flex items-center gap-2">
      <Logo height={75} />
    </a>

    <!-- Desktop navigation -->
    <nav class="hidden items-center gap-2 md:flex">
      <Navigation />
      <LanguageSwitcher />
      <ThemeToggle />
    </nav>

    <!-- Mobile navigation -->
    <div class="flex items-center gap-2 md:hidden">
      <LanguageSwitcher />
      <ThemeToggle isMobile={true} />
      <MobileMenu />
    </div>
  </div>
</header>
None - Uses Astro.url to detect language

WordCard Component

Displays a word preview card with category badge.
---
import type { CollectionEntry } from "astro:content";
import { getCategoryColor } from "../utils/categoryColors";
import { useTranslations } from "../utils/i18n";
import { ArrowRight } from "@lucide/astro";

interface Props {
  entry: CollectionEntry<"words-es" | "words-en">;
  lang: string;
  titleLevel?: "h3" | "h4";
}

const { entry, lang, titleLevel = "h4" } = Astro.props;
const t = useTranslations(lang);
---

<a
  href={`/${lang}/palabras/${entry.slug}/`}
  class="word-related-card rounded-lg border p-4 transition-all hover:-translate-y-1"
>
  {entry.data.category && (
    <span
      class="absolute top-3 right-3 rounded-full px-2 py-0.5 text-xs"
      style={`background-color: ${getCategoryColor(entry.data.category)};`}
    >
      {entry.data.category}
    </span>
  )}
  
  {titleLevel === "h3" ? (
    <h3 class="m-0 mb-2 font-semibold">{entry.data.word}</h3>
  ) : (
    <h4 class="m-0 mb-2 font-semibold">{entry.data.word}</h4>
  )}
  
  <p class="m-0 line-clamp-2 text-sm">{entry.data.meaning}</p>
  
  <span class="mt-2 inline-flex items-center gap-1 text-xs">
    {t("word.learn_more")}
    <ArrowRight size={12} />
  </span>
</a>
PropTypeRequiredDefaultDescription
entryCollectionEntryYes-Word data from content collection
langstringYes-Current language (es/en)
titleLevel”h3” | “h4”No”h4”Heading level for SEO

SearchBox Component

Search input with keyboard shortcut support.
---
interface Props {
  placeholder?: string;
}

const { placeholder = "Search..." } = Astro.props;
---

<form role="search" class="search-form">
  <input
    type="search"
    name="q"
    placeholder={placeholder}
    class="search-input"
    autocomplete="off"
    aria-label="Search words"
  />
</form>

<style>
  .search-input {
    width: 100%;
    padding: 0.75rem 1rem;
    border-radius: 0.5rem;
    border: 1px solid var(--border);
    background: var(--card);
    color: var(--text);
    font-size: 1rem;
  }
  
  .search-input:focus {
    outline: 2px solid var(--primary);
    border-color: var(--primary);
  }
</style>

Homepage Components

HeroSection Component

Homepage hero with title, subtitle, and search.
---
import { Info } from "@lucide/astro";
import SearchBox from "../SearchBox.astro";
import { useTranslations } from "../../utils/i18n";

interface Props {
  lang: string;
}

const { lang } = Astro.props;
const t = useTranslations(lang);
---

<section class="mx-auto max-w-[1100px]">
  <div class="card-with-gradient m-6 grid gap-3.5">
    <h1 class="gradient-text mx-4 mt-4 text-[clamp(1.8rem,2.4vw,2.6rem)]">
      {t("home.title")}
    </h1>
    <p class="text-muted mx-4 text-base">
      {t("home.subtitle")}
    </p>
    <div class="mx-4 mb-4">
      <SearchBox placeholder={t("home.search.placeholder")} />
    </div>
    <div class="text-muted mx-4 mb-4 hidden md:block">
      <span class="inline-flex items-center gap-2">
        <Info size={16} />
        {t("home.search.tip")}
        <kbd class="rounded border px-2 py-0.5">/</kbd>
        {t("home.search.tip2")}
      </span>
    </div>
  </div>
</section>

FeaturedWords Component

Grid of featured word cards.
---
import WordCard from "../WordCard.astro";
import { useTranslations } from "../../utils/i18n";

interface Props {
  words: any[];
  lang: string;
}

const { words, lang } = Astro.props;
const t = useTranslations(lang);
---

<section class="mx-auto max-w-[1100px] px-6">
  <h2 class="mb-6 text-2xl font-bold">{t("home.featured.title")}</h2>
  <div class="grid grid-cols-[repeat(auto-fill,minmax(260px,1fr))] gap-3.5">
    {words.map((entry) => (
      <WordCard entry={entry} lang={lang} titleLevel="h3" />
    ))}
  </div>
</section>

Schema Components

JSON-LD structured data components for SEO.

WordSchema Component

---
interface Props {
  lang: string;
  siteUrl: string;
  word: any;
  slug: string;
}

const { lang, siteUrl, word, slug } = Astro.props;

const schema = {
  "@context": "https://schema.org",
  "@type": "DefinedTerm",
  "name": word.word,
  "description": word.meaning,
  "inDefinedTermSet": `${siteUrl}/${lang}/indice/`,
  "url": `${siteUrl}/${lang}/palabras/${slug}/`,
};
---

<script type="application/ld+json" set:html={JSON.stringify(schema)} />

Creating New Components

Basic Component Template

1

Create the file

Create a new .astro file in src/components/:
touch src/components/MyComponent.astro
2

Define the component

---
// TypeScript props interface
interface Props {
  title: string;
  description?: string;
}

const { title, description } = Astro.props;
---

<div class="my-component">
  <h2>{title}</h2>
  {description && <p>{description}</p>}
</div>

<style>
  .my-component {
    padding: 1rem;
    border-radius: 0.5rem;
  }
</style>
3

Use the component

---
import MyComponent from "../components/MyComponent.astro";
---

<MyComponent title="Hello" description="World" />

Component with Client Script

For interactive components, add a <script> tag:
---
interface Props {
  buttonText: string;
}

const { buttonText } = Astro.props;
---

<button id="my-button" class="btn">{buttonText}</button>

<script>
  document.addEventListener("astro:page-load", () => {
    const button = document.getElementById("my-button");
    button?.addEventListener("click", () => {
      alert("Clicked!");
    });
  });
</script>
Use astro:page-load event for scripts that should work with View Transitions.

Component with Slots

Use slots for flexible content:
---
interface Props {
  title: string;
}

const { title } = Astro.props;
---

<section class="card">
  <h2>{title}</h2>
  <div class="content">
    <slot />  <!-- Default slot -->
  </div>
  <footer>
    <slot name="footer" />  <!-- Named slot -->
  </footer>
</section>
Usage:
<MyCard title="Title">
  <p>This goes in the default slot</p>
  <div slot="footer">Footer content</div>
</MyCard>

Best Practices

interface Props {
  title: string;
  count?: number;  // Optional prop
}

const { title, count = 0 } = Astro.props;  // Default value
Each component should have a single, clear purpose. Break large components into smaller, reusable pieces.
Styles in <style> tags are automatically scoped to the component:
<style>
  .my-class {
    color: red;  /* Only affects this component */
  }
</style>
<style>
  .card {
    background: var(--card);
    color: var(--text);
    border: 1px solid var(--border);
  }
</style>
For components that need translations:
---
import { useTranslations } from "../utils/i18n";

interface Props {
  lang: string;
}

const { lang } = Astro.props;
const t = useTranslations(lang);
---

Component Utilities

Category Colors

import { getCategoryColor } from "../utils/categoryColors";

const color = getCategoryColor("sustantivo");  // Returns hex color

Translations

import { useTranslations } from "../utils/i18n";

const t = useTranslations("es");
const title = t("home.title");

Next Steps

Layouts

Learn about layout components

Styling

Deep dive into styling

Internationalization

Work with translations

Icons

Using Lucide icons