Saltar al contenido

· Documentation  · 5 min read

AstroWind template in depth

While easy to get started, Astrowind is quite complex internally. This page provides documentation on some of the more intricate parts.

While easy to get started, Astrowind is quite complex internally.  This page provides documentation on some of the more intricate parts.

Overview

It can be a somewhat daunting task trying to get a handle on AstroWind internals, and particularly various points of usage.

This page outlines and clarifies some of the techniques found in AstroWind. Use it as a guide for further modification, or an instructional for techniques to use in your own endeavors.

Styling

As the name suggests, AstroWind relies on TailWind for styling. Furthermore, AstroWind defines custom low level style settings which are incorporated into TailWind seamlessly, and which provides consistency for higher level styling constructs, as well as enabling dark mode.

The styling mechanism consists of the following files (all paths are prefixed with /src/ ):

assets/styles/tailwind.css

This file is essentially an extension of TailWind’s base.css. High-level component styles are defined here. Note also styling on elements selected by ‘attribute’ selectors at the bottom of the files, particularly those selected by ‘data’ attributes.

components/CustomStyles.astro

Defines custom colors and fonts. For these to take effect in the ‘base.css’ file, they need to be loaded in the html header section. See next.

layouts/Layout.astro

This layout is used for all of the pages rendered by AstroWind. The contents of tailwind.css and CustomStyles.astro component, described above, is injected into the html header.

Dark Mode

Dark Mode is triggered by the little ‘sunlight’ icon:in the page header. It is defined in the components/common/ToggleTheme.astro, but the event is attached and the action defined in components/common/BasicScripts.astro in the following snippet:

attachEvent('[data-aw-toggle-color-scheme]', 'click', function () {
  if (defaultTheme.endsWith(':only')) {
    return;
  }
  document.documentElement.classList.toggle('dark');
  localStorage.theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
});

Note that this is a client event. BasicScripts.astro defines several other client-side functionality as well as this one.

Advanced Slot Usage

slots are part of the component implementation, which is a common concept among many frameworks, including Astrojs. The typical slot definition in a component looks like this:

---
// (file: MyComponent.astro)
const { title } = Astro.props;
export interface Props {
  title: string;
}
---

<div>
  <h2>{title}</h2>
  <slot />
  <!-- slot contents injected here -->
  <div></div>
</div>

And in usage elsewhere:

import MyComponent from "~/components/MyComponent"; ...
<MyComponent someArg="A Slot example">
  <p>This content will be displayed in the slot</p>
</MyComponent>

Alternate usage

There’s another way we can use slots, useful particularly when a component can have markdown content is as follows (study carefully…):

---
// (file: MyComponent.astro)

const { title } = Astro.props;
export interface Props {
  title: string;
}
const content: string = await Astro.props.render('default');
---

// renders the html to the 'content' variable
<div>
  <h2>{title}</h2>
  <div set:html={content} />
  <!-- slot contents injected here -->
  <div></div>
</div>

Whoa!! What’s going on here?

Notice there is no slot definition in the html portion of the component. Instead, what we do is have Astro render the slot content (here, the ‘default’ content, but you can also render named slots) into a variable, and then use that content in a div (for instance).

So, if the usage is in a markdown file, like so:

import MyComponent from '../../components/MyComponent';

# Using the above component in a .mdx file (that can take components)

{' '}

<MyComponent title="This is a slot implementor">### Here is some markdown content - With a bullet item.</MyComponent>

MyComponent renders the markdown to html and then injects it into the div.

This actually has a big advantage — consider that with the normal usage you don’t have access to the slot contents: Astro just plops the content into the <slot/> tag. Using this method, however, allows you to access the content and further manipulate it before it gets inserted into the html.

This allows a great deal of flexibility in component design.

Yet Another Step

Now, we get to the techniques used in AstroWind, we’ll use the pages/index.astro file to illustrate.

You’ll note that the index file imports a lot of components, each one roughly analagous to a panel in the index page. Each of these components, in turn, is instantiated sequentially throughout the page. But, you’ll notice that some of them use this kind of construct (we’ll use the last section, CallToAction, as it is most illustrative of the technique):

<CallToAction
  callToAction={{
    text: 'Get template',
    href: 'https://github.com/onwidget/astrowind',
    icon: 'tabler:download',
  }}
>
  <Fragment slot="title">
    Astro + <br class="block sm:hidden" /><span class="sm:whitespace-nowrap">Tailwind CSS</span>
  </Fragment>

  <Fragment slot="subtitle">
    Be very surprised by these huge fake numbers you are seeing on this page. <br class="hidden md:inline" />Don't waste
    more time! :P
  </Fragment>
</CallToAction>

Some things to note, here:

The callToAction argument

This argument is actually being passed a javascript object — not a string. (However, in the TS definition, it could be a string…)

There are several Fragment children

Furthermore, these <Fragment/> elements each have a slot=“(value)” specifier.

The latter seems odd, because <Fragment/> is a built-in component over which you have no control, and doesn’t have a provision for rendering slots, per se.

The answer lies in a paragraph in the Astro docs, slots section, which states:

Use a slot="my-slot" attribute on the child element that you want to pass through to a matching slot name="my-slot" /> placeholder in your component.

That’s pretty concise and a bit of a head-scratcher to read, but basically what it says is that:

  1. Given a component that defines a slot:
  2. you can reference a slot from a child element of that component and,
  3. provide content to the parent component’s slot from the child by naming the slot in the child with a slot="<slot-name>" property assignment, where the slot-name is the parent’s slot.

So, in the example above, the CallToAction component defines the subtitle slot, and the following <Fragment slot=“subtitle”> populates the slot with the following content:

<Fragment slot="subtitle">
  Be very surprised by these huge fake numbers you are seeing on this page. <br class="hidden md:inline" />Don't waste
  more time! :P
</Fragment>

And, the CallToAction component defines and renders it thusly:

---
//...
const { subtitle = await Astro.slots.render('subtitle') } = Astro.props;
---

//...
{subtitle && <p class="text-xl text-muted dark:text-slate-400" set:html={subtitle} />}
//...

There’s a lot to wrap your head around, here.

Notice first that subtitle is defined as a prop/argument, but it’s being processed as a slot. Interestingly, prop args and slots seem to be somewhat interchangeable: if the subtitle was just a string, it would simply take that assignment. The main difference is that if you render them independently, you have to call the render with an await modifier.

¿Te sirvió este artículo?

Recibe nuevos artículos sobre desarrollo web, IA y SEO directo en tu correo. Sin spam, cancela cuando quieras.

Sin spam. Al suscribirte aceptas la política de privacidad .

Volver al blog

Related Posts

View All Posts »
Cómo reduje 88% el peso de las imágenes de mi sitio (Astro + Sharp y WebP)

Cómo reduje 88% el peso de las imágenes de mi sitio (Astro + Sharp y WebP)

Un sitio estático en Astro con 15KB de JavaScript estaba enviando 3.4MB de imágenes. Estos son los tres modos de fallo que se colaron por el pipeline de imágenes — la regla de que el original siempre se publica, la captura que el optimizador nunca tocó y el material de plantilla que nadie referenciaba — y las 15 líneas de Sharp que lo arreglaron todo.

Cómo Valido un Post de Astro Antes de Publicarlo

Cómo Valido un Post de Astro Antes de Publicarlo

Publicar un post estático en Astro está a un merge de estar en vivo — justo por eso los bugs silenciosos son peligrosos. Un lang incorrecto, un canonical que da 404, un link entre idiomas que se filtró: nada de eso se ve en tu editor, solo en el HTML compilado. Aquí está el checklist que corro sobre dist/ antes de cada post, scripts incluidos.

La Configuración de SEO en Astro que la Mayoría de los Portafolios Omite

La Configuración de SEO en Astro que la Mayoría de los Portafolios Omite

Un title tag y una meta description no son una configuración de SEO — son la parte que cualquier template ya te regala. La plomería que se acumula (JSON-LD, canonicals que no dan 404, hreflang que no se te revierte, un sitemap limpio) es lo que los portafolios omiten. Aquí está mi configuración completa de SEO en Astro, con código real incluido.

Migración de Astro 5 a 6: los 3 cambios que me rompieron el build

Migración de Astro 5 a 6: los 3 cambios que me rompieron el build

La migración de Astro 5 a 6 se veía como un bump de seguridad de diez minutos. En vez de eso choqué con tres builds rotos, uno tras otro — todos en código del theme que nunca escribí. Aquí están los errores exactos, los fixes, y por qué un sitio con theme se rompe donde tu propio código no se rompería.