Fix minor typos
This commit is contained in:
@ -11,142 +11,162 @@ tags:
|
|||||||
- front-end
|
- front-end
|
||||||
canonical: https://astrowind.vercel.app/astrowind-template-in-depth
|
canonical: https://astrowind.vercel.app/astrowind-template-in-depth
|
||||||
---
|
---
|
||||||
import DListItem from "../../components/widgets/DListItem.astro";
|
|
||||||
import ToggleTheme from "../../components/common/ToggleTheme.astro";
|
import DListItem from '../../components/widgets/DListItem.astro';
|
||||||
|
import ToggleTheme from '../../components/common/ToggleTheme.astro';
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
It can be a somewhat daunting task trying to get a handle on _AstroWind_ internals, and particularly various points of usage.
|
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.
|
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
|
## 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.
|
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/` ):
|
The styling mechanism consists of the following files (all paths are prefixed with `/src/` ):
|
||||||
|
|
||||||
<DListItem dt="assets/styles/base.css">
|
<DListItem dt="assets/styles/base.css">
|
||||||
This file is essentially an extension of _TailWind's_ base.css. High-level component styles are defined here.
|
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
|
||||||
Note also styling on elements selected by 'attribute' selectors at the bottom of the files, particularly those selected by 'data' attributes.
|
'data' attributes.
|
||||||
</DListItem>
|
</DListItem>
|
||||||
<DListItem dt="components/CustomStyles.astro">
|
<DListItem dt="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.
|
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.
|
||||||
</DListItem>
|
</DListItem>
|
||||||
<DListItem dt="components/common/MetaTags.astro">
|
<DListItem dt="components/common/MetaTags.astro">
|
||||||
Instantiates the _CustomStyles.astro_ component, along with other meta information (as the name implies). All of this is injected in to the header - see next.
|
Instantiates the _CustomStyles.astro_ component, along with other meta information (as the name implies). All of this
|
||||||
|
is injected in to the header - see next.
|
||||||
</DListItem>
|
</DListItem>
|
||||||
<DListItem dt="layouts/BaseLayout.astro">
|
<DListItem dt="layouts/BaseLayout.astro">
|
||||||
This layout is used for all of the pages rendered by _AstroWind_. The contents of _MetaTags.astro_ component, described above, is injected into the html header.
|
This layout is used for all of the pages rendered by _AstroWind_. The contents of _MetaTags.astro_ component,
|
||||||
|
described above, is injected into the html header.
|
||||||
</DListItem>
|
</DListItem>
|
||||||
|
|
||||||
### Dark Mode
|
### Dark Mode
|
||||||
|
|
||||||
_Dark Mode_ is triggered by the little 'sunlight' icon:<ToggleTheme/>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:
|
_Dark Mode_ is triggered by the little 'sunlight' icon:<ToggleTheme/>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:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
attachEvent('[data-aw-toggle-color-scheme]', 'click', function () {
|
attachEvent('[data-aw-toggle-color-scheme]', 'click', function () {
|
||||||
if (defaultTheme.endsWith(':only')) {
|
if (defaultTheme.endsWith(':only')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.documentElement.classList.toggle('dark');
|
document.documentElement.classList.toggle('dark');
|
||||||
localStorage.theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
|
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.
|
|
||||||
|
Note that this is a client event. _BasicScripts.astro_ defines several other client-side functionality as well as this one.
|
||||||
|
|
||||||
## Advanced Slot Usage
|
## 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:
|
_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:
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
// (file: MyComponent.astro)
|
---
|
||||||
---
|
// (file: MyComponent.astro)
|
||||||
const {title} = Astro.props;
|
const { title } = Astro.props;
|
||||||
interface Props = {
|
export interface Props {
|
||||||
title:string
|
title: string;
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
<div>
|
|
||||||
<h2>{title}</h2>
|
<div>
|
||||||
<slot/> <!-- slot contents injected here -->
|
<h2>{title}</h2>
|
||||||
<div>
|
<slot />
|
||||||
|
<!-- slot contents injected here -->
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
And in usage elsewhere:
|
And in usage elsewhere:
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
import MyComponent from "~/components/MyComponent";
|
import MyComponent from "~/components/MyComponent"; ...
|
||||||
...
|
<MyComponent someArg="A Slot example">
|
||||||
<MyComponent someArg="A Slot example">
|
<p>This content will be displayed in the slot</p>
|
||||||
<p>This content will be displayed in the slot</p>
|
</MyComponent>
|
||||||
</MyComponent>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Alternate usage
|
### Alternate usage
|
||||||
|
|
||||||
There's another way we can use slots, useful particularly when a component can have markdown content is as follows (study carefully...):
|
There's another way we can use slots, useful particularly when a component can have markdown content is as follows (study carefully...):
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
// (file: MyComponent.astro)
|
---
|
||||||
---
|
// (file: MyComponent.astro)
|
||||||
const {title} = Astro.props;
|
|
||||||
interface Props = {
|
|
||||||
title:string
|
|
||||||
}
|
|
||||||
|
|
||||||
const content:string = await Astro.props.render('default');
|
const { title } = Astro.props;
|
||||||
// renders the html to the 'content' variable
|
export interface Props {
|
||||||
---
|
title: string;
|
||||||
<div>
|
}
|
||||||
<h2>{title}</h2>
|
const content: string = await Astro.props.render('default');
|
||||||
<div set:html={content}/> <!-- slot contents injected here -->
|
---
|
||||||
<div>
|
|
||||||
|
// 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?
|
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).
|
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:
|
So, if the usage is in a markdown file, like so:
|
||||||
|
|
||||||
```mdx
|
```mdx
|
||||||
import MyComponent from '../../components/MyComponent';
|
import MyComponent from '../../components/MyComponent';
|
||||||
|
|
||||||
# Using the above component in a .mdx file (that can take components)
|
# 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 title="This is a slot implementor">### Here is some markdown content - With a bullet item.</MyComponent>
|
||||||
</MyComponent>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
_MyComponent_ renders the markdown to html and then injects it into the div.
|
_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 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.
|
This allows a great deal of flexibility in component design.
|
||||||
|
|
||||||
### Yet Another Step
|
### Yet Another Step
|
||||||
|
|
||||||
Now, we get to the techniques used in _AstroWind_, we'll use the _pages/index.astro_ file to illustrate.
|
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):
|
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):
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
<CallToAction
|
<CallToAction
|
||||||
callToAction={{
|
callToAction={{
|
||||||
text: 'Get template',
|
text: 'Get template',
|
||||||
href: 'https://github.com/onwidget/astrowind',
|
href: 'https://github.com/onwidget/astrowind',
|
||||||
icon: 'tabler:download',
|
icon: 'tabler:download',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Fragment slot="title">
|
<Fragment slot="title">
|
||||||
Astro + <br class="block sm:hidden" /><span class="sm:whitespace-nowrap">Tailwind CSS</span>
|
Astro + <br class="block sm:hidden" /><span class="sm:whitespace-nowrap">Tailwind CSS</span>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
||||||
<Fragment slot="subtitle">
|
<Fragment slot="subtitle">
|
||||||
Be very surprised by these huge fake numbers you are seeing on this page. <br class="hidden md:inline" />Don't
|
Be very surprised by these huge fake numbers you are seeing on this page. <br class="hidden md:inline" />Don't waste
|
||||||
waste more time! :P
|
more time! :P
|
||||||
</Fragment>
|
</Fragment>
|
||||||
</CallToAction>
|
</CallToAction>
|
||||||
```
|
```
|
||||||
|
|
||||||
Some things to note, here:
|
Some things to note, here:
|
||||||
|
|
||||||
<DListItem dt="The <em>callToAction</em> argument">
|
<DListItem dt="The <em>callToAction</em> argument">
|
||||||
This argument is actually being passed a javascript object -- not a string. (However, in the TS definition, it could be a string...)
|
This argument is actually being passed a javascript object -- not a string. (However, in the TS definition, it could
|
||||||
|
be a string...)
|
||||||
</DListItem>
|
</DListItem>
|
||||||
<DListItem dt="There are several <em>Fragment</em> children">
|
<DListItem dt="There are several <em>Fragment</em> children">
|
||||||
Furthermore, these <Fragment/> elements each have a _slot="(value)"_ specifier.
|
Furthermore, these <Fragment/> elements each have a _slot="(value)"_ specifier.
|
||||||
@ -159,38 +179,39 @@ 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.
|
> 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:
|
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:
|
|
||||||
1. you can reference a slot from a child element of that component and,
|
1. Given a component that defines a slot:
|
||||||
1. 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.
|
1. you can reference a slot from a child element of that component and,
|
||||||
|
1. 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:
|
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:
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
<Fragment slot="subtitle">
|
<Fragment slot="subtitle">
|
||||||
Be very surprised by these huge fake numbers you are seeing on this page. <br class="hidden md:inline" />Don't
|
Be very surprised by these huge fake numbers you are seeing on this page. <br class="hidden md:inline" />Don't waste
|
||||||
waste more time! :P
|
more time! :P
|
||||||
</Fragment>
|
</Fragment>
|
||||||
```
|
```
|
||||||
|
|
||||||
And, the _CallToAction_ component defines and renders it thusly:
|
And, the _CallToAction_ component defines and renders it thusly:
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
---
|
---
|
||||||
...
|
//...
|
||||||
const {
|
const { subtitle = await Astro.slots.render('subtitle') } = Astro.props;
|
||||||
subtitle = await Astro.slots.render('subtitle'),
|
|
||||||
} = Astro.props;
|
|
||||||
---
|
---
|
||||||
...
|
|
||||||
{subtitle && <p class="text-xl text-muted dark:text-slate-400" set:html={subtitle} />}
|
|
||||||
...
|
|
||||||
|
|
||||||
|
//...
|
||||||
|
{subtitle && <p class="text-xl text-muted dark:text-slate-400" set:html={subtitle} />}
|
||||||
|
//...
|
||||||
```
|
```
|
||||||
|
|
||||||
There's a lot to wrap your head around, here.
|
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.
|
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.
|
||||||
|
|
||||||
## CallToAction Property
|
## CallToAction Property
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="font-bold">Note:</td>
|
<td class="font-bold">Note:</td>
|
||||||
@ -198,52 +219,46 @@ Notice first that _subtitle_ is defined as a prop/argument, but it's being proce
|
|||||||
There's a _lot_ of duplicate (or at least very similar code) in the following components, especially as regards the _CallToAction_ mechanism -- adding to a lot of confusion and something of a maintenance headache:
|
There's a _lot_ of duplicate (or at least very similar code) in the following components, especially as regards the _CallToAction_ mechanism -- adding to a lot of confusion and something of a maintenance headache:
|
||||||
|
|
||||||
<div class="ml-8">
|
<div class="ml-8">
|
||||||
src/components/widgets/Hero.astro
|
src/components/widgets/Hero.astro src/components/widgets/Steps2.astro src/components/widgets/CallToAction.astro
|
||||||
src/components/widgets/Steps2.astro
|
src/components/widgets/Hero2.astro src/pages/landing/startup.astro
|
||||||
src/components/widgets/CallToAction.astro
|
|
||||||
src/components/widgets/Hero2.astro
|
|
||||||
src/pages/landing/startup.astro
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Seems like this could be cleaned up.
|
Seems like this could be cleaned up.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
The _CallToAction_ property can be of type `string | CallToAction` where the `CallToAction` is a typed object. In the component, this is handled thusly:
|
The _CallToAction_ property can be of type `string | CallToAction` where the `CallToAction` is a typed object. In the component, this is handled thusly:
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
---
|
---
|
||||||
const {
|
const { callToAction = await Astro.slots.render('callToAction') } = Astro.props;
|
||||||
callToAction = await Astro.slots.render('callToAction'),
|
// also rendered as a slot
|
||||||
} = Astro.props;
|
|
||||||
// also rendered as a slot
|
|
||||||
---
|
---
|
||||||
<div>
|
|
||||||
|
<div>
|
||||||
...
|
...
|
||||||
{
|
{
|
||||||
typeof callToAction === 'string' ? (
|
typeof callToAction === 'string' ? (
|
||||||
<Fragment set:html={callToAction} />
|
<Fragment set:html={callToAction} />
|
||||||
) : (
|
) : (
|
||||||
callToAction &&
|
callToAction &&
|
||||||
callToAction.text &&
|
callToAction.text &&
|
||||||
callToAction.href && (
|
callToAction.href && (
|
||||||
<div class="mt-6 max-w-xs mx-auto">
|
<div class="mt-6 max-w-xs mx-auto">
|
||||||
<a class="btn btn-primary w-full sm:w-auto" href={callToAction.href} target="_blank" rel="noopener">
|
<a class="btn btn-primary w-full sm:w-auto" href={callToAction.href} target="_blank" rel="noopener">
|
||||||
{callToAction.icon && <Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />}
|
{callToAction.icon && <Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />}
|
||||||
{callToAction.text}
|
{callToAction.text}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
</div>
|
||||||
</div>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
First, the property type is checked to see if it's a string: if so, it's just set into a child _<Fragment>'s_ html.
|
First, the property type is checked to see if it's a string: if so, it's just set into a child _<Fragment>'s_ html.
|
||||||
|
|
||||||
If not, it is validated as an object and then members of the object are assigned to various
|
If not, it is validated as an object and then members of the object are assigned to various
|
||||||
html elements.
|
html elements.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user