Initial commit

This commit is contained in:
Akilan Selvacoumar
2023-02-12 22:29:37 +00:00
committed by GitHub
commit ba5d6d24ef
99 changed files with 4904 additions and 0 deletions

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
# https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = false

4
.eslintignore Normal file
View File

@@ -0,0 +1,4 @@
dist
node_modules
.github
types.generated.d.ts

50
.eslintrc.js Normal file
View File

@@ -0,0 +1,50 @@
module.exports = {
env: {
node: true,
es2022: true,
browser: true,
},
extends: ['eslint:recommended', 'plugin:astro/recommended'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {},
overrides: [
{
files: ['*.js'],
rules: {
'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
},
},
{
files: ['*.astro'],
parser: 'astro-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
extraFileExtensions: ['.astro'],
},
rules: {
'no-mixed-spaces-and-tabs': ['error', 'smart-tabs'],
},
},
{
files: ['*.ts'],
parser: '@typescript-eslint/parser',
extends: ['plugin:@typescript-eslint/recommended'],
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', destructuredArrayIgnorePattern: '^_' },
],
'@typescript-eslint/no-non-null-assertion': 'off',
},
},
{
// Define the configuration for `<script>` tag.
// Script in `<script>` is assigned a virtual file name with the `.js` extension.
files: ['**/*.astro/*.js', '*.astro/*.js'],
parser: '@typescript-eslint/parser',
},
],
};

25
.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
# build output
dist/
.output/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
package-lock.json
pnpm-lock.yaml
.astro

2
.npmrc Normal file
View File

@@ -0,0 +1,2 @@
# Expose Astro dependencies for `pnpm` users
shamefully-hoist=true

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
dist
node_modules
.github
.changeset

13
.prettierrc.js Normal file
View File

@@ -0,0 +1,13 @@
/** @type {import('prettier').Config} */
module.exports = {
printWidth: 120,
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
useTabs: false,
plugins: [require.resolve('prettier-plugin-astro')],
overrides: [{ files: '*.astro', options: { parser: 'astro' } }],
};

6
.stackblitzrc Normal file
View File

@@ -0,0 +1,6 @@
{
"startCommand": "npm start",
"env": {
"ENABLE_CJS_IMPORTS": true
}
}

10
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"recommendations": [
"astro-build.astro-vscode",
"bradlc.vscode-tailwindcss",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"unifiedjs.vscode-mdx"
],
"unwantedRecommendations": []
}

11
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

17
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,17 @@
{
"css.customData": ["./vscode.tailwind.json"],
"eslint.validate": [
"javascript",
"javascriptreact",
"astro", // Enable .astro
"typescript", // Enable .ts
"typescriptreact" // Enable .tsx
],
"files.associations": {
"*.mdx": "markdown"
},
"prettier.documentSelectors": ["**/*.astro"],
"[astro]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

21
LICENSE.md Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 onWidget
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

258
README.md Normal file
View File

@@ -0,0 +1,258 @@
# 🚀 AstroWind
<img src="https://raw.githubusercontent.com/onwidget/.github/main/resources/astrowind/lighthouse-score.png" align="right"
alt="AstroWind Lighthouse Score" width="100" height="358">
🌟 _Most *starred* & *forked* Astro theme in 2022_. 🌟
**AstroWind** is a free and open-source template to make your website using **[Astro 2.0](https://astro.build/blog/astro-2/) + [Tailwind CSS](https://tailwindcss.com/)**. Ready to start a new project and designed taking into account web best practices.
## Features
- ✅ Integration with **Tailwind CSS** ([@astrojs/tailwind](https://docs.astro.build/en/guides/integrations-guide/tailwind/)) supporting **Dark mode**.
-**Production-ready** scores in [Lighthouse](https://web.dev/measure/) and [PageSpeed Insights](https://pagespeed.web.dev/) reports.
-**Fast and SEO friendly blog** with automatic **RSS feed** ([@astrojs/rss](https://docs.astro.build/en/guides/rss/)), [**MDX** support](https://docs.astro.build/en/guides/integrations-guide/mdx/), **Categories & Tags**, **Social Share**, ...
-**Image optimization** ([@astrojs/images](https://docs.astro.build/en/guides/integrations-guide/image/)) and **Font optimization**.
- ✅ Generation of **project sitemap** based on your routes ([@astrojs/sitemap](https://docs.astro.build/en/guides/integrations-guide/sitemap/)).
-**Open Graph tags** for social media sharing.
-**Analytics** built-in Google Analytics, and Splitbee integration.
<br>
<img src="https://raw.githubusercontent.com/onwidget/.github/main/resources/astrowind/screenshot-astro2.jpg" alt="AstroWind Theme Screenshot">
[![onWidget](https://custom-icon-badges.demolab.com/badge/made%20by%20-onWidget-556bf2?style=flat-square&logo=onwidget&logoColor=white&labelColor=101827)](https://onwidget.com)
[![License](https://img.shields.io/github/license/onwidget/astrowind?style=flat-square&color=dddddd&labelColor=000000)](https://github.com/onwidget/astrowind/blob/main/LICENSE.md)
[![Maintained](https://img.shields.io/badge/maintained%3F-yes-brightgreen.svg?style=flat-square)](https://github.com/onwidget)
[![Contributions Welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/onwidget/astrowind#contributing)
[![Known Vulnerabilities](https://snyk.io/test/github/onwidget/astrowind/badge.svg?style=flat-square)](https://snyk.io/test/github/onwidget/astrowind)
[![Stars](https://img.shields.io/github/stars/onwidget/astrowind.svg?style=social&label=stars&maxAge=86400&color=ff69b4)](https://github.com/onwidget/astrowind)
[![Forks](https://img.shields.io/github/forks/onwidget/astrowind.svg?style=social&label=forks&maxAge=86400&color=ff69b4)](https://github.com/onwidget/astrowind)
<br>
<details open>
<summary>Table of Contents</summary>
- [Demo](#demo)
- [Getting started](#getting-started)
- [Project structure](#project-structure)
- [Commands](#commands)
- [Configuration](#configuration)
- [Deploy](#deploy)
- [Frequently Asked Questions](#frequently-asked-questions)
- [Related Projects](#related-projects)
- [Contributing](#contributing)
- [Acknowledgements](#acknowledgements)
- [License](#license)
</details>
<br>
## Demo
📌 [https://astrowind.vercel.app/](https://astrowind.vercel.app/)
<br>
## Getting started
**AstroWind** tries to give you quick access to creating a website using [Astro 2.0](https://astro.build/blog/astro-2/) + [Tailwind CSS](https://tailwindcss.com/). It's a free theme focuses on simplicity, good practices and high performance.
Very little vanilla javascript is used only to provide basic functionality so that each developer decides which framework (React, Vue, Svelte, Solid JS...) to use and how to approach their goals..
### Project structure
Inside AstroWind template, you'll see the following folders and files:
```
/
├── public/
│ ├── robots.txt
│ └── favicon.ico
├── src/
│ ├── assets/
│ │ ├── images/
│ │ └── styles/
│ │ └── base.css
│ ├── components/
│ │ ├── blog/
│ │ ├── common/
│ │ ├── widgets/
│ │ │ ├── Header.astro
│ │ │ └── ...
│ │ ├── CustomStyles.astro
│ │ └── Logo.astro
│ ├── content/
│ │ ├── post/
│ │ │ ├── post-slug-1.md
│ │ │ ├── post-slug-2.mdx
│ │ │ └── ...
│ │ └-- config.ts
│ ├── layouts/
│ │ ├── BaseLayout.astro
│ │ └── ...
│ ├── pages/
│ │ ├── [...blog]/
│ │ │ ├── [category]/
│ │ │ ├── [tag]/
│ │ │ ├── [...page].astro
│ │ │ └── index.astro
│ │ ├── index.astro
│ │ ├── 404.astro
│ │ ├-- rss.xml.ts
│ │ └── ...
│ ├── utils/
│ ├── config.mjs
│ └── data.js
├── package.json
├── astro.config.mjs
└── ...
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory if they do not require any transformation or in the `assets/` directory if they are imported directly.
[![Edit AstroWind on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://githubbox.com/onwidget/astrowind/tree/main)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Update `config.mjs` and contents. Have fun!
<br>
### Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :-------------------- | :------------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run format` | Format codes with Prettier |
| `npm run lint:eslint` | Run Eslint |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
<br>
### Configuration
Basic configuration file: `./src/config.mjs`
```javascript
const CONFIG = {
name: 'Example',
origin: 'https://example.com',
basePathname: '/', // Change this if you need to deploy to Github Pages, for example
trailingSlash: false, // Generate permalinks with or without "/" at the end
title: 'Example - This is the homepage title of Example', // Default seo title
description: 'This is the homepage description of Example', // Default seo description
defaultImage: 'image.jpg', // Default seo image
defaultTheme: 'system', // Values: "system" | "light" | "dark" | "light:only" | "dark:only"
language: 'en', // Default language
textDirection: 'ltr', // Default html text direction
dateFormatter: new Intl.DateTimeFormat('en', {
// Date format
year: 'numeric',
month: 'short',
day: 'numeric',
timeZone: 'UTC',
}),
googleAnalyticsId: false, // Or "G-XXXXXXXXXX",
googleSiteVerificationId: false, // Or some value,
blog: {
disabled: false,
postsPerPage: 4,
post: {
permalink: '/%slug%', // variables: %slug%, %year%, %month%, %day%, %hour%, %minute%, %second%, %category%
noindex: false,
disabled: false,
},
list: {
pathname: 'blog', // Blog main path, you can change this to "articles" (/articles)
noindex: false,
disabled: false,
},
category: {
pathname: 'category', // Category main path /category/some-category
noindex: true,
disabled: false,
},
tag: {
pathname: 'tag', // Tag main path /tag/some-tag
noindex: true,
disabled: false,
},
},
};
```
<br>
### Deploy
#### Deploy to production (manual)
You can create an optimized production build with:
```shell
npm run build
```
Now, your website is ready to be deployed. All generated files are located at
`dist` folder, which you can deploy the folder to any hosting service you
prefer.
#### Deploy to Netlify
Clone this repository on own GitHub account and deploy to Netlify:
[![Netlify Deploy button](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/onwidget/astrowind)
#### Deploy to Vercel
Clone this repository on own GitHub account and deploy to Vercel:
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fonwidget%2Fastrowind)
<br>
## Frequently Asked Questions
- Why?
-
-
<br>
## Related projects
- [Qwind](https://qwind.pages.dev/) - A template to make your website using Qwik + Tailwind CSS.
## Contributing
If you have any idea, suggestions or find any bugs, feel free to open a discussion, an issue or create a pull request.
That would be very useful for all of us and we would be happy to listen and take action.
## Acknowledgements
Initially created by [onWidget](https://onwidget.com) and maintained by a community of [contributors](https://github.com/onwidget/astrowind/graphs/contributors).
## License
**AstroWind** is licensed under the MIT license — see the [LICENSE](./LICENSE.md) file for details.

70
astro.config.mjs Normal file
View File

@@ -0,0 +1,70 @@
import path from 'path';
import { fileURLToPath } from 'url';
import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import sitemap from '@astrojs/sitemap';
import image from '@astrojs/image';
import mdx from '@astrojs/mdx';
import partytown from '@astrojs/partytown';
import compress from 'astro-compress';
import { readingTimeRemarkPlugin } from './src/utils/frontmatter.mjs';
import { SITE } from './src/config.mjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const whenExternalScripts = (items = []) =>
SITE.googleAnalyticsId ? (Array.isArray(items) ? items.map((item) => item()) : [items()]) : [];
export default defineConfig({
site: SITE.origin,
base: SITE.basePathname,
trailingSlash: SITE.trailingSlash ? 'always' : 'never',
output: 'static',
markdown: {
remarkPlugins: [readingTimeRemarkPlugin],
},
integrations: [
tailwind({
config: {
applyBaseStyles: false,
},
}),
sitemap(),
image({
serviceEntryPoint: '@astrojs/image/sharp',
}),
mdx(),
...whenExternalScripts(() =>
partytown({
config: { forward: ['dataLayer.push'] },
})
),
compress({
css: true,
html: {
removeAttributeQuotes: false,
},
img: false,
js: true,
svg: false,
logger: 1,
}),
],
vite: {
resolve: {
alias: {
'~': path.resolve(__dirname, './src'),
},
},
},
});

7
netlify.toml Normal file
View File

@@ -0,0 +1,7 @@
[build]
publish = "dist"
command = "npm run build"
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"

49
package.json Normal file
View File

@@ -0,0 +1,49 @@
{
"name": "@onwidget/astrowind",
"description": "A template to make your website using Astro + Tailwind CSS.",
"version": "0.9.8",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro",
"format": "prettier -w .",
"lint:eslint": "eslint . --ext .js,.ts,.astro",
"subfont": "subfont -ir --no-fallbacks --silent --root dist"
},
"devDependencies": {
"@astrojs/image": "^0.14.1",
"@astrojs/mdx": "^0.16.0",
"@astrojs/partytown": "^1.0.3",
"@astrojs/rss": "^2.1.0",
"@astrojs/sitemap": "^1.0.1",
"@astrojs/tailwind": "^3.0.1",
"@astrolib/analytics": "^0.3.0",
"@astrolib/seo": "^0.3.0",
"@fontsource/inter": "^4.5.15",
"@tailwindcss/typography": "^0.5.9",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.51.0",
"astro": "^2.0.10",
"astro-compress": "^1.1.32",
"astro-icon": "^0.8.0",
"eslint": "^8.33.0",
"eslint-plugin-astro": "^0.23.0",
"eslint-plugin-jsx-a11y": "^6.7.1",
"limax": "2.1.0",
"mdast-util-to-string": "^3.1.1",
"prettier": "^2.8.4",
"prettier-plugin-astro": "^0.8.0",
"reading-time": "^1.5.0",
"sharp": "^0.31.3",
"subfont": "^6.12.4",
"svgo": "2.8.0",
"tailwindcss": "^3.2.6",
"typescript": "^4.9.5"
},
"engines": {
"node": ">=16.12.0"
}
}

2
public/_headers Normal file
View File

@@ -0,0 +1,2 @@
/_astro/*
Cache-Control: public, max-age=31536000, immutable

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

11
public/favicon.svg Normal file
View File

@@ -0,0 +1,11 @@
<svg width="256" height="256" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
#flame { fill: #FF5D01; }
#a { fill: #000014; }
@media (prefers-color-scheme: dark) {
#a { fill: #fff; }
}
</style>
<path id="a" fill-rule="evenodd" clip-rule="evenodd" d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z" />
<path id="flame" fill-rule="evenodd" clip-rule="evenodd" d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

2
public/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

11
sandbox.config.json Normal file
View File

@@ -0,0 +1,11 @@
{
"infiniteLoopProtection": true,
"hardReloadOnChange": false,
"view": "browser",
"template": "node",
"container": {
"port": 3000,
"startScript": "start",
"node": "16"
}
}

BIN
src/assets/images/caos.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

BIN
src/assets/images/hero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
src/assets/images/tools.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -0,0 +1,61 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.text-page {
color: var(--aw-color-text-page);
}
.text-muted {
color: var(--aw-color-text-muted);
}
.bg-light {
background-color: var(--aw-color-bg-page);
}
.bg-dark {
@apply bg-slate-900;
}
.btn {
@apply inline-flex items-center justify-center rounded-full shadow-md border-gray-400 border bg-transparent font-medium text-center text-base text-page leading-snug transition py-3.5 px-6 md:px-8 ease-in duration-200 focus:ring-blue-500 focus:ring-offset-blue-200 focus:ring-2 focus:ring-offset-2 hover:bg-gray-100 hover:border-gray-600 dark:text-slate-300 dark:border-slate-500 dark:hover:bg-slate-800 dark:hover:border-slate-800;
}
.btn-ghost {
@apply border-none shadow-none text-muted hover:text-gray-900 dark:text-gray-400 dark:hover:text-white;
}
.btn-primary {
@apply font-semibold bg-primary text-white border-primary hover:bg-blue-900 hover:border-blue-900 hover:text-white dark:text-white dark:bg-primary dark:border-primary dark:hover:border-blue-900 dark:hover:bg-blue-900;
}
}
#header.scroll {
@apply shadow-md md:shadow-lg bg-white md:bg-white/90 md:backdrop-blur-sm dark:bg-slate-900 dark:md:bg-slate-900/90;
}
.dropdown:hover .dropdown-menu {
display: block;
}
[astro-icon].icon-light > * {
stroke-width: 1.2;
}
[astro-icon].icon-bold > * {
stroke-width: 2.4;
}
[data-aw-toggle-menu] path {
@apply transition;
}
[data-aw-toggle-menu].expanded g > path:first-child {
@apply -rotate-45 translate-y-[15px] translate-x-[-3px];
}
[data-aw-toggle-menu].expanded g > path:last-child {
@apply rotate-45 translate-y-[-8px] translate-x-[14px];
}

View File

@@ -0,0 +1,21 @@
---
import '@fontsource/inter/variable.css';
// Nunito
// Dosis
---
<style is:inline is:global>
:root {
--aw-font-sans: 'InterVariable';
--aw-font-serif: var(--aw-font-sans);
--aw-font-heading: var(--aw-font-sans);
--aw-color-primary: rgb(30 64 175);
--aw-color-secondary: rgb(30 58 138);
--aw-color-accent: rgb(109 40 217);
--aw-color-text-page: rgb(17 24 39);
--aw-color-text-muted: rgb(75 85 99);
--aw-color-bg-page: rgb(255 255 255);
}
</style>

View File

@@ -0,0 +1,6 @@
---
---
<span class="self-center ml-2 text-2xl md:text-xl font-bold text-gray-900 whitespace-nowrap dark:text-white">
🚀 AstroWind</span
>

View File

@@ -0,0 +1,14 @@
---
import Item from '~/components/blog/GridItem.astro';
import type { Post } from '~/types';
export interface Props {
posts: Array<Post>;
}
const { posts } = Astro.props;
---
<div class="grid gap-6 row-gap-5 md:grid-cols-2 lg:grid-cols-4 -mb-6">
{posts.map((post) => <Item post={post} />)}
</div>

View File

@@ -0,0 +1,52 @@
---
import { Picture } from '@astrojs/image/components';
import { BLOG } from '~/config.mjs';
import type { Post } from '~/types';
import { findImage } from '~/utils/images';
import { getPermalink } from '~/utils/permalinks';
export interface Props {
post: Post;
}
const { post } = Astro.props;
const image = await findImage(post.image);
---
<article class="mb-6 transition">
<div class="relative md:h-64 bg-gray-400 dark:bg-slate-700 rounded shadow-lg mb-6">
{
image && (
<Picture
src={image}
class="md:object-cover w-full md:w-auto md:h-full rounded shadow-lg bg-gray-400 dark:bg-slate-700"
widths={[400, 900]}
width={400}
height={224}
sizes="(max-width: 900px) 400px, 900px"
alt={post.title}
aspectRatio="16:9"
loading="lazy"
decoding="async"
/>
)
}
</div>
<h3 class="mb-2 text-xl font-bold leading-tight sm:text-2xl font-heading">
{
BLOG?.post?.disabled ? (
post.title
) : (
<a
href={getPermalink(post.permalink, 'post')}
class="hover:text-primary dark:hover:text-blue-700 transition ease-in duration-200"
>
{post.title}
</a>
)
}
</h3>
<p class="text-muted dark:text-slate-400 text-lg">{post.excerpt || post.description}</p>
</article>

View File

@@ -0,0 +1,12 @@
---
const { title = await Astro.slots.render('default'), subtitle = await Astro.slots.render('subtitle') } = Astro.props;
---
<header class="mb-8 md:mb-16 text-center max-w-3xl mx-auto">
<h1 class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter font-heading" set:html={title} />
{
subtitle && (
<div class="mt-2 md:mt-3 mx-auto text-xl text-gray-500 dark:text-slate-400 font-medium" set:html={subtitle} />
)
}
</header>

View File

@@ -0,0 +1,53 @@
---
import Grid from '~/components/blog/Grid.astro';
import { getBlogPermalink } from '~/utils/permalinks';
import { findPostsByIds } from '~/utils/blog';
export interface Props {
title?: string;
allPostsText?: string;
allPostsLink?: string | URL;
information?: string;
postIds: string[];
}
const {
title = await Astro.slots.render('title'),
allPostsText = 'View all posts',
allPostsLink = getBlogPermalink(),
information = await Astro.slots.render('information'),
postIds = [],
} = Astro.props;
const posts = await findPostsByIds(postIds);
---
<section class="px-4 py-16 mx-auto max-w-7xl lg:py-20">
<div class="flex flex-col lg:justify-between lg:flex-row mb-8">
<div class="md:max-w-sm">
{
title && (
<h2
class="text-3xl font-bold tracking-tight sm:text-4xl sm:leading-none group font-heading mb-2"
set:html={title}
/>
)
}
{
allPostsText && allPostsLink && (
<a
class="text-muted dark:text-slate-400 hover:text-primary transition ease-in duration-200 block mb-6 md:mb-0"
href={allPostsLink}
>
{allPostsText} »
</a>
)
}
</div>
{information && <p class="text-muted dark:text-slate-400 lg:text-sm lg:max-w-md" set:html={information} />}
</div>
<Grid posts={posts} />
</section>

View File

@@ -0,0 +1,53 @@
---
import Grid from '~/components/blog/Grid.astro';
import { getBlogPermalink } from '~/utils/permalinks';
import { findLatestPosts } from '~/utils/blog';
export interface Props {
title?: string;
allPostsText?: string;
allPostsLink?: string | URL;
information?: string;
postIds: string[];
}
const {
title = await Astro.slots.render('title'),
allPostsText = 'View all posts',
allPostsLink = getBlogPermalink(),
information = await Astro.slots.render('information'),
count = 4,
} = Astro.props;
const posts = await findLatestPosts({ count });
---
<section class="px-4 py-16 mx-auto max-w-7xl lg:py-20">
<div class="flex flex-col lg:justify-between lg:flex-row mb-8">
<div class="md:max-w-sm">
{
title && (
<h2
class="text-3xl font-bold tracking-tight sm:text-4xl sm:leading-none group font-heading mb-2"
set:html={title}
/>
)
}
{
allPostsText && allPostsLink && (
<a
class="text-muted dark:text-slate-400 hover:text-primary transition ease-in duration-200 block mb-6 lg:mb-0"
href={allPostsLink}
>
{allPostsText} »
</a>
)
}
</div>
{information && <p class="text-muted dark:text-slate-400 lg:text-sm lg:max-w-md" set:html={information} />}
</div>
<Grid posts={posts} />
</section>

View File

@@ -0,0 +1,20 @@
---
import Item from '~/components/blog/ListItem.astro';
import type { Post } from '~/types';
export interface Props {
posts: Array<Post>;
}
const { posts } = Astro.props;
---
<ul>
{
posts.map((post) => (
<li class="mb-12 md:mb-20">
<Item post={post} />
</li>
))
}
</ul>

View File

@@ -0,0 +1,81 @@
---
import { Icon } from 'astro-icon';
import { Picture } from '@astrojs/image/components';
import PostTags from '~/components/blog/Tags.astro';
import { BLOG } from '~/config.mjs';
import type { Post } from '~/types';
import { getPermalink } from '~/utils/permalinks';
import { findImage } from '~/utils/images';
import { getFormattedDate } from '~/utils/utils';
export interface Props {
post: Post;
}
const { post } = Astro.props;
const image = await findImage(post.image);
const link = !BLOG?.post?.disabled ? getPermalink(post.permalink, 'post') : '';
---
<article class={`max-w-md mx-auto md:max-w-none grid gap-6 md:gap-8 ${image ? 'md:grid-cols-2' : ''}`}>
{
image && (
<a class="relative block group" href={link ?? 'javascript:void(0)'}>
<div class="relative h-0 pb-[56.25%] md:pb-[75%] md:h-72 lg:pb-[56.25%] overflow-hidden bg-gray-400 dark:bg-slate-700 rounded shadow-lg">
{image && (
<Picture
src={image}
class="absolute inset-0 object-cover w-full h-full mb-6 rounded shadow-lg bg-gray-400 dark:bg-slate-700"
widths={[400, 900]}
sizes="(max-width: 900px) 400px, 900px"
alt={post.title}
aspectRatio="16:9"
loading="lazy"
decoding="async"
/>
)}
</div>
</a>
)
}
<div class="mt-2">
<header>
<div class="mb-1">
<span class="text-sm">
<Icon name="tabler:clock" class="w-3.5 h-3.5 inline-block -mt-0.5 dark:text-gray-400" />
<time datetime={String(post.publishDate)}>{getFormattedDate(post.publishDate)}</time>
{
post.category && (
<>
{' '}
·{' '}
<a class="capitalize hover:underline" href={getPermalink(post.category, 'category')}>
{post.category.replaceAll('-', ' ')}
</a>
</>
)
}
</span>
</div>
<h2 class="text-xl sm:text-2xl font-bold leading-tight mb-2 font-heading dark:text-slate-300">
{
link ? (
<a class="hover:text-primary dark:hover:text-blue-700 transition ease-in duration-200" href={link}>
{post.title}
</a>
) : (
post.title
)
}
</h2>
</header>
{post.excerpt && <p class="flex-grow text-muted dark:text-slate-400 text-lg">{post.excerpt}</p>}
<footer class="mt-5">
<PostTags tags={post.tags} />
</footer>
</div>
</article>

View File

@@ -0,0 +1,34 @@
---
import { Icon } from 'astro-icon';
import { getPermalink } from '~/utils/permalinks';
export interface Props {
prevUrl: string;
nextUrl: string;
prevText?: string;
nextText?: string;
}
const { prevUrl, nextUrl, prevText = 'Newer posts', nextText = 'Older posts' } = Astro.props;
---
{
(prevUrl || nextUrl) && (
<div class="container flex">
<div class="flex flex-row mx-auto container justify-between">
<a href={getPermalink(prevUrl)} class={`btn btn-ghost md:px-3 px-3 mr-2 ${!prevUrl ? 'invisible' : ''}`}>
<div class="flex flex-row align-middle">
<Icon name="tabler:chevron-left" class="w-6 h-6" />
<p class="ml-2">{prevText}</p>
</div>
</a>
<a href={getPermalink(nextUrl)} class={`btn btn-ghost md:px-3 px-3 ${!nextUrl ? 'invisible' : ''}`}>
<div class="flex flex-row align-middle">
<span class="mr-2">{nextText}</span>
<Icon name="tabler:chevron-right" class="w-6 h-6" />
</div>
</a>
</div>
</div>
)
}

View File

@@ -0,0 +1,95 @@
---
import { Icon } from 'astro-icon';
import { Picture } from '@astrojs/image/components';
import PostTags from '~/components/blog/Tags.astro';
import SocialShare from '~/components/common/SocialShare.astro';
import { getPermalink } from '~/utils/permalinks';
import { getFormattedDate } from '~/utils/utils';
import type { Post } from '~/types';
export interface Props {
post: Post;
url: string | URL;
}
const { post, url } = Astro.props;
---
<section class="py-8 sm:py-16 lg:py-20 mx-auto">
<article>
<header class={post.image ? '' : ''}>
<div class="flex justify-between flex-col sm:flex-row max-w-3xl mx-auto mt-0 mb-2 px-4 sm:px-6 sm:items-center">
<p>
<Icon name="tabler:clock" class="w-4 h-4 inline-block -mt-0.5 dark:text-gray-400" />
<time datetime={String(post.publishDate)}>{getFormattedDate(post.publishDate)}</time>
{
post.category && (
<>
{' '}
·{' '}
<a class="capitalize hover:underline" href={getPermalink(post.category, 'category')}>
{post.category.replaceAll('-', ' ')}
</a>
</>
)
}
{post.readingTime && <> · {post.readingTime} min read</>}
</p>
</div>
<h1
class="px-4 sm:px-6 max-w-3xl mx-auto text-4xl md:text-5xl font-bold leading-tighter tracking-tighter font-heading"
>
{post.title}
</h1>
<p
class="max-w-3xl mx-auto mt-4 mb-8 px-4 sm:px-6 text-xl md:text-2xl text-muted dark:text-slate-400 text-justify"
>
{post.excerpt}
</p>
{
post.image ? (
<Picture
src={post.image}
class="max-w-full lg:max-w-6xl mx-auto mb-6 sm:rounded-md bg-gray-400 dark:bg-slate-700"
widths={[400, 900]}
sizes="(max-width: 900px) 400px, 900px"
alt={post.description || ''}
loading="eager"
aspectRatio={16 / 9}
width={900}
height={506}
loading="eager"
decoding="async"
background={undefined}
/>
) : (
<div class="max-w-3xl mx-auto px-4 sm:px-6">
<div class="border-t dark:border-slate-700" />
</div>
)
}
</header>
<div
class="mx-auto px-6 sm:px-6 max-w-3xl prose prose-lg lg:prose-xl dark:prose-invert dark:prose-headings:text-slate-300 prose-md prose-headings:font-heading prose-headings:leading-tighter prose-headings:tracking-tighter prose-headings:font-bold prose-a:text-primary dark:prose-a:text-blue-400 prose-img:rounded-md prose-img:shadow-lg mt-8"
>
{
post.Content ? (
<>
{/* @ts-ignore */}
<post.Content />
</>
) : (
<Fragment set:html={post.content} />
)
}
</div>
<div class="mx-auto px-6 sm:px-6 max-w-3xl mt-8 flex justify-between flex-col sm:flex-row">
<PostTags tags={post.tags} class="mr-5" />
<SocialShare url={url} text={post.title} class="mt-5 sm:mt-1 align-middle text-gray-500 dark:text-slate-600" />
</div>
</article>
</section>

View File

@@ -0,0 +1,34 @@
---
import { getPermalink } from '~/utils/permalinks';
import { BLOG } from '~/config.mjs';
import type { Post } from '~/types';
export interface Props {
tags: Post['tags'];
class?: string;
}
const { tags, class: className = 'text-sm' } = Astro.props;
---
{
tags && Array.isArray(tags) && (
<ul class={className}>
{tags.map((tag) => (
<li class="bg-gray-100 dark:bg-slate-700 inline-block mr-2 mb-2 py-0.5 px-2 lowercase font-medium">
{BLOG?.tag?.disabled ? (
tag
) : (
<a
href={getPermalink(tag, 'tag')}
class="text-muted dark:text-slate-300 hover:text-primary dark:hover:text-gray-200"
>
{tag}
</a>
)}
</li>
))}
</ul>
)
}

View File

@@ -0,0 +1,10 @@
---
import { Icon } from 'astro-icon';
import { getBlogPermalink } from '~/utils/permalinks';
---
<div class="mx-auto px-6 sm:px-6 max-w-3xl pt-8 md:pt-4 pb-12 md:pb-20">
<a class="btn btn-ghost px-3 md:px-3" href={getBlogPermalink()}
><Icon name="tabler:chevron-left" class="w-5 h-5 mr-1 -ml-1.5" /> Back to Blog</a
>
</div>

View File

@@ -0,0 +1,119 @@
---
import { SITE } from '~/config.mjs';
---
<script is:inline define:vars={{ defaultTheme: SITE.defaultTheme }}>
function applyTheme(theme) {
if (theme === 'dark') {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}
if ((defaultTheme && defaultTheme.endsWith(':only')) || (!localStorage.theme && defaultTheme !== 'system')) {
applyTheme(defaultTheme.replace(':only', ''));
} else if (
localStorage.theme === 'dark' ||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
applyTheme('dark');
} else {
applyTheme('light');
}
function attachEvent(selector, event, fn) {
const matches = typeof selector === 'string' ? document.querySelectorAll(selector) : selector;
if (matches && matches.length) {
matches.forEach((elem) => {
elem.addEventListener(event, (e) => fn(e, elem), false);
});
}
}
window.onload = function () {
let lastKnownScrollPosition = window.scrollY;
let ticking = true;
attachEvent('[data-aw-toggle-menu]', 'click', function (_, elem) {
elem.classList.toggle('expanded');
document.body.classList.toggle('overflow-hidden');
document.getElementById('header')?.classList.toggle('h-screen');
document.querySelector('#header nav')?.classList.toggle('hidden');
});
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';
});
attachEvent('[data-aw-social-share]', 'click', function (_, elem) {
const network = elem.getAttribute('data-aw-social-share');
const url = encodeURIComponent(elem.getAttribute('data-aw-url'));
const text = encodeURIComponent(elem.getAttribute('data-aw-text'));
let href;
switch (network) {
case 'facebook':
href = `https://www.facebook.com/sharer.php?u=${url}`;
break;
case 'twitter':
href = `https://twitter.com/intent/tweet?url=${url}&text=${text}`;
break;
case 'linkedin':
href = `https://www.linkedin.com/shareArticle?mini=true&url=${url}&title=${text}`;
break;
case 'whatsapp':
href = `https://wa.me/?text=${text}%20${url}`;
break;
case 'mail':
href = `mailto:?subject=%22${text}%22&body=${text}%20${url}`;
break;
default:
return;
}
const newlink = document.createElement('a');
newlink.target = '_blank';
newlink.href = href;
newlink.click();
});
function appyHeaderStylesOnScroll() {
const header = document.getElementById('header');
if (lastKnownScrollPosition > 60 && !header.classList.contains('scroll')) {
document.getElementById('header').classList.add('scroll');
} else if (lastKnownScrollPosition <= 60 && header.classList.contains('scroll')) {
document.getElementById('header').classList.remove('scroll');
}
ticking = false;
}
appyHeaderStylesOnScroll();
attachEvent([document], 'scroll', function () {
lastKnownScrollPosition = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(() => {
appyHeaderStylesOnScroll();
});
ticking = true;
}
});
};
window.onpageshow = function () {
document.documentElement.classList.add('motion-safe:scroll-smooth');
const elem = document.querySelector('[data-aw-toggle-menu]');
if (elem) {
elem.classList.remove('expanded');
}
document.body.classList.remove('overflow-hidden');
document.getElementById('header')?.classList.remove('h-screen');
document.querySelector('#header nav')?.classList.add('hidden');
};
</script>

View File

@@ -0,0 +1,97 @@
---
import { AstroSeo } from '@astrolib/seo';
import { GoogleAnalytics } from '@astrolib/analytics';
import { getImage } from '@astrojs/image';
import { SITE } from '~/config.mjs';
import { MetaSEO } from '~/types';
import { getCanonical, getAsset } from '~/utils/permalinks';
import { getRelativeUrlByFilePath } from '~/utils/directories';
import CustomStyles from '~/components/CustomStyles.astro';
import SplitbeeAnalytics from './SplitbeeAnalytics.astro';
export interface Props extends MetaSEO {
dontUseTitleTemplate?: boolean;
}
const defaultImage = SITE.defaultImage
? (
await getImage({
src: SITE.defaultImage,
alt: 'Default image',
width: 1200,
height: 628,
})
).src
: '';
const {
title = SITE.name,
description = '',
image: _image = defaultImage,
canonical = getCanonical(String(Astro.url.pathname)),
noindex = false,
nofollow = false,
ogTitle = title,
ogType = 'website',
dontUseTitleTemplate = false,
} = Astro.props;
const image =
typeof _image === 'string'
? new URL(_image, Astro.site)
: _image && typeof _image['src'] !== 'undefined'
? // @ts-ignore
new URL(getRelativeUrlByFilePath(_image.src), Astro.site)
: null;
---
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<AstroSeo
title={title}
titleTemplate={dontUseTitleTemplate ? '%s' : `%s — ${SITE.name}`}
description={description}
canonical={String(canonical)}
noindex={noindex}
nofollow={nofollow}
openGraph={{
url: String(canonical),
title: ogTitle,
description: description,
type: ogType,
images: image
? [
{
url: image.toString(),
alt: ogTitle,
},
]
: undefined,
// site_name: 'SiteName',
}}
twitter={{
// handle: '@handle',
// site: '@site',
cardType: image ? 'summary_large_image' : undefined,
}}
/>
<CustomStyles />
<!-- Google Site Verification -->
{SITE.googleSiteVerificationId && <meta name="google-site-verification" content={SITE.googleSiteVerificationId} />}
<!-- Google Analytics -->
{SITE.googleAnalyticsId && <GoogleAnalytics id={String(SITE.googleAnalyticsId)} partytown={true} />}
{SITE.splitbeeAnalytics?.enabled && <SplitbeeAnalytics {...SITE.splitbeeAnalytics} />}
<link rel="shortcut icon" href={getAsset('/favicon.ico')} />
<link rel="icon" type="image/svg+xml" href={getAsset('/favicon.svg')} />
<link rel="mask-icon" href={getAsset('/favicon.svg')} color="#8D46E7" />

View File

@@ -0,0 +1,45 @@
---
import { Icon } from 'astro-icon';
export interface Props {
text: string;
url: string | URL;
class?: string;
}
const { text, url, class: className = 'inline-block' } = Astro.props;
---
<div class={className}>
<span class="align-super font-bold text-gray-400 dark:text-slate-400">Share:</span>
<button class="ml-2" title="Twitter Share" data-aw-social-share="twitter" data-aw-url={url} data-aw-text={text}
><Icon
name="ri:twitter-fill"
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
/>
</button>
<button class="ml-2" title="Facebook Share" data-aw-social-share="facebook" data-aw-url={url}
><Icon
name="ri:facebook-box-fill"
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
/>
</button>
<button class="ml-2" title="Linkedin Share" data-aw-social-share="linkedin" data-aw-url={url} data-aw-text={text}
><Icon
name="ri:linkedin-box-fill"
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
/>
</button>
<button class="ml-2" title="Whatsapp Share" data-aw-social-share="whatsapp" data-aw-url={url} data-aw-text={text}
><Icon
name="ri:whatsapp-fill"
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
/>
</button>
<button class="ml-2" title="Email Share" data-aw-social-share="mail" data-aw-url={url} data-aw-text={text}
><Icon
name="ri:mail-fill"
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
/>
</button>
</div>

View File

@@ -0,0 +1,6 @@
---
const { doNotTrack = true, noCookieMode = false, url = 'https://cdn.splitbee.io/sb.js' } = Astro.props;
---
<!-- Splitbee Analytics -->
<script data-respect-dnt={doNotTrack} data-no-cookie={noCookieMode} async src={url}></script>

View File

@@ -0,0 +1,22 @@
---
import { Icon } from 'astro-icon';
export interface Props {
label?: string;
class?: string;
iconClass?: string;
iconName?: string;
}
const {
label = 'Toggle Menu',
class:
className = 'ml-1.5 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center transition',
iconClass = 'w-6 h-6',
iconName = 'tabler:menu',
} = Astro.props;
---
<button type="button" class={className} aria-label={label} data-aw-toggle-menu>
<Icon name={iconName} class={iconClass} optimize={false} />
</button>

View File

@@ -0,0 +1,28 @@
---
import { Icon } from 'astro-icon';
import { SITE } from '~/config.mjs';
export interface Props {
label?: string;
class?: string;
iconClass?: string;
iconName?: string;
}
const {
label = 'Toggle between Dark and Light mode',
class:
className = 'text-muted dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center',
iconClass = 'w-6 h-6',
iconName = 'tabler:sun',
} = Astro.props;
---
{
!(SITE?.defaultTheme && SITE.defaultTheme.endsWith(':only')) && (
<button type="button" class={className} aria-label={label} data-aw-toggle-color-scheme>
<Icon name={iconName} class={iconClass} />
</button>
)
}

View File

@@ -0,0 +1,27 @@
---
---
<div
class="hidden md:block bg-blue-900 dark:bg-slate-800 dark:border-slate-800 dark:text-slate-400 border-b border-blue-900 text-sm px-3 py-2 text-gray-200 overflow-hidden whitespace-nowrap text-ellipsis"
>
<span class="text-xs py-0.5 px-1 bg-primary dark:bg-slate-700 dark:text-slate-300 font-semibold">NEW</span>
<a
href="https://astro.build/blog/astro-2/"
class="hover:underline text-gray-200 dark:text-slate-400"
>Astro 2.0 is released. »</a
>
<a
target="_blank"
rel="noopener"
class="float-right"
title="If you like AstroWind, give us a star."
href="https://github.com/onwidget/astrowind"
>
<img
src="https://img.shields.io/github/stars/onwidget/astrowind.svg?style=social&label=Stars&maxAge=86400"
alt="Follow @onWidget"
width="84"
height="20"
/>
</a>
</div>

View File

@@ -0,0 +1,57 @@
---
import { Icon } from 'astro-icon';
interface CallToAction {
text: string;
href: string;
icon?: string;
}
export interface Props {
title?: string;
description?: string;
callToAction?: string | CallToAction;
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
callToAction = await Astro.slots.render('callToAction'),
} = Astro.props;
---
<section class="relative">
<div class="max-w-6xl mx-auto px-4 sm:px-6">
<div class="py-12 md:py-20">
<div
class="max-w-3xl mx-auto text-center p-6 rounded-md shadow-xl dark:shadow-none dark:border dark:border-slate-600"
>
{
title && (
<h2
class="text-4xl md:text-4xl font-bold leading-tighter tracking-tighter mb-4 font-heading"
set:html={title}
/>
)
}
{subtitle && <p class="text-xl text-muted dark:text-slate-400" set:html={subtitle} />}
{
typeof callToAction === 'string' ? (
<Fragment set:html={callToAction} />
) : (
callToAction &&
callToAction.text &&
callToAction.href && (
<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">
{callToAction.icon && <Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />}
{callToAction.text}
</a>
</div>
)
)
}
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,107 @@
---
import { Icon } from 'astro-icon';
import { Picture } from '@astrojs/image/components';
interface Item {
title: string;
description?: string;
icon?: string;
}
export interface Props {
title?: string;
subtitle?: string;
highlight?: string;
content?: string;
items?: Array<Item>;
image?: string | any; // TODO: find HTMLElementProps
isReversed?: boolean;
isAfterContent?: boolean;
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
highlight,
content = await Astro.slots.render('content'),
items = [],
image = await Astro.slots.render('image'),
isReversed = false,
isAfterContent = false,
} = Astro.props;
---
<section class:list={[{ 'pt-0 md:pt-0': isAfterContent }, 'bg-blue-50 dark:bg-slate-800 py-16 md:py-20']}>
<div class="max-w-xl sm:mx-auto lg:max-w-2xl">
{
(title || subtitle || highlight) && (
<div class="mb-10 md:mx-auto text-center md:mb-12 max-w-3xl">
{highlight && (
<p
class="text-base text-primary dark:text-blue-200 font-semibold tracking-wide uppercase"
set:html={highlight}
/>
)}
{title && (
<h2
class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading"
set:html={title}
/>
)}
{subtitle && (
<p class="max-w-3xl mx-auto sm:text-center text-xl text-muted dark:text-slate-400" set:html={subtitle} />
)}
</div>
)
}
</div>
<div class="mx-auto max-w-7xl p-4 md:px-8">
<div class={`md:flex ${isReversed ? 'md:flex-row-reverse' : ''} md:gap-16`}>
<div class="md:basis-1/2 self-center">
{content && <div class="mb-12 text-lg dark:text-slate-400" set:html={content} />}
{
items && (
<div class="space-y-8">
{items.map(({ title: title2, description, icon }) => (
<div class="flex">
<div class="flex-shrink-0">
<div class="flex h-7 w-7 items-center justify-center rounded-full bg-primary text-gray-50">
<Icon name={icon ? icon : 'tabler:check'} class="w-5 h-5" />
</div>
</div>
<div class="ml-4">
{title2 && <h3 class="text-lg font-medium leading-6 dark:text-white">{title2}</h3>}
{description && <p class="mt-2 text-muted dark:text-slate-400" set:html={description} />}
</div>
</div>
))}
</div>
)
}
</div>
<div aria-hidden="true" class="mt-10 md:mt-0 md:basis-1/2">
{
image && (
<div class="relative m-auto max-w-4xl">
{typeof image === 'string' ? (
<Fragment set:html={image} />
) : (
<Picture
class="mx-auto w-full rounded-lg bg-gray-500 shadow-lg"
width={500}
height={500}
widths={[400, 768]}
sizes="(max-width: 768px) 100vw, 432px"
aspectRatio="500:500"
{...image}
/>
)}
</div>
)
}
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,66 @@
---
import { Icon } from 'astro-icon';
interface Item {
question: string;
answer: string;
}
export interface Props {
title?: string;
subtitle?: string;
highlight?: string;
items: Array<Array<Item>>;
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
highlight,
items = [],
} = Astro.props;
---
<div class="px-4 py-16 mx-auto max-w-6xl lg:py-20">
<div class="max-w-xl sm:mx-auto lg:max-w-2xl">
{
(title || subtitle || highlight) && (
<div class="max-w-xl mb-10 md:mx-auto md:text-center lg:max-w-2xl md:mb-12">
{highlight && (
<p
class="text-base text-primary dark:text-blue-200 font-semibold tracking-wide uppercase"
set:html={highlight}
/>
)}
{title && (
<h2
class="max-w-lg mb-4 text-3xl font-bold leading-none md:tracking-tight sm:text-4xl md:mx-auto font-heading"
set:html={title}
/>
)}
{subtitle && <p class="max-w-3xl mx-auto text-xl text-muted dark:text-slate-400" set:html={subtitle} />}
</div>
)
}
</div>
<div class="max-w-7xl sm:mx-auto">
<div class="grid grid-cols-1 gap-x-8 gap-y-8 lg:gap-x-16 md:grid-cols-2">
{
items &&
items.map((subitems) => (
<div class="space-y-8">
{subitems.map(({ question, answer }) => (
<div>
<h3 class="mb-4 text-xl font-bold">
<Icon name="tabler:arrow-down-right" class="w-7 h-7 text-primary inline-block" />
{question}
</h3>
{answer && <div class="text-muted dark:text-gray-400" set:html={answer} />}
</div>
))}
</div>
))
}
</div>
</div>
</div>

View File

@@ -0,0 +1,71 @@
---
import { Icon } from 'astro-icon';
interface Item {
title: string;
description: string;
icon?: string;
}
export interface Props {
title?: string;
subtitle?: string;
highlight?: string;
items: Array<Array<Item>>;
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
highlight,
items = [],
} = Astro.props;
---
<section class="scroll-mt-16" id="features">
<div class="px-4 py-16 mx-auto max-w-6xl lg:px-8 lg:py-20">
{
(title || subtitle || highlight) && (
<div class="mb-10 md:mx-auto text-center md:mb-12 max-w-3xl">
{highlight && (
<p
class="text-base text-primary dark:text-blue-200 font-semibold tracking-wide uppercase"
set:html={highlight}
/>
)}
{title && (
<h2
class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading"
set:html={title}
/>
)}
{subtitle && (
<p class="max-w-3xl mx-auto sm:text-center text-xl text-muted dark:text-slate-400" set:html={subtitle} />
)}
</div>
)
}
<div class="grid mx-auto space-y-6 md:grid-cols-2 md:space-y-0">
{
items.map((subitems) => (
<div class="space-y-8 sm:px-8">
{subitems.map(({ title, description, icon }) => (
<div class="flex flex-row max-w-md">
<div class="mb-4 mr-4">
<div class="flex items-center justify-center w-12 h-12 rounded-full bg-primary dark:bg-blue-700">
{icon && <Icon name={icon} class="w-6 h-6 text-white icon-light" />}
</div>
</div>
<div>
<h3 class="mb-3 text-xl font-bold">{title}</h3>
<p class="text-muted dark:text-slate-400" set:html={description} />
</div>
</div>
))}
</div>
))
}
</div>
</div>
</section>

View File

@@ -0,0 +1,69 @@
---
import { Icon } from 'astro-icon';
interface Item {
title?: string;
description?: string;
icon?: string;
}
export interface Props {
title?: string;
subtitle?: string;
highlight?: string;
items: Array<Item>;
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
highlight,
items = [],
} = Astro.props;
---
<section class="relative">
<div class="absolute inset-0 bg-blue-50 dark:bg-slate-800 pointer-events-none mb-32" aria-hidden="true"></div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 -mb-12">
<div class="py-4 pt-8 sm:py-6 lg:py-8 lg:pt-12">
{
(title || subtitle || highlight) && (
<div class="mb-8 md:mx-auto text-center max-w-3xl">
{highlight && (
<p
class="text-base text-primary dark:text-blue-200 font-semibold tracking-wide uppercase"
set:html={highlight}
/>
)}
{title && (
<h2
class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading"
set:html={title}
/>
)}
{subtitle && (
<p
class="max-w-3xl mx-auto sm:text-center text-xl text-muted dark:text-slate-400"
set:html={subtitle}
/>
)}
</div>
)
}
<div class={`grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 my-12 dark:text-white items-stretch`}>
{
items.map(({ title, description, icon }) => (
<div class="relative flex flex-col p-6 bg-white dark:bg-slate-900 rounded shadow-lg hover:shadow-md transition border border-transparent dark:border-slate-800">
<div class="flex items-center">
<Icon name={icon} class="w-10 h-10" />
<div class="ml-4 text-xl font-bold">{title}</div>
</div>
{description && <p class="text-muted dark:text-gray-400 text-md mt-4" set:html={description} />}
</div>
))
}
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,99 @@
---
import { Icon } from 'astro-icon';
import { SITE } from '~/config.mjs';
import { getHomePermalink } from '~/utils/permalinks';
interface Link {
text?: string;
href?: string;
ariaLabel?: string;
icon?: string;
}
interface Links {
title?: string;
links: Array<Link>;
}
export interface Props {
links: Array<Links>;
secondaryLinks: Array<Link>;
socialLinks: Array<Link>;
footNote?: string;
theme?: string;
}
const { socialLinks = [], secondaryLinks = [], links = [], footNote = '', theme = 'light' } = Astro.props;
---
<footer class:list={[{ dark: theme === 'dark' }, 'relative border-t border-gray-200 dark:border-slate-800']}>
<div class="dark:bg-dark absolute inset-0 pointer-events-none" aria-hidden="true"></div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 dark:text-slate-300">
<div class="grid grid-cols-12 gap-4 gap-y-8 sm:gap-8 py-8 md:py-12">
<div class="col-span-12 lg:col-span-4">
<div class="mb-2">
<a class="inline-block font-bold text-xl" href={getHomePermalink()}>{SITE?.name}</a>
</div>
<div class="text-sm text-muted">
{
secondaryLinks.map(({ text, href }) => (
<a
class="text-muted hover:text-gray-700 dark:text-gray-400 hover:underline transition duration-150 ease-in-out mr-2"
href={href}
set:html={text}
/>
))
}
</div>
</div>
{
links.map(({ title, links }) => (
<div class="col-span-6 md:col-span-3 lg:col-span-2">
<div class="dark:text-gray-300 font-medium mb-2">{title}</div>
{links && Array.isArray(links) && links.length > 0 && (
<ul class="text-sm">
{links.map(({ text, href, ariaLabel }) => (
<li class="mb-2">
<a
class="text-muted hover:text-gray-700 hover:underline dark:text-gray-400 transition duration-150 ease-in-out"
href={href}
aria-label={ariaLabel}
>
<Fragment set:html={text} />
</a>
</li>
))}
</ul>
)}
</div>
))
}
</div>
<div class="md:flex md:items-center md:justify-between py-6 md:py-8">
{
socialLinks?.length ? (
<ul class="flex mb-4 md:order-1 -ml-2 md:ml-4 md:mb-0">
{socialLinks.map(({ ariaLabel, href, text, icon }) => (
<li>
<a
class="text-muted dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center"
aria-label={ariaLabel}
href={href}
>
{icon && <Icon name={icon} class="w-5 h-5" />}
<Fragment set:html={text} />
</a>
</li>
))}
</ul>
) : (
''
)
}
<div class="text-sm mr-4 dark:text-slate-400">
<Fragment set:html={footNote} />
</div>
</div>
</div>
</footer>

View File

@@ -0,0 +1,136 @@
---
import { Icon } from 'astro-icon';
import Logo from '~/components/Logo.astro';
import ToggleTheme from '~/components/common/ToggleTheme.astro';
import ToggleMenu from '~/components/common/ToggleMenu.astro';
import { getHomePermalink, getAsset } from '~/utils/permalinks';
interface Link {
text?: string;
href?: string;
ariaLabel?: string;
icon?: string;
}
interface ActionLink extends Link {
type?: string;
}
interface MenuLink extends Link {
links?: Array<Link>;
}
export interface Props {
links?: Array<MenuLink>;
actions?: Array<ActionLink>;
isSticky?: boolean;
showToggleTheme?: boolean;
showRssFeed?: boolean;
position?: string;
}
const {
links = [],
actions = [],
isSticky = false,
showToggleTheme = false,
showRssFeed = false,
position = 'center',
} = Astro.props;
---
<header
class:list={[
{ sticky: isSticky, relative: !isSticky },
'top-0 z-40 flex-none mx-auto w-full transition-all ease-in duration-100',
]}
id="header"
>
<div class="py-3 px-3 md:py-3.5 md:px-4 mx-auto w-full md:flex md:justify-between max-w-7xl">
<div class:list={[{"mr-auto": position === "right"}, "flex justify-between"]}>
<a class="flex items-center" href={getHomePermalink()}>
<Logo />
</a>
<div class="flex items-center md:hidden">
{showToggleTheme && <ToggleTheme />}
<ToggleMenu />
</div>
</div>
<nav
class="items-center w-full md:w-auto hidden md:flex dark:text-slate-200 h-[calc(100vh-72px)] md:h-auto overflow-y-auto md:overflow-visible md:mx-5"
aria-label="Main navigation"
>
<ul class="flex flex-col pt-8 md:pt-0 md:flex-row md:self-center w-full md:w-auto text-xl md:text-base">
{
links.map(({ text, href, links }) => (
<li class={links?.length ? 'dropdown' : ''}>
{links?.length ? (
<>
<button class="font-medium hover:text-gray-900 dark:hover:text-white px-4 py-3 flex items-center transition duration-150 ease-in-out">
{text} <Icon name="tabler:chevron-down" class="w-3.5 h-3.5 ml-0.5 hidden md:inline" />
</button>
<ul class="dropdown-menu md:backdrop-blur-md dark:md:bg-dark rounded md:absolute pl-4 md:pl-0 md:hidden font-medium md:bg-white/90 md:min-w-[200px] drop-shadow-xl">
{links.map(({ text: text2, href: href2 }) => (
<li>
<a
class="first:rounded-t last:rounded-b md:hover:bg-gray-200 dark:hover:bg-gray-700 py-2 px-5 block whitespace-no-wrap"
href={href2}
>
{text2}
</a>
</li>
))}
</ul>
</>
) : (
<a
class="font-medium hover:text-gray-900 dark:hover:text-white px-4 py-3 flex items-center transition duration-150 ease-in-out"
href={href}
>
{text}
</a>
)}
</li>
))
}
</ul>
</nav>
<div class:list={[{ 'ml-auto': position === 'left' }, 'md:self-center flex items-center md:mb-0']}>
<div class="hidden items-center md:flex">
{showToggleTheme && <ToggleTheme iconClass="w-5 h-5" />}
{
showRssFeed && (
<a
class="text-muted dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center"
aria-label="RSS Feed"
href={getAsset('/rss.xml')}
>
<Icon name="tabler:rss" class="w-5 h-5" />
</a>
)
}
{
actions?.length ? (
<span class="ml-4">
{actions.map(({ text, href, type }) => (
<a
class:list={[
'btn ml-2 py-2.5 px-5.5 md:px-6 font-semibold shadow-none text-sm',
{ 'btn-ghost': type === 'ghost', 'btn-primary': type === 'primary' },
]}
href={href}
>
<Fragment set:html={text} />
</a>
))}
</span>
) : (
''
)
}
</div>
</div>
</div>
</header>

View File

@@ -0,0 +1,110 @@
---
import { Icon } from 'astro-icon';
import { Picture } from '@astrojs/image/components';
interface CallToAction {
text: string;
href: string;
icon?: string;
}
export interface Props {
title?: string;
subtitle?: string;
callToAction?: string | CallToAction;
callToAction2?: string | CallToAction;
image?: string | any; // TODO: find HTMLElementProps
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
content = await Astro.slots.render('content'),
callToAction = await Astro.slots.render('callToAction'),
callToAction2 = await Astro.slots.render('callToAction2'),
image = await Astro.slots.render('image'),
} = Astro.props;
---
<section class="relative md:-mt-[76px]">
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"></div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6">
<div class="pt-0 md:pt-[76px] pointer-events-none"></div>
<div class="py-12 md:py-20">
<div class="text-center pb-10 md:pb-16 max-w-screen-lg mx-auto">
{
title && (
<h1
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200"
set:html={title}
/>
)
}
<div class="max-w-3xl mx-auto">
{subtitle && <p class="text-xl text-muted mb-6 dark:text-slate-300" set:html={subtitle} />}
<div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4">
{
callToAction && (
<div class="flex w-full sm:w-auto">
{typeof callToAction === 'string' ? (
<Fragment set:html={callToAction} />
) : (
<a class="btn btn-primary sm:mb-0 w-full" href={callToAction?.href} target="_blank" rel="noopener">
{callToAction?.icon && (
<>
<Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />{' '}
</>
)}
{callToAction?.text}
</a>
)}
</div>
)
}
{
callToAction2 && (
<div class="flex w-full sm:w-auto">
{typeof callToAction2 === 'string' ? (
<Fragment set:html={callToAction2} />
) : (
<a class="btn w-full" href={callToAction2?.href}>
{callToAction2?.icon && (
<>
<Icon name={callToAction2.icon} class="w-5 h-5 mr-1 -ml-1.5" />{' '}
</>
)}
{callToAction2.text}
</a>
)}
</div>
)
}
</div>
</div>
{content && <Fragment set:html={content} />}
</div>
<div>
{
image && (
<div class="relative m-auto max-w-5xl">
{typeof image === 'string' ? (
<Fragment set:html={image} />
) : (
<Picture
class="mx-auto rounded-md w-full"
widths={[400, 768, 1024, 2040]}
sizes="(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px"
aspectRatio={1024 / 576}
loading="eager"
width={1024}
height={576}
{...image}
/>
)}
</div>
)
}
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,110 @@
---
import { Icon } from 'astro-icon';
import { Picture } from '@astrojs/image/components';
interface CallToAction {
text: string;
href: string;
icon?: string;
}
export interface Props {
title?: string;
subtitle?: string;
callToAction?: string | CallToAction;
callToAction2?: string | CallToAction;
image?: string | any; // TODO: find HTMLElementProps
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
content = await Astro.slots.render('content'),
callToAction = await Astro.slots.render('callToAction'),
callToAction2 = await Astro.slots.render('callToAction2'),
image = await Astro.slots.render('image'),
} = Astro.props;
---
<section class="relative md:-mt-[76px]">
<div class="absolute inset-0 pointer-events-none" aria-hidden="true"></div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6">
<div class="pt-0 md:pt-[76px] pointer-events-none"></div>
<div class="py-12 md:py-20 lg:py-0 lg:flex lg:items-center lg:h-screen lg:gap-8">
<div class="basis-1/2 text-center lg:text-left pb-10 md:pb-16 mx-auto">
{
title && (
<h1
class="text-5xl md:text-6xl font-bold leading-tighter tracking-tighter mb-4 font-heading dark:text-gray-200"
set:html={title}
/>
)
}
<div class="max-w-3xl mx-auto lg:max-w-none">
{subtitle && <p class="text-xl text-muted mb-6 dark:text-slate-300" set:html={subtitle} />}
<div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4 lg:justify-start lg:m-0 lg:max-w-7xl">
{
callToAction && (
<div class="flex w-full sm:w-auto">
{typeof callToAction === 'string' ? (
<Fragment set:html={callToAction} />
) : (
<a class="btn btn-primary sm:mb-0 w-full" href={callToAction?.href} target="_blank" rel="noopener">
{callToAction?.icon && (
<>
<Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />{' '}
</>
)}
{callToAction?.text}
</a>
)}
</div>
)
}
{
callToAction2 && (
<div class="flex w-full sm:w-auto">
{typeof callToAction2 === 'string' ? (
<Fragment set:html={callToAction2} />
) : (
<a class="btn w-full" href={callToAction2?.href}>
{callToAction2?.icon && (
<>
<Icon name={callToAction2.icon} class="w-5 h-5 mr-1 -ml-1.5" />{' '}
</>
)}
{callToAction2.text}
</a>
)}
</div>
)
}
</div>
</div>
{content && <Fragment set:html={content} />}
</div>
<div class="basis-1/2">
{
image && (
<div class="relative m-auto max-w-5xl">
{typeof image === 'string' ? (
<Fragment set:html={image} />
) : (
<Picture
class="mx-auto rounded-md w-full"
widths={[400, 768, 1024, 2040]}
sizes="(max-width: 767px) 400px, (max-width: 1023px) 768px, (max-width: 2039px) 1024px, 2040px"
aspectRatio={600 / 600}
loading="eager"
width={600}
height={600}
{...image}
/>
)}
</div>
)
}
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,11 @@
---
import { Icon } from 'astro-icon';
---
<section class="bg-blue-50 dark:bg-slate-800">
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-4 text-md text-center font-medium">
<span class="font-bold">
<Icon name="tabler:info-square" class="w-5 h-5 inline-block align-text-bottom" /> Philosophy:</span
> Simplicity, Best Practices and High Performance
</div>
</section>

View File

@@ -0,0 +1,57 @@
---
interface Item {
name: string;
value: string;
}
export interface Props {
title?: string;
subtitle?: string;
highlight?: string;
items?: Array<Item>;
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
highlight,
items = [],
} = Astro.props;
---
<div class="px-4 py-4 md:py-16 sm:px-6 mx-auto md:px-24 lg:px-8 lg:py-20 max-w-6xl">
{
(title || subtitle || highlight) && (
<div class="max-w-xl mb-10 md:mx-auto sm:text-center lg:max-w-2xl md:mb-12">
{highlight && (
<p class="text-base text-primary dark:text-blue-200 font-semibold tracking-wide uppercase">
{highlight}
</p>
)}
{title && (
<h2
class="max-w-lg mb-4 text-3xl font-bold leading-none tracking-tight sm:text-4xl md:mx-auto font-heading"
set:html={title}
/>
)}
{subtitle && (
<p class="max-w-3xl mx-auto text-center text-xl text-muted dark:text-slate-400" set:html={subtitle} />
)}
</div>
)
}
<div class="grid grid-cols-2 row-gap-8 md:grid-cols-4">
{
items.map(({ name, value }) => (
<div class="text-center md:border-r md:last:border-none dark:md:border-slate-500 mb-12 md:mb-0">
<div class="text-[2.6rem] font-bold lg:text-5xl xl:text-6xl text-primary dark:text-blue-600 font-heading">
{value}
</div>
<p class="text-sm font-medium tracking-widest text-gray-800 dark:text-slate-400 uppercase lg:text-base">
{name}
</p>
</div>
))
}
</div>
</div>

View File

@@ -0,0 +1,74 @@
---
import { Icon } from 'astro-icon';
import { Picture } from '@astrojs/image/components';
interface Item {
title: string;
description?: string;
icon?: string;
}
export interface Props {
title?: string;
items: Array<Item>;
image?: string | any; // TODO: find HTMLElementProps
}
const {
title = await Astro.slots.render('title'),
items = [],
image = await Astro.slots.render('image'),
} = Astro.props;
---
<section class="px-4 py-16 sm:px-6 mx-auto lg:px-8 lg:py-20 max-w-6xl">
<div class="grid gap-6 row-gap-10 md:grid-cols-2">
<div class="md:py-4 md:pr-16 mb-4 md:mb-0">
{title && <h2 class="mb-8 text-3xl lg:text-4xl font-bold font-heading" set:html={title} />}
{
items &&
items.length &&
items.map(({ title, description, icon }, index) => (
<div class="flex">
<div class="flex flex-col items-center mr-4">
<div>
{index !== items.length - 1 ? (
<div class="flex items-center justify-center w-10 h-10 rounded-full border-primary dark:border-blue-700 border-2">
{icon && <Icon name={icon} class="w-6 h-6 text-primary dark:text-slate-200" />}
</div>
) : (
<div class="flex items-center justify-center w-10 h-10 rounded-full border-primary border-2 bg-primary dark:bg-blue-700 dark:border-blue-700">
<Icon name={icon} class="w-6 h-6 text-white dark:text-slate-200" />
</div>
)}
</div>
<div class="w-px h-full bg-gray-300 dark:bg-slate-500" />
</div>
<div class={`pt-1 ${index !== items.length - 1 ? 'pb-8' : ''}`}>
{title && <p class="mb-2 text-xl font-bold dark:text-slate-300" set:html={title} />}
{description && <p class="text-muted dark:text-slate-400" set:html={description} />}
</div>
</div>
))
}
</div>
<div class="relative">
{
image &&
(typeof image === 'string' ? (
<Fragment set:html={image} />
) : (
<Picture
class="inset-0 object-cover object-top w-full rounded-md shadow-lg md:absolute md:h-full bg-gray-400 dark:bg-slate-700"
widths={[400, 768]}
sizes="(max-width: 768px) 100vw, 432px"
aspectRatio="432:768"
width={432}
height={768}
{...image}
/>
))
}
</div>
</div>
</section>

View File

@@ -0,0 +1,127 @@
---
import { Icon } from 'astro-icon';
interface Item {
title: string;
description: string;
icon?: string;
}
interface CallToAction {
text: string;
href: string;
icon?: string;
}
export interface Props {
title?: string;
subtitle?: string;
highlight?: string;
callToAction?: string | CallToAction;
items: Array<Item>;
}
const {
title = await Astro.slots.render('title'),
subtitle = await Astro.slots.render('subtitle'),
highlight,
callToAction = await Astro.slots.render('callToAction'),
items = [],
} = Astro.props;
/**
*
<!-- Steps2 Widget Example ***************** -->
<Steps2
title="Sed ac magna sit amet risus tristique interdum, at vel velit in hac habitasse platea dictumst."
subtitle="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla, sed porttitor est nibh at nulla. Praesent placerat enim ut ex tincidunt vehicula. Fusce sit amet dui tellus."
callToAction={{
text: 'Get template',
href: 'https://github.com/onwidget/astrowind',
icon: 'tabler:download',
}}
items={[
{
title: 'Responsive Elements',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
},
{
title: 'Flexible Team',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
},
{
title: 'Ecologic Software',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
},
]}
/>
*/
---
<section>
<div class="max-w-7xl mx-auto px-4 sm:px-6 overflow-hidden">
<div class="py-12 md:py-20">
<div class="py-4 sm:py-6 lg:py-8">
<div class="flex flex-wrap md:-mx-8">
<div class="w-full lg:w-1/2 px-0 sm:px-8 mb-12">
<div>
{
highlight && (
<p
class="text-base text-primary dark:text-blue-200 font-semibold tracking-wide uppercase"
set:html={highlight}
/>
)
}
{title && <h2 class="mb-4 text-3xl lg:text-4xl font-bold font-heading" set:html={title} />}
{subtitle && <p class="mb-8 text-xl text-muted dark:text-slate-400" set:html={subtitle} />}
<div class="w-full">
{
typeof callToAction === 'string' ? (
<Fragment set:html={callToAction} />
) : (
callToAction &&
callToAction.text &&
callToAction.href && (
<a class="btn btn-primary mb-4 sm:mb-0" href={callToAction.href} target="_blank" rel="noopener">
{callToAction.icon && <Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />}
{callToAction.text}
</a>
)
)
}
</div>
</div>
</div>
<div class="w-full lg:w-1/2 px-0 sm:px-8">
<ul class="space-y-10">
{
items && items.length
? items.map(({ title: title2, description, icon }, index) => (
<li class="flex md:-mx-4">
<div class="pr-4 sm:pl-4">
<span class="flex w-16 h-16 mx-auto items-center justify-center text-2xl font-bold rounded-full bg-blue-100 text-primary">
{icon ? <Icon name={icon} class="w-6 h-6 icon-bold" /> : index + 1}
</span>
</div>
<div class="pl-4">
<h3 class="mb-4 text-xl font-semibold font-heading" set:html={title2} />
<p class="text-muted dark:text-gray-400" set:html={description} />
</div>
</li>
))
: ''
}
</ul>
</div>
</div>
</div>
</div>
</div>
</section>

62
src/config.mjs Normal file
View File

@@ -0,0 +1,62 @@
import defaultImage from './assets/images/default.png';
const CONFIG = {
name: 'AstroWind',
origin: 'https://astrowind.vercel.app',
basePathname: '/',
trailingSlash: false,
title: 'AstroWind — Free template for create a website with Astro + Tailwind CSS',
description:
'🚀 Suitable for Startups, Small Business, Sass Websites, Professional Portfolios, Marketing Websites, Landing Pages & Blogs.',
defaultImage: defaultImage,
defaultTheme: 'system', // Values: "system" | "light" | "dark" | "light:only" | "dark:only"
language: 'en',
textDirection: 'ltr',
dateFormatter: new Intl.DateTimeFormat('en', {
year: 'numeric',
month: 'short',
day: 'numeric',
timeZone: 'UTC',
}),
googleAnalyticsId: false, // or "G-XXXXXXXXXX",
googleSiteVerificationId: 'orcPxI47GSa-cRvY11tUe6iGg2IO_RPvnA1q95iEM3M',
blog: {
disabled: false,
postsPerPage: 4,
post: {
permalink: '/%slug%', // Variables: %slug%, %year%, %month%, %day%, %hour%, %minute%, %second%, %category%
noindex: false,
disabled: false,
},
list: {
pathname: 'blog', // Blog main path, you can change this to "articles" (/articles)
noindex: false,
disabled: false,
},
category: {
pathname: 'category', // Category main path /category/some-category
noindex: true,
disabled: false,
},
tag: {
pathname: 'tag', // Tag main path /tag/some-tag
noindex: true,
disabled: false,
},
},
};
export const SITE = { ...CONFIG, blog: undefined };
export const BLOG = CONFIG.blog;
export const DATE_FORMATTER = CONFIG.dateFormatter;

23
src/content/config.ts Normal file
View File

@@ -0,0 +1,23 @@
import { z, defineCollection } from 'astro:content';
const post = defineCollection({
schema: z.object({
title: z.string(),
description: z.string().optional(),
image: z.string().optional(),
canonical: z.string().url().optional(),
publishDate: z.date().or(z.string()).optional(),
draft: z.boolean().optional(),
excerpt: z.string().optional(),
category: z.string().optional(),
tags: z.array(z.string()).optional(),
author: z.string().optional(),
}),
});
export const collections = {
post: post,
};

View File

@@ -0,0 +1,49 @@
---
publishDate: 2023-01-08T00:00:00Z
title: AstroWind template in depth
description: Lorem ipsum dolor sit amet
excerpt: Ornare cum cursus laoreet sagittis nunc fusce posuere per euismod dis vehicula a, semper fames lacus maecenas
image: ~/assets/images/stickers.jpg
category: Tutorials
tags:
- astro
- tailwind css
- front-end
canonical: https://astrowind.vercel.app/astrowind-template-in-depth
---
## Dictum integer fusce ac ridiculus et odio sollicitudin diam at
Lorem ipsum dolor sit amet consectetur adipiscing elit euismod rutrum, consequat fringilla ultricies nullam curae mollis semper conubia viverra, orci aenean dapibus pharetra nec tortor tellus cubilia. Ullamcorper mi lectus eu malesuada tempor massa praesent magna mattis posuere, lobortis vulputate ut duis magnis parturient habitant nibh id tristique, quis suspendisse donec nisl penatibus sem non feugiat taciti. Mollis per ridiculus integer cursus semper vestibulum fermentum penatibus cubilia blandit scelerisque, tempus platea leo posuere ac pharetra volutpat aliquet euismod id ullamcorper lobortis, urna est magna mus rhoncus massa curae libero praesent eget. Mattis malesuada vestibulum quis ac nam phasellus suscipit facilisis libero diam posuere, cursus massa vehicula neque imperdiet tincidunt dui egestas lacinia mollis aliquet orci, nisl curabitur dapibus litora dis cum nostra montes ligula praesent. Facilisi aliquam convallis molestie tempor blandit ultricies bibendum parturient cubilia quam, porttitor morbi torquent tempus taciti nec faucibus elementum phasellus, quis inceptos vestibulum gravida augue potenti eget nunc maecenas. Tempor facilisis ligula volutpat habitant consequat inceptos orci per potenti blandit platea, mus sapien eget vel libero vestibulum augue cubilia ut ultrices fringilla lectus, imperdiet pellentesque cum ridiculus convallis sollicitudin nisl interdum semper felis.
Ornare cum cursus laoreet sagittis nunc fusce posuere per euismod dis vehicula a, semper fames lacus maecenas dictumst pulvinar neque enim non potenti. Torquent hac sociosqu eleifend potenti augue nulla vivamus senectus odio, quisque curabitur enim consequat class sociis feugiat ullamcorper, felis dis imperdiet cubilia commodo sed massa phasellus. Viverra purus mus nisi condimentum dui vehicula facilisis turpis, habitant nascetur lectus tempor quisque habitasse urna scelerisque, nibh nullam vestibulum luctus aenean mollis metus. Suscipit gravida duis nec aliquet natoque molestie a ridiculus scelerisque cum, justo cursus sapien sodales purus dignissim vel facilisi magnis, inceptos rutrum ut integer auctor commodo sollicitudin fames et. Faucibus ligula nibh sagittis mauris auctor posuere habitant, scelerisque phasellus accumsan egestas gravida viverra nam, sed etiam eleifend proin massa dictumst. Porttitor risus luctus per aenean tellus primis fringilla vitae fames lacinia mauris metus, nec pulvinar quisque commodo sodales ac nibh natoque phasellus semper placerat. Lectus aenean potenti leo sollicitudin tristique eros quam ligula, vestibulum diam consequat enim torquent nec tempus, blandit viverra dapibus eleifend dis nunc nascetur.
## Sodales hendrerit malesuada et vestibulum
- Luctus euismod pretium nisi et, est dui enim.
- Curae eget inceptos malesuada, fermentum class.
- Porttitor vestibulum aliquam porta feugiat velit, potenti eu placerat.
- Ligula lacus tempus ac porta, vel litora.
Torquent non nisi lacinia faucibus nibh tortor taciti commodo porttitor, mus hendrerit id leo scelerisque mollis habitasse orci tristique aptent, lacus at molestie cubilia facilisis porta accumsan condimentum. Metus lacus suscipit porttitor integer facilisi torquent, nostra nulla platea at natoque varius venenatis, id quam pharetra aliquam leo. Dictum orci himenaeos quam mi fusce lacinia maecenas ac magna eleifend laoreet, vivamus enim curabitur ullamcorper est ultrices convallis suscipit nascetur. Ornare fames pretium ante ac eget nisi tellus vivamus, convallis mauris sapien imperdiet sollicitudin aliquet taciti quam, lacinia tempor primis magna iaculis at eu. Est facilisi proin risus eleifend orci torquent ultricies platea, quisque nullam vel porttitor euismod sociis non, maecenas sociosqu interdum arcu sed pharetra potenti. Aliquet risus tempus hendrerit sapien tellus eget cursus enim etiam dui, lobortis nostra pellentesque odio posuere morbi ad neque senectus arcu eu, turpis proin ac felis purus fames magnis dis dignissim.
Orci volutpat augue viverra scelerisque dictumst ut condimentum vivamus, accumsan cum sem sollicitudin aliquet vehicula porta pretium placerat, malesuada euismod primis cubilia rutrum tempus parturient. Urna mauris in nibh morbi hendrerit vulputate condimentum, iaculis consequat porttitor dui dis euismod eros, arcu elementum venenatis varius lectus nisi. Nibh arcu ultrices semper morbi quam aptent quisque porta posuere iaculis, vestibulum cum vitae primis varius natoque conubia eu. Placerat sociis sagittis sociosqu morbi purus lobortis convallis, bibendum tortor ridiculus orci habitasse viverra dictum, quis rutrum fusce potenti volutpat vehicula. Curae porta inceptos lectus mus urna litora semper aliquam libero rutrum sem dui maecenas ligula quis, eget risus non imperdiet cum morbi magnis suspendisse etiam augue porttitor placerat facilisi hendrerit. Et eleifend eget augue duis fringilla sagittis erat est habitasse commodo tristique quisque pretium, suspendisse imperdiet inceptos mollis blandit magna mus elementum molestie sed vestibulum. Euismod morbi hendrerit suscipit felis ornare libero ligula, mus tortor urna interdum blandit nisi netus posuere, purus fermentum magnis nam primis nulla.
## Elementum nisi urna cursus nisl quam ante tristique blandit ultricies eget
Netus at rutrum taciti vestibulum molestie conubia semper class potenti lobortis, hendrerit donec vitae ad libero natoque parturient litora congue. Torquent rhoncus odio cursus iaculis molestie arcu leo condimentum accumsan, laoreet congue duis libero justo tortor commodo fusce, massa eros hac euismod netus sodales mi magnis. Aenean nullam sollicitudin ad velit nulla venenatis suspendisse iaculis, aliquet senectus mollis aptent fringilla volutpat nascetur, nec urna vehicula lacinia neque augue orci. Suspendisse et eleifend convallis sollicitudin posuere diam turpis gravida congue ultrices, laoreet ultricies dapibus proin facilisis magna class praesent fusce. Mus morbi magnis ultricies sed turpis ultrices tempus tortor bibendum, netus nulla viverra torquent malesuada ridiculus tempor. Parturient sociosqu erat ullamcorper gravida natoque varius, etiam habitant augue praesent per curabitur iaculis, donec pellentesque cursus suscipit aliquet. Congue curae cursus scelerisque pellentesque quis fusce arcu eros dictumst luctus ridiculus nisl viverra, turpis class faucibus phasellus feugiat eleifend fringilla orci tristique habitasse conubia quam. Habitasse montes congue sodales rutrum cras torquent cursus auctor condimentum imperdiet egestas nascetur, platea tincidunt ut sollicitudin purus libero lobortis ad nisi diam quam.
Venenatis suscipit class iaculis non velit ultrices ligula nulla mattis turpis erat, enim montes sapien rhoncus tincidunt scelerisque fermentum dapibus imperdiet risus, tempor est massa pretium at molestie morbi nec libero aptent. Morbi rhoncus massa accumsan a pharetra nec conubia at, sem justo sociis suspendisse aenean dis magna, parturient inceptos ad vestibulum lectus ullamcorper ante. In condimentum suscipit iaculis suspendisse nisl gravida risus sociis, cursus nec lectus per tristique phasellus imperdiet ultrices taciti, natoque sociosqu curae tellus hendrerit feugiat dignissim. Risus est fringilla elementum ullamcorper nibh urna sociosqu quis, netus eu mollis torquent ridiculus nisi et, sodales cum vulputate augue facilisis egestas vel. Ridiculus volutpat nisi netus venenatis vitae posuere purus, nec aliquam fusce nascetur mus iaculis, sociosqu sodales erat id tempor malesuada. Lacinia platea sagittis tincidunt semper nam magna praesent, ante ornare senectus in ligula justo, id rhoncus nullam nec maecenas aliquam. Enim aenean rutrum magnis magna duis pulvinar curae posuere massa, dui orci class dis phasellus parturient aliquet luctus eget bibendum, ultricies fringilla erat purus habitasse natoque urna aliquam.
Cursus varius volutpat aliquam tellus blandit netus orci, augue eleifend molestie cubilia proin. Sagittis enim nam hendrerit risus sem laoreet commodo interdum, odio fames et nunc bibendum urna conubia cursus neque, arcu accumsan nascetur ridiculus cubilia vestibulum sapien. Luctus maecenas accumsan turpis donec dictum justo ridiculus consequat ad, habitant elementum litora magna sed rutrum tempus. Et dapibus eget feugiat dictum quam proin sem tincidunt lectus risus natoque, massa ut purus sollicitudin dignissim hac sed nibh facilisis arcu dis lacinia, cras fringilla erat sociis eleifend varius vestibulum nunc aenean neque. Penatibus curabitur aptent magna faucibus aliquam sed massa curae maecenas nibh, sodales montes nulla cursus litora justo suscipit ut neque, varius rutrum enim dignissim auctor velit luctus blandit nostra. Torquent lobortis nec volutpat aliquet vestibulum penatibus bibendum, eget platea a dictum mollis congue. Netus consequat eget cursus cubilia nostra quam etiam sollicitudin purus, imperdiet per bibendum proin duis felis montes ut tincidunt semper, ad vestibulum accumsan tortor fames potenti tristique praesent. Arcu mollis tempus tincidunt ad platea mauris, nec inceptos dis penatibus donec, primis taciti fermentum erat mi.
Lacinia pellentesque dui porttitor arcu mauris turpis quam vitae rutrum in vel, sociosqu ultricies ultrices rhoncus fames taciti ut aliquet placerat ligula. Nec libero aptent nisl euismod pellentesque curae posuere magnis, fusce condimentum augue fames penatibus mollis consequat, justo ullamcorper semper nibh netus turpis est. Condimentum nisi bibendum fames placerat habitasse curabitur facilisis accumsan sagittis ante, etiam id turpis aliquam elementum habitant eget aptent nisl, duis nullam velit hac cubilia risus ultricies interdum ultrices. A praesent taciti duis tempor sollicitudin primis auctor, consequat potenti porta iaculis ad imperdiet, habitasse sagittis quam eu mus nisl. Lacus morbi aptent fusce augue curae elementum diam litora leo condimentum pharetra facilisi eros, dis bibendum primis habitasse tempus porttitor dictumst potenti justo congue hendrerit curabitur. Pretium natoque penatibus pellentesque auctor, luctus libero pharetra proin vitae, habitant nam posuere. Tortor facilisis sed venenatis nostra massa congue lobortis rutrum ornare, ullamcorper libero nibh elementum dictumst torquent felis at, nam porttitor curabitur neque natoque accumsan hac id. Dictum aenean sed facilisis interdum libero eu, praesent curae purus ac platea, natoque penatibus malesuada erat faucibus.
Porta lobortis ad dapibus id vitae convallis litora vehicula molestie mattis aliquam, sociosqu nisi ridiculus netus faucibus platea aptent diam vivamus. Taciti tristique in erat ridiculus ad ultricies mollis risus, laoreet aliquam semper felis nam cubilia praesent, tempus nostra augue penatibus convallis proin quis. Lacus commodo senectus tempus suspendisse suscipit, class porttitor mi potenti at, vitae mattis a pharetra. At cras felis fermentum turpis sapien fames volutpat sollicitudin integer egestas ornare nunc, praesent mollis nibh quisque tristique vitae curae lobortis lacinia donec sagittis. Tincidunt enim morbi etiam malesuada odio laoreet lacinia, phasellus fringilla integer inceptos lobortis ridiculus suscipit massa, turpis leo molestie a mus conubia. Ridiculus magna fermentum sodales interdum posuere mi risus per rhoncus donec orci, pulvinar curabitur facilisis curae accumsan ligula scelerisque porta a porttitor, dignissim parturient facilisi felis quis malesuada litora convallis cum integer. Fringilla pulvinar consequat suspendisse aenean mollis interdum odio viverra aptent, nascetur eget magna facilisi erat fermentum turpis taciti ornare molestie, nam sollicitudin natoque gravida porta nulla rutrum condimentum.
Bibendum leo etiam porta fermentum donec mauris netus nibh per non varius diam, massa aliquam elementum lacinia himenaeos sem eros platea justo neque placerat. Etiam morbi ac magna quis gravida mi molestie praesent potenti pharetra id ornare euismod congue, est sapien iaculis rhoncus platea posuere litora integer mattis lacinia cubilia rutrum sem. Facilisis est tempus ridiculus ultricies faucibus nec aenean praesent ad vel penatibus, mus hac bibendum pretium rhoncus nascetur non neque mollis curabitur lacinia, consequat ut tortor aptent et sapien ornare quisque suspendisse vitae. Et suscipit nulla mauris integer nam elementum massa, purus pellentesque placerat magnis scelerisque leo metus, sociis interdum tincidunt ac ullamcorper feugiat. Magnis inceptos justo tincidunt ad etiam conubia ultricies tortor, metus congue ullamcorper turpis bibendum sociosqu cubilia curae urna, in phasellus pellentesque quam platea imperdiet accumsan. Urna vivamus ut bibendum diam sociis euismod rutrum, consequat magnis primis curae donec quis placerat conubia, semper mattis mollis auctor fringilla erat. Suspendisse phasellus gravida mollis eleifend nascetur dictum lectus dui fringilla pulvinar feugiat, venenatis lobortis posuere dictumst porttitor bibendum ullamcorper montes ultrices congue, scelerisque lacinia viverra suscipit vel natoque inceptos dignissim habitant laoreet.
Nascetur gravida mus imperdiet dapibus suspendisse cras nisl conubia, vulputate placerat senectus viverra nibh dictum et morbi pulvinar, cubilia ad non eleifend sagittis scelerisque penatibus. Auctor rutrum arcu phasellus tellus et magnis in, aliquet sociis fermentum class praesent aliquam lectus curabitur, natoque id pulvinar hac ultricies augue. Himenaeos commodo cum donec metus curae convallis sociis, facilisi lectus torquent fermentum rutrum vivamus non platea, nisl venenatis id ullamcorper mauris at. Facilisis etiam pharetra porta hendrerit eu conubia aliquam malesuada senectus, dui sociis penatibus integer mus quis turpis. Venenatis placerat ultricies tincidunt morbi himenaeos metus, diam curae aliquet neque pellentesque justo lobortis, cras sodales nam imperdiet parturient. Ligula potenti fringilla platea feugiat pulvinar dis habitant, nec nisl diam orci consequat condimentum gravida accumsan, massa vehicula euismod faucibus dignissim et. Scelerisque natoque vel eu proin etiam nostra pulvinar ullamcorper convallis facilisi, viverra vitae nunc penatibus ligula magnis cum senectus habitasse at, sagittis rhoncus faucibus netus nisl facilisis tristique parturient nulla. Condimentum massa lectus volutpat mollis curae eu, curabitur feugiat conubia mauris commodo mattis, magnis ad nullam ac sapien.

View File

@@ -0,0 +1,50 @@
---
publishDate: 2023-01-12T00:00:00Z
title: Get started with AstroWind to create a website using Astro and Tailwind CSS
description: Lorem ipsum dolor sit amet
excerpt: Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur.
image: ~/assets/images/do-more.jpg
category: Tutorials
tags:
- astro
- tailwind css
canonical: https://astrowind.vercel.app/get-started-website-with-astro-tailwind-css
---
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
## Nostra torquent consequat volutpat aliquet neque
Lorem ipsum dolor sit amet consectetur adipiscing elit proin, aenean litora volutpat urna egestas magnis arcu non, cras ut cursus et sed morbi lectus. Integer faucibus sagittis eu nunc urna aliquet a laoreet torquent, suspendisse penatibus nulla sollicitudin congue rutrum dictum. Ornare mi habitasse fermentum phasellus dui et morbi litora sodales dictum id erat, nibh purus class ligula aenean lectus venenatis euismod cras torquent ac. Senectus sagittis conubia hendrerit at egestas porta venenatis nisi metus gravida tempor, aenean facilisis nisl ante facilisi lacus integer hac iaculis purus. Scelerisque libero torquent egestas curae tellus viverra inceptos imperdiet urna, porta suspendisse interdum primis odio morbi tempor commodo dictumst, suscipit ornare habitasse semper feugiat cras quisque lobortis.
Iaculis arcu commodo dis proin vitae himenaeos, ante tristique potenti magna ligula, sagittis libero fermentum ullamcorper sociis. Sem eros non arcu natoque fringilla lacus vestibulum lacinia integer mus viverra in proin, sagittis fusce tortor erat enim rutrum vulputate curae laoreet class diam. Inceptos convallis ac nisi natoque nam quisque magnis ut nullam fringilla curae, luctus lacus purus habitant erat magna molestie class habitasse metus, nibh lobortis tortor curabitur neque phasellus feugiat netus morbi parturient. Neque malesuada mauris justo himenaeos pharetra, ullamcorper enim ligula a nulla consequat, eget vivamus velit ridiculus.
## Praesent tellus ad sapien erat or
- Quam orci nostra mi nulla, hac a.
- Interdum iaculis quis tellus sociis orci nulla, quam rutrum conubia tortor primis.
- Non felis sem placerat aenean duis, ornare turpis nostra.
- Habitasse duis sociis sagittis cursus, ante dictumst commodo.
Duis maecenas massa habitasse inceptos imperdiet scelerisque at condimentum ultrices, nam dui leo enim taciti varius cras habitant pretium rhoncus, ut hac euismod nostra metus sagittis mi aenean. Quam eleifend aliquet litora eget a tempor, ultricies integer vestibulum non felis sodales, eros diam massa libero iaculis.
Nisl ligula ante magnis himenaeos pellentesque orci cras integer urna ut convallis, id phasellus libero est nunc ultrices eget blandit massa ac hac, morbi vulputate quisque tellus feugiat conubia luctus tincidunt curae fermentum. Venenatis dictumst tincidunt senectus vivamus duis dis sociis taciti porta primis, rhoncus ridiculus rutrum curae mattis ullamcorper ac sagittis nascetur curabitur erat, faucibus placerat vulputate eu at habitasse nulla nisl interdum. Varius turpis dignissim montes ac ante tristique quis parturient hendrerit faucibus, consequat auctor penatibus suspendisse rutrum erat nulla inceptos est justo, etiam mollis mauris facilisi cras sociosqu eu sapien sed.
Blandit aptent conubia mollis mauris habitasse suspendisse torquent aenean, ac primis auctor congue cursus mi posuere molestie, velit elementum per feugiat libero dictumst phasellus. Convallis mollis taciti condimentum praesent id porttitor ac dictumst at, sed in eu eleifend vehicula fermentum lectus litora venenatis, gravida hac molestie cum sociosqu mus viverra torquent. Congue est fusce habitasse ridiculus integer suscipit platea volutpat, inceptos varius elementum pellentesque malesuada interdum magnis. Hac lacus eget enim purus massa commodo nec lectus natoque fames arcu, mattis class quam ut neque dui cras quis diam orci sed velit, erat morbi eros suscipit sagittis laoreet vivamus torquent nulla turpis.
Ridiculus velit suscipit consequat auctor interdum magna gravida dictumst libero ut habitasse, sollicitudin vehicula suspendisse leo erat tristique at platea sagittis proin dignissim, id ornare scelerisque et urna maecenas congue tincidunt dictum malesuada. Dui vulputate accumsan scelerisque ridiculus dictum quisque et nam hac, tempus ultricies curabitur proin netus diam vivamus. Vestibulum ante ac auctor mi urna risus lacinia vulputate justo orci sociis dui semper, commodo morbi enim vivamus neque sem pellentesque velit donec hac metus odio. Tempor ultrices himenaeos massa sollicitudin mus conubia scelerisque cubilia, nascetur potenti mauris convallis et lectus gravida egestas sociis, erat eros ultricies aptent congue tortor ornare.
Pretium aliquet sodales aliquam tincidunt litora lectus, erat dui nibh diam mus, sed hendrerit condimentum senectus arcu. Arcu a nibh auctor dapibus eros turpis tempus commodo, libero hendrerit dictum interdum mus class sed scelerisque, sapien dictumst enim magna molestie habitant donec. Fringilla dui sed curabitur commodo varius est vel, viverra primis habitant sapien montes mattis dignissim, gravida cubilia laoreet tempus aliquet senectus. Sociosqu purus praesent porttitor curae sollicitudin accumsan feugiat maecenas donec quis lacus, suscipit taciti convallis odio morbi eros nibh bibendum nunc orci. Magna cras nullam aliquam metus nibh sagittis facilisi tortor nec, mus varius curae ridiculus fames congue interdum erat urna, neque odio lobortis mi mattis diam cubilia arcu.
Laoreet fusce nec class porttitor mus proin aenean, velit vestibulum feugiat porta egestas sapien posuere, conubia nisi tempus varius hendrerit tortor. Congue aliquam scelerisque neque vivamus habitasse semper mauris pellentesque accumsan posuere, suspendisse lectus gravida erat sagittis arcu praesent mus ornare. Habitasse nibh nam morbi mollis senectus erat risus, cum sollicitudin class platea congue mattis venenatis, luctus aenean parturient hendrerit malesuada ante. Mus auctor tincidunt consequat massa tortor nulla luctus habitasse vestibulum quis velit, laoreet sagittis cum facilisi in sem tellus leo vulputate vehicula bibendum orci, felis nisl blandit lacus convallis congue turpis magna facilisis condimentum.
Dictumst pellentesque urna donec sociis suscipit montes consequat, commodo quam habitasse senectus fringilla maecenas, inceptos magna tristique eu nullam nam. Maecenas orci nibh hac eu tristique ut penatibus ultrices ante, pellentesque cubilia pharetra dis facilisis aliquam praesent malesuada vivamus, commodo cras velit convallis molestie nec tellus augue. Etiam ut convallis risus id dapibus platea laoreet accumsan, habitant et aenean netus inceptos iaculis per, mauris curae at ligula odio ad eu. Mauris erat tempor interdum sapien commodo per nullam tortor, fusce facilisis vehicula egestas dui nulla conubia ut fames, fringilla et tincidunt penatibus facilisi at mollis.
Fermentum sociosqu litora primis sollicitudin fusce diam consequat vehicula per lobortis et, viverra sodales magna rutrum sed mollis faucibus molestie purus montes est, risus nostra congue venenatis lectus enim torquent eros dis dapibus. Dui suscipit scelerisque massa ligula euismod accumsan augue, magna vel lacus ante nullam senectus commodo, viverra cubilia eros eget penatibus tempor. Mattis mauris hac felis semper dui sociis faucibus mollis ornare pretium aliquam velit nisl, quis litora sem at vel duis rutrum imperdiet natoque viverra himenaeos tempor.
Integer eu tristique purus luctus vivamus porttitor vel nisl, tortor malesuada augue vulputate diam velit pellentesque sodales, duis phasellus vestibulum fermentum leo facilisi porta. Hac porttitor cum dapibus volutpat quisque odio taciti nulla senectus mollis curae, accumsan suscipit cubilia tempor ligula in venenatis justo leo erat, magna tincidunt nullam lacinia luctus malesuada non vivamus praesent pharetra. Non quam felis montes pretium volutpat suspendisse lacus, torquent magna dictumst orci libero porta, feugiat taciti cras ridiculus aenean rutrum. Tellus nostra tincidunt hac in ligula mi vulputate venenatis pellentesque urna dui, at luctus tristique quisque vel a dignissim scelerisque platea pretium, suspendisse ante phasellus porttitor quis aliquam malesuada etiam enim nullam.
Hendrerit taciti litora nec facilisis diam vehicula magnis potenti, parturient velit egestas nisl lobortis tincidunt rutrum cursus, fusce senectus mi massa primis mattis rhoncus. Accumsan est ac varius consequat vulputate, ligula cursus euismod sagittis inceptos scelerisque, lacus malesuada torquent dictumst. Volutpat morbi metus urna rhoncus nunc tempor molestie, congue curabitur quis interdum posuere. Mollis viverra velit tortor mus netus nunc molestie metus, sem massa himenaeos luctus feugiat taciti iaculis fames porttitor, leo arcu consequat gravida dapibus pulvinar elementum.

View File

@@ -0,0 +1,63 @@
---
publishDate: 2023-01-06T00:00:00Z
title: How to customize AstroWind template to suit your branding
excerpt: Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur.
image: ~/assets/images/colors.jpg
tags:
- astro
- tailwind css
- theme
canonical: https://astrowind.vercel.app/how-to-customize-astrowind-to-your-brand
---
## Congue justo vulputate nascetur convallis varius orci fringilla nulla pharetr
Lorem ipsum dolor sit amet consectetur adipiscing elit, augue malesuada natoque in ad erat aliquam facilisi, lacus rhoncus mattis nostra et a. Mauris malesuada rutrum dis libero egestas mus vulputate, fermentum ad morbi phasellus faucibus tellus leo urna, blandit ullamcorper diam imperdiet dictumst litora. Fringilla eros malesuada lobortis mi odio metus leo, blandit imperdiet augue fames aliquam ultricies tortor massa, duis magnis hendrerit id magna sociosqu. Aptent mi imperdiet id sapien suscipit ut netus turpis, lacinia ac porttitor potenti dui taciti at egestas, fermentum neque nascetur sodales tortor nunc congue.
Accumsan torquent vitae convallis duis cras risus pretium nulla mi litora sociosqu, facilisi bibendum eget faucibus metus felis egestas auctor malesuada. Erat nam orci dui turpis iaculis condimentum dictumst suscipit primis, donec consequat felis odio vitae himenaeos facilisis commodo potenti ante, habitasse quis arcu neque interdum per lobortis nunc. Ultricies lobortis ullamcorper sagittis et sollicitudin sociis sed dignissim posuere, nisi pharetra erat varius id aenean lacinia commodo morbi primis, ornare diam proin nunc volutpat nec dui egestas.
## Mauris velit laoreet vitae cursus augue
- Massa egestas consequat nisl id volutpat, varius neque aenean.
- Venenatis tincidunt eros pretium viverra lacinia convallis, turpis orci condimentum fusce.
- Pellentesque in aliquet nisi gravida netus, commodo aptent volutpat.
- Nisi rutrum eros euismod, parturient ullamcorper mattis a, dapibus vestibulum.
Senectus fermentum tristique egestas bibendum per dictumst purus pharetra cras dictum pulvinar, vitae nec eros montes dis quis nullam duis netus litora, feugiat cubilia mollis porttitor velit ligula metus ante risus eu.
Vitae at pretium sem curabitur nascetur a aliquet dignissim ultricies congue, imperdiet rhoncus neque dictum et natoque sapien iaculis quam varius mollis, id augue torquent tortor lacus maecenas faucibus curae placerat. Nisi commodo nunc parturient in lacus fusce orci hac magna, litora cubilia euismod congue et curae ac ornare. Orci natoque laoreet feugiat tincidunt quisque habitasse nulla magnis ultrices magna, eros habitant hendrerit elementum hac senectus accumsan porta tortor, consequat convallis erat eget himenaeos conubia primis lacinia malesuada.
Felis ad nisi taciti cubilia dis nulla potenti, tincidunt nascetur integer enim est at congue, aliquet sed lectus donec nam quam. Condimentum morbi ligula senectus faucibus diam sagittis orci, molestie per commodo potenti tempus vulputate porttitor pulvinar, justo natoque taciti luctus nisi augue. Ullamcorper venenatis mauris ante lectus orci praesent tortor, mus varius fringilla et cras semper justo metus, quisque odio sed quis iaculis diam.
Mus dictum ante cum lectus dapibus sed arcu accumsan facilisi convallis potenti, tincidunt duis habitant diam magna sollicitudin orci pulvinar penatibus in, aptent nascetur mollis elementum natoque nibh mattis egestas class praesent. Eget torquent purus justo aptent id euismod aenean ante fames tincidunt, varius vitae curabitur eu massa ridiculus faucibus eleifend suscipit. Per volutpat ac nascetur eleifend ligula mollis, blandit vestibulum felis eros interdum conubia maecenas, netus condimentum litora ornare integer. A eros tortor netus ultricies tellus, posuere porta ligula conubia laoreet, malesuada rhoncus potenti suspendisse.
Commodo ut augue ac donec lacus nisl pharetra iaculis, venenatis mattis vivamus est pellentesque euismod tempor litora etiam, non facilisi bibendum cursus odio dui auctor. Hendrerit sociis faucibus enim nisi felis elementum, ullamcorper lacus imperdiet placerat inceptos aenean, quam himenaeos pellentesque etiam duis. Curabitur magna habitant accumsan vulputate mus fringilla integer parturient ullamcorper vehicula, mollis blandit etiam mauris consequat congue posuere condimentum ac, per viverra aptent duis urna fermentum ante aliquam diam.
Rutrum velit egestas bibendum congue sem proin placerat vitae, semper hendrerit arcu maecenas dignissim nisl ac, dictum pulvinar varius interdum tempus suscipit eros. Ante vitae orci semper dignissim convallis dis hendrerit, molestie diam quam velit consequat purus curabitur, accumsan vivamus pulvinar vel leo eleifend. Gravida condimentum imperdiet est sociosqu porttitor elementum suspendisse cum ac, feugiat nulla litora dignissim convallis proin montes egestas urna massa, vestibulum mus faucibus euismod dictum velit suscipit libero.
Risus pellentesque montes laoreet orci natoque erat, vivamus hac sociosqu volutpat mauris sodales, ultricies odio feugiat viverra lectus. Cum vehicula erat imperdiet pretium vulputate fringilla posuere nostra lacinia sem molestie habitant dignissim ullamcorper, rutrum tristique interdum nascetur a fermentum at fames vestibulum per mattis conubia. Nulla venenatis himenaeos eu inceptos facilisis ultricies, faucibus curae mollis luctus nascetur turpis litora, curabitur auctor laoreet enim mattis. Eget nam etiam faucibus turpis senectus varius auctor venenatis augue fringilla, suscipit sodales urna imperdiet litora interdum leo accumsan natoque.
Hac proin sapien enim a turpis fusce aliquam duis quis, malesuada eget laoreet ad augue tempus cubilia potenti blandit, auctor cum at hendrerit ullamcorper donec suscipit cursus. Ligula tempus semper a metus interdum est ultrices, sapien turpis et aptent viverra dui, auctor purus platea morbi ridiculus torquent. Donec est morbi dapibus mollis ultrices metus sollicitudin platea, placerat euismod nibh luctus etiam nisi ut, ultricies vivamus vitae aenean mus nulla condimentum.
Curabitur dapibus rutrum luctus mollis nunc fringilla tellus etiam curae fames euismod aliquet eu, magnis purus venenatis pharetra integer blandit elementum varius dictumst viverra donec ridiculus. Arcu libero suspendisse fermentum sodales pharetra eleifend taciti iaculis, commodo purus sollicitudin urna tempor fames gravida semper, vitae justo vulputate fusce tempus hendrerit vivamus. Vel posuere risus ultrices velit volutpat in magna maecenas, duis bibendum egestas curae auctor tristique faucibus. Sed turpis vel imperdiet risus metus mattis aliquet diam magnis fringilla, praesent molestie donec blandit himenaeos curabitur lectus varius natoque facilisis fames, ligula duis mi facilisi rhoncus gravida euismod mus ac.
Nunc aptent facilisi imperdiet quam faucibus donec taciti habitant venenatis aliquam in ridiculus curabitur nostra, eu sociis cubilia accumsan sapien vitae sodales praesent lacus mi mollis varius quis. Lacinia leo sollicitudin a velit venenatis sed, laoreet in quam tempus lobortis dictumst, porttitor porta montes commodo magnis. Malesuada erat consequat varius lobortis ornare cursus nibh velit, ultrices rutrum dignissim dictum elementum dis volutpat risus at, ante ridiculus mi tempus tellus senectus duis.
Donec dapibus est aliquam cum dictum potenti diam, fusce himenaeos molestie phasellus massa eros nam pulvinar, eget sociosqu sapien duis natoque nunc. Justo donec natoque mus at tempus curae ornare, aenean congue fames mauris sociosqu mattis orci, quam accumsan erat nunc senectus massa. Cum dis vestibulum litora fames mattis lacinia ligula, habitasse viverra suspendisse faucibus consequat primis, magna risus arcu vel commodo facilisis.
Curae tincidunt sed enim eleifend non ornare mus interdum augue, lectus ut quis ultricies habitant varius integer fringilla, aptent volutpat eget nisi cum in conubia pretium. Vivamus ut phasellus hac venenatis ullamcorper porta ad ante class morbi, at facilisi molestie sodales erat posuere accumsan mattis turpis, sed per commodo id netus himenaeos vel justo mauris. Sapien dui vestibulum dictum massa augue lectus taciti aenean, vitae orci pellentesque donec interdum ultrices molestie, hac fames nulla nisi leo justo est.
Erat tellus ultrices luctus mauris sapien lacinia ac convallis cubilia, orci lacus velit felis nisi eget hac neque, placerat fames conubia eros lobortis nostra torquent dictum. Ultricies donec ad vel pharetra purus enim leo vivamus, sagittis id tempor molestie pretium arcu nibh sem, mattis sodales mollis massa fringilla nisi faucibus. Nostra diam habitasse per convallis dignissim dictum gravida facilisis, scelerisque felis ullamcorper posuere mollis ultrices quisque laoreet, ridiculus auctor habitant aliquet arcu natoque mattis.
Porttitor sollicitudin tellus vel libero mi morbi dui sem viverra taciti, pharetra habitasse placerat nullam auctor praesent risus nulla tempus proin, integer conubia eros ligula ultrices cubilia class lectus tincidunt. Morbi maecenas penatibus potenti enim platea ante, quis per lobortis curae natoque. Nec sodales tortor diam blandit venenatis eleifend nascetur eu duis, faucibus morbi magna curae ut aenean cubilia condimentum, sociosqu semper fringilla sollicitudin curabitur vulputate quis ac. Nostra purus in risus laoreet litora urna torquent faucibus, morbi commodo facilisis proin enim conubia hendrerit, nibh ornare consequat sem eu cursus aliquam.
Montes vulputate fermentum sed nunc penatibus cubilia tempus malesuada dapibus, posuere semper interdum lacinia rutrum facilisis elementum sociosqu, conubia tincidunt aenean tortor porttitor phasellus vehicula eleifend. Potenti habitant pellentesque tempus praesent class curabitur scelerisque suspendisse sociosqu dis, senectus tellus nec cursus fermentum ridiculus malesuada magnis elementum, neque leo velit non nascetur mauris feugiat vel netus. Dui laoreet sem natoque diam gravida condimentum interdum faucibus elementum lacus, auctor quam etiam integer convallis tincidunt rhoncus volutpat nulla, varius odio sociis ut fermentum fusce feugiat ultricies luctus.
Dignissim tristique venenatis diam auctor malesuada aenean aliquam ornare iaculis, primis vulputate libero suspendisse viverra vivamus sociosqu. Luctus cras suspendisse quis magna odio varius gravida turpis nec metus non id fringilla, parturient maecenas dapibus faucibus hendrerit felis laoreet mollis cum nostra commodo. Porttitor hendrerit dictum eleifend fusce dis fermentum at pellentesque, laoreet commodo dictumst semper dui erat montes, curabitur duis praesent facilisi sem ullamcorper inceptos.
Imperdiet sagittis sapien lobortis quis consequat blandit habitant porta potenti sed, natoque dictum nulla phasellus viverra felis pretium parturient. Convallis habitasse sem turpis nunc praesent ornare mi elementum eu hendrerit, id nascetur sagittis tempor nibh quam a ligula primis imperdiet ullamcorper, nam purus luctus morbi class scelerisque vulputate magna tellus. Pharetra quisque pellentesque nam imperdiet lacinia enim, donec vitae senectus scelerisque phasellus dictumst, ac aliquam mattis urna ante.
Habitant praesent pulvinar scelerisque per phasellus lobortis velit, magnis odio himenaeos primis curabitur senectus, nascetur ullamcorper convallis nunc placerat nisl. Porta tellus commodo praesent ullamcorper cursus senectus tempor vivamus, penatibus eu purus ultrices posuere mi sodales, urna quisque accumsan imperdiet convallis aptent nisl. Gravida hendrerit venenatis curabitur sollicitudin metus auctor vivamus vulputate malesuada, mauris purus maecenas ac magna duis nostra ad a massa, nisl conubia odio lacinia rhoncus felis erat montes. Nostra eros proin mi venenatis enim semper ad magnis netus, in vestibulum ornare ac fusce aliquet aptent non condimentum faucibus, tempor arcu potenti blandit magna consequat luctus nam.

View File

@@ -0,0 +1,190 @@
---
publishDate: 2023-01-02T00:00:00Z
title: Markdown elements demo post
description: Lorem ipsum dolor sit amet
excerpt: Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur nisi minim dolor.
tags:
- markdown
- blog
- Astro
---
import Logo from '~/components/Logo.astro';
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
## <a name="Headings"></a>Headings
Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur nisi minim dolor. Aliquip et adipisicing sit sit fugiat commodo id sunt. Nostrud enim ad commodo incididunt cupidatat in ullamco ullamco Lorem cupidatat velit enim et Lorem. Ut laborum cillum laboris fugiat culpa sint irure do reprehenderit culpa occaecat. Exercitation esse mollit tempor magna aliqua in occaecat aliquip veniam reprehenderit nisi dolor in laboris dolore velit.
## Heading two
Aute officia nulla deserunt do deserunt cillum velit magna. Officia veniam culpa anim minim dolore labore pariatur voluptate id ad est duis quis velit dolor pariatur enim. Incididunt enim excepteur do veniam consequat culpa do voluptate dolor fugiat ad adipisicing sit. Labore officia est adipisicing dolore proident eiusmod exercitation deserunt ullamco anim do occaecat velit. Elit dolor consectetur proident sunt aliquip est do tempor quis aliqua culpa aute. Duis in tempor exercitation pariatur et adipisicing mollit irure tempor ut enim esse commodo laboris proident. Do excepteur laborum anim esse aliquip eu sit id Lorem incididunt elit irure ea nulla dolor et. Nulla amet fugiat qui minim deserunt enim eu cupidatat aute officia do velit ea reprehenderit.
### Heading three
Voluptate cupidatat cillum elit quis ipsum eu voluptate fugiat consectetur enim. Quis ut voluptate culpa ex anim aute consectetur dolore proident voluptate exercitation eiusmod. Esse in do anim magna minim culpa sint. Adipisicing ipsum consectetur proident ullamco magna sit amet aliqua aute fugiat laborum exercitation duis et.
#### Heading four
Commodo fugiat aliqua minim quis pariatur mollit id tempor. Non occaecat minim esse enim aliqua adipisicing nostrud duis consequat eu adipisicing qui. Minim aliquip sit excepteur ipsum consequat laborum pariatur excepteur. Veniam fugiat et amet ad elit anim laborum duis mollit occaecat et et ipsum et reprehenderit. Occaecat aliquip dolore adipisicing sint labore occaecat officia fugiat. Quis adipisicing exercitation exercitation eu amet est laboris sunt nostrud ipsum reprehenderit ullamco. Enim sint ut consectetur id anim aute voluptate exercitation mollit dolore magna magna est Lorem. Ut adipisicing adipisicing aliqua ullamco voluptate labore nisi tempor esse magna incididunt.
##### Heading five
Veniam enim esse amet veniam deserunt laboris amet enim consequat. Minim nostrud deserunt cillum consectetur commodo eu enim nostrud ullamco occaecat excepteur. Aliquip et ut est commodo enim dolor amet sint excepteur. Amet ad laboris laborum deserunt sint sunt aliqua commodo ex duis deserunt enim est ex labore ut. Duis incididunt velit adipisicing non incididunt adipisicing adipisicing. Ad irure duis nisi tempor eu dolor fugiat magna et consequat tempor eu ex dolore. Mollit esse nisi qui culpa ut nisi ex proident culpa cupidatat cillum culpa occaecat anim. Ut officia sit ea nisi ea excepteur nostrud ipsum et nulla.
###### Heading six
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
[[Top]](#top)
## <a name="Paragraphs"></a>Paragraphs
Incididunt ex adipisicing ea ullamco consectetur in voluptate proident fugiat tempor deserunt reprehenderit ullamco id dolore laborum. Do laboris laboris minim incididunt qui consectetur exercitation adipisicing dolore et magna consequat magna anim sunt. Officia fugiat Lorem sunt pariatur incididunt Lorem reprehenderit proident irure. Dolore ipsum aliqua mollit ad officia fugiat sit eu aliquip cupidatat ipsum duis laborum laborum fugiat esse. Voluptate anim ex dolore deserunt ea ex eiusmod irure. Occaecat excepteur aliqua exercitation aliquip dolor esse eu eu.
Officia dolore laborum aute incididunt commodo nisi velit est est elit et dolore elit exercitation. Enim aliquip magna id ipsum aliquip consectetur ad nulla quis. Incididunt pariatur dolor consectetur cillum enim velit cupidatat laborum quis ex.
Officia irure in non voluptate adipisicing sit amet tempor duis dolore deserunt enim ut. Reprehenderit incididunt in ad anim et deserunt deserunt Lorem laborum quis. Enim aute anim labore proident laboris voluptate elit excepteur in. Ex labore nulla velit officia ullamco Lorem Lorem id do. Dolore ullamco ipsum magna dolor pariatur voluptate ipsum id occaecat ipsum. Dolore tempor quis duis commodo quis quis enim.
[[Top]](#top)
## <a name="Blockquotes"></a>Blockquotes
Ad nisi laborum aute cupidatat magna deserunt eu id laboris id. Aliquip nulla cupidatat sint ex Lorem mollit laborum dolor amet est ut esse aute. Nostrud ex consequat id incididunt proident ipsum minim duis aliqua ut ex et ad quis. Laborum sint esse cillum anim nulla cillum consectetur aliqua sit. Nisi excepteur cillum labore amet excepteur commodo enim occaecat consequat ipsum proident exercitation duis id in.
> Ipsum et cupidatat mollit exercitation enim duis sunt irure aliqua reprehenderit mollit. Pariatur Lorem pariatur laboris do culpa do elit irure. Eiusmod amet nulla voluptate velit culpa et aliqua ad reprehenderit sit ut.
Labore ea magna Lorem consequat aliquip consectetur cillum duis dolore. Et veniam dolor qui incididunt minim amet laboris sit. Dolore ad esse commodo et dolore amet est velit ut nisi ea. Excepteur ea nulla commodo dolore anim dolore adipisicing eiusmod labore id enim esse quis mollit deserunt est. Minim ea culpa voluptate nostrud commodo proident in duis aliquip minim.
> Qui est sit et reprehenderit aute est esse enim aliqua id aliquip ea anim. Pariatur sint reprehenderit mollit velit voluptate enim consectetur sint enim. Quis exercitation proident elit non id qui culpa dolore esse aliquip consequat.
Ipsum excepteur cupidatat sunt minim ad eiusmod tempor sit.
> Deserunt excepteur adipisicing culpa pariatur cillum laboris ullamco nisi fugiat cillum officia. In cupidatat nulla aliquip tempor ad Lorem Lorem quis voluptate officia consectetur pariatur ex in est duis. Mollit id esse est elit exercitation voluptate nostrud nisi laborum magna dolore dolore tempor in est consectetur.
Adipisicing voluptate ipsum culpa voluptate id aute laboris labore esse fugiat veniam ullamco occaecat do ut. Tempor et esse reprehenderit veniam proident ipsum irure sit ullamco et labore ea excepteur nulla labore ut. Ex aute minim quis tempor in eu id id irure ea nostrud dolor esse.
[[Top]](#top)
## <a name="Lists"></a>Lists
### Ordered List
1. Longan
2. Lychee
3. Excepteur ad cupidatat do elit laborum amet cillum reprehenderit consequat quis.
Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip aliquip velit pariatur dolore.
4. Marionberry
5. Melon
- Cantaloupe
- Honeydew
- Watermelon
6. Miracle fruit
7. Mulberry
### Unordered List
- Olive
- Orange
- Blood orange
- Clementine
- Papaya
- Ut aute ipsum occaecat nisi culpa Lorem id occaecat cupidatat id id magna laboris ad duis. Fugiat cillum dolore veniam nostrud proident sint consectetur eiusmod irure adipisicing.
- Passionfruit
[[Top]](#top)
## <a name="Horizontal"></a>Horizontal rule
In dolore velit aliquip labore mollit minim tempor veniam eu veniam ad in sint aliquip mollit mollit. Ex occaecat non deserunt elit laborum sunt tempor sint consequat culpa culpa qui sit. Irure ad commodo eu voluptate mollit cillum cupidatat veniam proident amet minim reprehenderit.
---
In laboris eiusmod reprehenderit aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi. Dolor amet cillum excepteur consequat sint non sint.
[[Top]](#top)
## <a name="Table"></a>Table
Duis sunt ut pariatur reprehenderit mollit mollit magna dolore in pariatur nulla commodo sit dolor ad fugiat. Laboris amet ea occaecat duis eu enim exercitation deserunt ea laborum occaecat reprehenderit. Et incididunt dolor commodo consequat mollit nisi proident non pariatur in et incididunt id. Eu ut et Lorem ea ex magna minim ipsum ipsum do.
| Table Heading 1 | Table Heading 2 | Center align | Right align | Table Heading 5 |
| :-------------- | :-------------- | :----------: | ----------: | :-------------- |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
| Item 1 | Item 2 | Item 3 | Item 4 | Item 5 |
Minim id consequat adipisicing cupidatat laborum culpa veniam non consectetur et duis pariatur reprehenderit eu ex consectetur. Sunt nisi qui eiusmod ut cillum laborum Lorem officia aliquip laboris ullamco nostrud laboris non irure laboris. Cillum dolore labore Lorem deserunt mollit voluptate esse incididunt ex dolor.
[[Top]](#top)
## <a name="Code"></a>Code
### Inline code
Ad amet irure est magna id mollit Lorem in do duis enim. Excepteur velit nisi magna ea pariatur pariatur ullamco fugiat deserunt sint non sint. Duis duis est `code in text` velit velit aute culpa ex quis pariatur pariatur laborum aute pariatur duis tempor sunt ad. Irure magna voluptate dolore consectetur consectetur irure esse. Anim magna `<strong>in culpa qui officia</strong>` dolor eiusmod esse amet aute cupidatat aliqua do id voluptate cupidatat reprehenderit amet labore deserunt.
### Highlighted
Et fugiat ad nisi amet magna labore do cillum fugiat occaecat cillum Lorem proident. In sint dolor ullamco ad do adipisicing amet id excepteur Lorem aliquip sit irure veniam laborum duis cillum. Aliqua occaecat minim cillum deserunt magna sunt laboris do do irure ea nostrud consequat ut voluptate ex.
```go
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
```
Ex amet id ex aliquip id do laborum excepteur exercitation elit sint commodo occaecat nostrud est. Nostrud pariatur esse veniam laborum non sint magna sit laboris minim in id. Aliqua pariatur pariatur excepteur adipisicing irure culpa consequat commodo et ex id ad.
[[Top]](#top)
## <a name="Inline"></a>Inline elements
Sint ea anim ipsum ad commodo cupidatat do **exercitation** incididunt et minim ad labore sunt. Minim deserunt labore laboris velit nulla incididunt ipsum nulla. Ullamco ad laborum ea qui et anim in laboris exercitation tempor sit officia laborum reprehenderit culpa velit quis. **Consequat commodo** reprehenderit duis [irure](#!) esse esse exercitation minim enim Lorem dolore duis irure. Nisi Lorem reprehenderit ea amet excepteur dolor excepteur magna labore proident voluptate ipsum. Reprehenderit ex esse deserunt aliqua ea officia mollit Lorem nulla magna enim. Et ad ipsum labore enim ipsum **cupidatat consequat**. Commodo non ea cupidatat magna deserunt dolore ipsum velit nulla elit veniam nulla eiusmod proident officia.
![Super wide](https://images.unsplash.com/photo-1471128466710-c26ff0d26143?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY2MDc4MTk3Mw&ixlib=rb-1.2.1&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080)
_Proident sit veniam in est proident officia adipisicing_ ea tempor cillum non cillum velit deserunt. Voluptate laborum incididunt sit consectetur Lorem irure incididunt voluptate nostrud. Commodo ut eiusmod tempor cupidatat esse enim minim ex anim consequat. Mollit sint culpa qui laboris quis consectetur ad sint esse. Amet anim anim minim ullamco et duis non irure. Sit tempor adipisicing ea laboris `culpa ex duis sint` anim aute reprehenderit id eu ea. Aute [excepteur proident](#!) Lorem minim adipisicing nostrud mollit ad ut voluptate do nulla esse occaecat aliqua sint anim.
![Not so big](https://placekitten.com/480/400)
Incididunt in culpa cupidatat mollit cillum qui proident sit. In cillum aliquip incididunt voluptate magna amet cupidatat cillum pariatur sint aliqua est _enim **anim** voluptate_. Magna aliquip proident incididunt id duis pariatur eiusmod incididunt commodo culpa dolore sit. Culpa do nostrud elit ad exercitation anim pariatur non minim nisi **adipisicing sunt _officia_**. Do deserunt magna mollit Lorem commodo ipsum do cupidatat mollit enim ut elit veniam ea voluptate.
Reprehenderit non eu quis in ad elit esse qui aute id [incididunt](#!) dolore cillum. Esse laboris consequat dolor anim exercitation tempor aliqua deserunt velit magna laboris. Culpa culpa minim duis amet mollit do quis amet commodo nulla irure.
[[Top]](#top)
## MDX
```js
---
publishDate: 'Aug 02 2022'
title: 'Markdown elements demo post'
---
import Logo from "~/components/Logo.astro";
## MDX
<Logo />
```
<div style="background:#eee;padding: 10px 5px">
<Logo />
</div>
[[Top]](#top)

View File

@@ -0,0 +1,62 @@
---
publishDate: 2023-01-09T00:00:00Z
title: Useful tools and resources to create a professional website
excerpt: Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur.
image: ~/assets/images/tools.jpg
tags:
- front-end
- tools
- resources
---
## Magna nunc senectus torquent per fusce sapien ligula tempus cra
Lorem ipsum dolor sit amet consectetur adipiscing, elit fusce imperdiet gravida velit massa, ligula aenean suscipit sociis lacinia. Sapien scelerisque rutrum sem accumsan orci imperdiet aliquam inceptos aliquet tempus ornare, netus nostra nam nunc platea pulvinar urna et suscipit pellentesque, aenean congue sociis non tellus quis proin etiam venenatis pretium. Nibh senectus lacinia volutpat nostra taciti ac posuere, dictum ultricies dictumst luctus in vehicula, mus molestie venenatis penatibus ridiculus elementum. Phasellus sollicitudin dignissim parturient tempor cubilia erat massa eleifend dapibus, condimentum cras tortor eu sem dictumst non. Faucibus neque est malesuada nostra luctus maecenas at condimentum, arcu eros vulputate curabitur blandit mollis volutpat, lectus leo dictumst duis semper tempor hendrerit. Egestas scelerisque fusce torquent cubilia consequat conubia lacus et mollis, condimentum taciti elementum sapien risus vulputate est.
Tristique eleifend enim praesent mollis sem leo, molestie dictum penatibus sodales consequat ligula nulla, platea feugiat aptent sapien turpis. Mollis mus ac taciti maecenas pretium hendrerit proin accumsan, mattis dictumst netus nunc facilisi morbi cursus euismod quis, a commodo nulla integer varius enim vehicula. Consequat mi risus vulputate ullamcorper sociosqu pretium molestie cursus, parturient viverra non tempor tellus convallis vitae eleifend mus, bibendum pellentesque imperdiet vivamus nunc phasellus iaculis. Volutpat est ac dictumst eleifend maecenas torquent quam hac, mollis aliquam mattis euismod ornare risus fringilla proin nisi, sem fermentum primis ultrices varius etiam id. Posuere nunc mus curabitur condimentum lobortis euismod donec tincidunt ridiculus, tristique senectus cum taciti quam blandit leo malesuada, sociis nullam cras litora sem laoreet sed nec.
## Magna lacus tortor luctus platea co
- Luctus molestie taciti aliquam dictumst imperdiet, donec torquent nisi.
- Montes cursus habitant risus platea senectus, lectus sagittis mi.
- Eleifend facilisi quam ultricies, accumsan aliquet euismod velit, sem tortor.
- Senectus nisl potenti congue sociosqu at, porttitor habitant vivamus.
Nostra dictum porta consequat quisque diam nisl iaculis velit varius, placerat curabitur risus commodo condimentum morbi eros dictumst phasellus, tempor duis libero ultrices est rhoncus mattis nam. Quisque lectus massa lobortis nulla enim, praesent eu ut elementum. Felis placerat nibh donec erat platea rutrum taciti cursus, elementum metus semper feugiat risus tellus nulla, aliquam hendrerit faucibus inceptos commodo justo porta.
Cursus imperdiet montes natoque potenti suscipit facilisi porta mollis posuere consequat, aliquam turpis tortor libero viverra rhoncus accumsan inceptos blandit convallis diam, penatibus ut pretium in duis leo auctor proin quisque. Sollicitudin inceptos quam molestie sociis habitasse class sapien vivamus facilisis, consequat ante vehicula velit tempor cum rutrum magnis, eget semper quisque turpis pretium praesent per faucibus. Ullamcorper blandit taciti primis sed pharetra inceptos duis, eu nisi ac fringilla tellus accumsan iaculis, morbi integer at purus hac est. Elementum hac lacus per in posuere erat ad, egestas dapibus malesuada suscipit nunc interdum, mi risus auctor pretium lectus massa. Condimentum nullam molestie tincidunt sodales luctus parturient est et congue hendrerit vel vulputate iaculis, curabitur sollicitudin quisque magna nostra nisl nam massa viverra donec neque class.
Euismod tempus potenti interdum fusce placerat habitant, taciti turpis faucibus curabitur tempor felis porta, sed aenean mi arcu magnis. Pellentesque tincidunt aptent eget nisi convallis lobortis sapien, habitasse sollicitudin proin vehicula ridiculus duis congue, himenaeos lectus vitae nulla taciti ante. Enim commodo non taciti ultricies donec iaculis aliquet interdum, dictumst a pulvinar lacus cursus fames praesent cras ad, rutrum nostra dis accumsan primis euismod sagittis. Eu habitant euismod mattis at congue fusce ad commodo litora himenaeos aenean, porta lobortis suscipit pulvinar magna facilisi nullam ante non senectus, urna volutpat sodales vitae varius lectus tincidunt montes rutrum vulputate. Enim cum habitant morbi maecenas nisl imperdiet a egestas velit, gravida laoreet hendrerit rutrum molestie fames sapien euismod turpis metus, faucibus class sed primis leo nam malesuada fermentum.
Semper etiam tellus a risus lobortis dictumst sem massa eros, eget curae gravida accumsan hac parturient nulla fringilla convallis, condimentum torquent placerat mauris conubia augue mattis leo. Dictum tincidunt quis risus volutpat netus mi suscipit parturient suspendisse vestibulum, ad lacus dictumst luctus nec fusce ultricies vivamus. Dui sociis nulla suscipit gravida mi arcu netus, vitae mus donec dapibus nascetur id ante urna, egestas viverra auctor sodales litora enim rutrum, sapien molestie imperdiet ut massa elementum. Aptent ante risus erat malesuada nec porta, ligula nascetur dictum nunc turpis natoque, tristique conubia netus arcu a.
Nam scelerisque ridiculus suspendisse viverra conubia et fermentum nascetur turpis quisque, vestibulum cubilia curae per feugiat lectus rhoncus suscipit neque. Urna habitasse mus hac fringilla rutrum sodales, nullam aliquam porttitor quis vehicula arcu class, in felis placerat mattis vestibulum habitant, mauris eros dapibus penatibus viverra. Senectus tristique molestie scelerisque quisque mus augue facilisi massa, ac viverra dapibus vehicula nostra vel nam, posuere montes parturient auctor eu ultrices natoque. Quam fringilla volutpat morbi in per aliquet laoreet a maecenas, lacus velit mauris purus ultricies sociosqu pulvinar netus sodales, convallis placerat turpis tellus nullam libero leo aptent. Praesent lacus ultricies per ligula taciti aenean conubia parturient, feugiat sodales viverra urna cubilia etiam nibh curabitur congue, tristique nisl at elementum dis natoque commodo.
Sagittis erat velit integer cursus congue viverra, conubia himenaeos egestas ultricies praesent, hac litora mattis non venenatis. Duis libero morbi curae potenti litora vitae sed etiam consequat magna ultricies, magnis fermentum vehicula feugiat tortor ad quis orci rhoncus per porta, ante mi gravida dis nostra tempor lobortis aenean convallis molestie. Ligula fusce blandit ac accumsan magnis rutrum nostra velit maecenas, netus lobortis himenaeos purus justo sapien posuere libero, cum etiam urna mi ultrices est sociis tortor. Neque inceptos quisque vestibulum tempor phasellus id himenaeos magna suspendisse a in nunc cursus, morbi dignissim ornare non auctor massa iaculis mus nec elementum ultricies maecenas. Scelerisque maecenas ultrices integer gravida dis cursus, sed at semper libero iaculis varius, justo augue nec tincidunt suspendisse.
Rutrum augue natoque felis non vestibulum nam duis, quam praesent taciti himenaeos class vel dis rhoncus, dapibus pulvinar etiam ridiculus curae nibh blandit, scelerisque cursus nostra pretium suspendisse vehicula. Etiam sem metus eleifend suscipit felis suspendisse ut, velit fames habitant semper placerat fusce cras, nunc venenatis platea aenean euismod libero. In eu eget pellentesque libero egestas suspendisse quis tristique torquent nulla, magnis dis malesuada purus quam platea aliquet tortor odio. Accumsan nostra augue lobortis elementum justo sociosqu posuere aptent est, nisl metus conubia tellus sollicitudin lacus inceptos. Morbi mauris aenean malesuada arcu fusce libero venenatis commodo iaculis litora dis, erat parturient class sed facilisi mus a nec dictum.
Senectus platea dapibus volutpat dictum pharetra cursus netus cras, arcu sociis ornare potenti porttitor tempus sollicitudin, ullamcorper duis nam convallis sapien pretium conubia. Mi metus vivamus cum id semper fringilla senectus scelerisque pretium placerat sociis rhoncus pulvinar porttitor accumsan, curae ligula fermentum mus hendrerit ridiculus condimentum per suscipit rutrum sociosqu odio pellentesque suspendisse. Dui massa nulla suscipit duis metus mollis pellentesque, scelerisque posuere interdum ligula cum dignissim sed, placerat ante ultrices mi netus augue. Eu porttitor malesuada diam morbi torquent egestas magnis tempus metus imperdiet nisl, ad sociis lectus neque mauris gravida habitant primis lobortis. Phasellus mattis nulla fames parturient pharetra pretium egestas, diam rhoncus placerat lectus maecenas dictumst sed cum, justo non ac volutpat morbi enim.
Justo fringilla morbi netus habitasse varius primis eu magna, tristique accumsan mus enim lectus cubilia convallis auctor, nunc imperdiet erat mollis rutrum vel turpis. Justo purus laoreet eros turpis interdum et ridiculus torquent integer nunc, himenaeos eu tellus proin scelerisque tincidunt congue posuere ultricies vestibulum auctor, aliquet semper varius placerat imperdiet non nisl cubilia fermentum. Feugiat nisl himenaeos cum metus mi est ac, euismod elementum velit tempus dictum mauris, bibendum faucibus cubilia phasellus nulla ornare. Etiam justo venenatis varius laoreet sociis montes dignissim, elementum ligula malesuada euismod praesent magnis auctor, eleifend class egestas a vestibulum blandit. Scelerisque potenti facilisis torquent mollis nisi felis et sed, aptent tortor platea non quisque nec accumsan inceptos, velit molestie nunc enim cubilia egestas per.
Ultrices morbi et potenti eros aenean condimentum magnis est felis porta, dictumst taciti inceptos etiam ultricies cubilia hac torquent tempor vulputate, sodales erat semper vestibulum dignissim sociis viverra suscipit sagittis. Justo non auctor penatibus iaculis sed in volutpat pretium feugiat lectus rutrum, curabitur sociosqu sapien semper a laoreet augue primis fringilla dui. Fringilla iaculis blandit feugiat euismod congue morbi erat eros, mi dis egestas facilisi volutpat risus cras porta, orci vivamus turpis conubia est commodo torquent. Lectus euismod maecenas potenti in ac natoque sed ullamcorper ridiculus, diam fringilla condimentum eget convallis hendrerit varius pellentesque. Feugiat cras nullam tristique leo nisl dignissim lacinia aenean vivamus potenti consequat, vulputate curabitur sed risus mus suspendisse litora sollicitudin tempor.
Egestas hac arcu dapibus placerat proin aptent a pellentesque posuere, in condimentum fames facilisi maecenas semper nisl mus, sodales donec elementum praesent enim ac dictum ridiculus. Justo in nibh luctus vitae etiam nisl ac quisque fringilla, habitasse sociosqu curae inceptos semper ut mi hac, congue volutpat himenaeos sed augue morbi tellus nec. Congue libero posuere varius eleifend tristique nascetur integer ullamcorper, est leo vitae mi erat enim augue urna magnis, elementum ultricies pulvinar blandit arcu malesuada duis. Cubilia nulla vel et integer sed pellentesque gravida felis pulvinar mollis ultricies mi, montes suspendisse vestibulum aliquet dui in magna nunc ridiculus aliquam elementum. Justo erat montes enim felis eu sed vivamus faucibus imperdiet ac luctus vulputate, cursus accumsan blandit et mus sodales conubia cubilia phasellus leo.
Velit in felis penatibus semper laoreet libero tristique condimentum sem montes suscipit, morbi habitant gravida tellus quisque neque torquent lobortis interdum. Ridiculus sollicitudin suscipit semper quam eleifend at, neque tincidunt magnis penatibus dui orci, praesent vulputate himenaeos feugiat vel. Habitasse senectus a sodales dapibus nulla auctor sagittis nullam molestie, imperdiet volutpat quam odio facilisis nostra magnis dictumst, sociis cum erat facilisi dignissim urna lacus magna. Primis porttitor nullam quis vestibulum mi dictumst magna dapibus taciti magnis inceptos fames, purus etiam auctor metus bibendum felis accumsan id aliquet suscipit imperdiet. Pellentesque sem velit nulla consequat vehicula cubilia curabitur, platea curae natoque tristique nullam litora, nascetur imperdiet habitant tincidunt suspendisse sociis.
Platea cum auctor eget consequat elementum lacinia ad aliquet orci, imperdiet nibh penatibus ac dictum rutrum mollis ante cursus, volutpat scelerisque velit ornare in vivamus pharetra blandit. Cum mattis interdum in diam purus sapien lacinia gravida, semper montes vestibulum rhoncus auctor morbi dictum. Mus semper erat mollis taciti sapien ultrices accumsan ante magna eros at commodo, malesuada diam nullam massa curabitur lobortis felis interdum nisi duis pellentesque. Accumsan faucibus tristique augue enim hac ante feugiat, porttitor phasellus condimentum nulla maecenas dignissim at platea, facilisis nam donec primis habitasse ac. Nec convallis ridiculus potenti primis faucibus erat eget metus mollis, luctus ac fusce condimentum orci suscipit volutpat malesuada mi, velit feugiat pharetra sem turpis est accumsan porta, ligula torquent lacus tristique a senectus tortor dignissim.
Pharetra eleifend vivamus potenti congue proin himenaeos, fusce mi venenatis natoque montes, suscipit commodo porta magnis mattis. Et lobortis mollis libero quis himenaeos felis dis porta, donec iaculis mattis cursus accumsan pulvinar mus etiam, habitasse leo taciti vitae suscipit suspendisse bibendum. Sodales at ante dictumst nostra est risus senectus semper morbi facilisis neque tempus, venenatis penatibus fusce mattis phasellus velit diam iaculis hac tortor class, orci ridiculus varius dis odio cras rutrum porttitor facilisi massa parturient. Augue facilisi nam proin at elementum massa, tellus vestibulum mattis tortor porta, cubilia sodales orci congue vel. Rhoncus nec quam iaculis sapien risus suspendisse dictum tincidunt, vivamus lobortis blandit metus ullamcorper torquent.
Ante fermentum hac tincidunt nam sodales vestibulum pellentesque ut nulla habitasse, ornare diam facilisis aptent facilisi penatibus arcu congue lacus, lectus fringilla per primis dapibus eu imperdiet erat dictumst. Pulvinar eu ad mauris nulla ac sed nisl ullamcorper natoque etiam fames, platea aliquam dis netus odio dignissim tincidunt quam blandit laoreet, at mollis ridiculus molestie lacus metus nullam suspendisse nibh duis. Suspendisse congue vestibulum fringilla ridiculus tristique sagittis sociosqu integer, volutpat lacinia pulvinar felis aliquam pharetra faucibus dictumst ad, fusce dignissim cursus mauris eget nostra lectus. Lacinia egestas iaculis scelerisque odio gravida ullamcorper, at arcu ligula ornare parturient phasellus laoreet, augue convallis platea tortor aenean.
Interdum fames lobortis sollicitudin aliquet mus aptent netus, penatibus consequat pulvinar velit enim curae accumsan, maecenas litora mi rutrum sagittis tincidunt. Lacinia malesuada id netus suscipit sapien sociosqu orci habitasse turpis, feugiat donec placerat sed quam hendrerit pellentesque. Erat accumsan ligula id sapien turpis mus nulla lobortis consequat nec, urna habitasse ultrices aliquet vulputate est suspendisse gravida senectus odio, vehicula fusce proin in sed tempor vitae convallis molestie. Nascetur semper feugiat velit hendrerit lacinia nunc, risus quis congue nullam himenaeos commodo porttitor, natoque facilisi ad maecenas faucibus. Dictum id sodales interdum accumsan habitant natoque class parturient mi venenatis aenean, est nam tortor donec lobortis non vehicula magnis lacinia. Feugiat vitae morbi litora vehicula in a, nam ad ultrices auctor sollicitudin, ullamcorper fringilla hendrerit placerat faucibus.
Nulla nisi ac placerat duis semper mus cursus interdum netus vestibulum, tortor praesent proin nec rhoncus magnis commodo blandit himenaeos purus, volutpat id montes scelerisque suspendisse risus nisl erat dui. Senectus et habitant dis nulla velit faucibus venenatis sapien, dapibus etiam metus eget magnis feugiat tristique. Augue montes elementum pulvinar mollis pellentesque diam cursus tristique vel cubilia erat mus, congue curae sagittis dui quis fusce tortor consequat taciti natoque. Praesent montes erat feugiat sed euismod condimentum potenti malesuada nec, mi vitae suspendisse aptent senectus eleifend faucibus pulvinar scelerisque, augue ornare accumsan pretium magna eu iaculis metus. Suscipit accumsan massa vitae platea ad duis rhoncus fermentum vulputate, interdum pretium metus per aptent enim in facilisis eros, sollicitudin consequat iaculis erat dictumst quisque leo sociis.
Tempor etiam potenti auctor est ut habitant ac nisl ultrices pulvinar, sem primis tempus lacus aliquam consequat fringilla tristique. Consequat cum rhoncus massa sociis blandit rutrum nisi quam cras vitae fusce, sociosqu erat penatibus convallis fames accumsan eros himenaeos pulvinar sagittis, habitasse primis integer odio nascetur in montes faucibus semper potenti. Diam aliquam fringilla risus phasellus habitasse aenean eu erat, netus nulla pellentesque ut morbi torquent pharetra semper, sed etiam primis in conubia hendrerit velit. Ornare magna dictum purus metus sociosqu pulvinar sed, quam faucibus posuere pretium senectus interdum. Ornare sodales in litora nascetur sociosqu senectus auctor, cras arcu fusce ac inceptos integer tempor aliquam, tristique imperdiet metus hendrerit erat eleifend.

127
src/data.js Normal file
View File

@@ -0,0 +1,127 @@
import { getPermalink, getBlogPermalink, getAsset } from './utils/permalinks';
export const headerData = {
links: [
{
text: 'Landing',
links: [
{
text: 'Sass',
href: getPermalink('/landing/saas'),
},
{
text: 'Startup',
href: getPermalink('/landing/startup'),
},
{
text: 'Mobile App',
href: getPermalink('/landing/mobile-app'),
},
],
},
{
text: 'Pages',
links: [
{
text: 'Features',
href: '#',
},
{
text: 'Pricing',
href: '#',
},
{
text: 'About us',
href: '#',
},
{
text: 'Contact',
href: '#',
},
{
text: 'Terms',
href: getPermalink('/terms'),
},
{
text: 'Privacy policy',
href: getPermalink('/privacy'),
},
],
},
{
text: 'Widgets',
href: '#',
},
{
text: 'Blog',
href: getBlogPermalink(),
},
],
actions: [
{ type: 'button', text: 'Download', href: 'https://github.com/onwidget/astrowind' }
],
};
export const footerData = {
links: [
{
title: 'Product',
links: [
{ text: 'Features', href: '#' },
{ text: 'Security', href: '#' },
{ text: 'Team', href: '#' },
{ text: 'Enterprise', href: '#' },
{ text: 'Customer stories', href: '#' },
{ text: 'Pricing', href: '#' },
{ text: 'Resources', href: '#' },
],
},
{
title: 'Platform',
links: [
{ text: 'Developer API', href: '#' },
{ text: 'Partners', href: '#' },
{ text: 'Atom', href: '#' },
{ text: 'Electron', href: '#' },
{ text: 'AstroWind Desktop', href: '#' },
],
},
{
title: 'Support',
links: [
{ text: 'Docs', href: '#' },
{ text: 'Community Forum', href: '#' },
{ text: 'Professional Services', href: '#' },
{ text: 'Skills', href: '#' },
{ text: 'Status', href: '#' },
],
},
{
title: 'Company',
links: [
{ text: 'About', href: '#' },
{ text: 'Blog', href: '#' },
{ text: 'Careers', href: '#' },
{ text: 'Press', href: '#' },
{ text: 'Inclusion', href: '#' },
{ text: 'Social Impact', href: '#' },
{ text: 'Shop', href: '#' },
],
},
],
secondaryLinks: [
{ text: 'Terms', href: getPermalink('/terms') },
{ text: 'Privacy Policy', href: getPermalink('/privacy') },
],
socialLinks: [
{ ariaLabel: 'Twitter', icon: 'tabler:brand-twitter', href: '#' },
{ ariaLabel: 'Instagram', icon: 'tabler:brand-instagram', href: '#' },
{ ariaLabel: 'Facebook', icon: 'tabler:brand-facebook', href: '#' },
{ ariaLabel: 'RSS', icon: 'tabler:rss', href: getAsset('/rss.xml') },
{ ariaLabel: 'Github', icon: 'tabler:brand-github', href: 'https://github.com/onwidget/astrowind' },
],
footNote: `
<span class="w-5 h-5 md:w-6 md:h-6 md:-mt-0.5 bg-cover mr-1.5 float-left rounded-sm bg-[url(https://onwidget.com/favicon/favicon-32x32.png)]"></span>
Made by <a class="text-blue-600 hover:underline dark:text-gray-200" href="https://onwidget.com/"> onWidget</a> · All rights reserved.
`,
};

2
src/env.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="@astrojs/image/client" />

View File

@@ -0,0 +1,33 @@
---
import '~/assets/styles/base.css';
import MetaTags from '~/components/common/MetaTags.astro';
import BasicScripts from '~/components/common/BasicScripts.astro';
import { MetaSEO } from '~/types';
import { SITE } from '~/config.mjs';
export interface Props {
meta?: MetaSEO;
}
const { meta = {} } = Astro.props;
const { language = 'en', textDirection = 'ltr' } = SITE;
---
<!DOCTYPE html>
<html lang={language} dir={textDirection} class="2xl:text-[20px]">
<head>
<MetaTags {...meta} />
</head>
<body class="antialiased text-page bg-light dark:text-slate-300 tracking-tight dark:bg-dark">
<slot />
<BasicScripts />
<style is:global>
img {
content-visibility: auto;
}
</style>
</body>
</html>

View File

@@ -0,0 +1,24 @@
---
import Layout from '~/layouts/PageLayout.astro';
import { MetaSEO } from '~/types';
export interface Props {}
const { frontmatter } = Astro.props;
const meta: MetaSEO = {
title: frontmatter?.title,
};
---
<Layout {meta}>
<section class="px-4 py-16 sm:px-6 mx-auto lg:px-8 lg:py-20 max-w-4xl">
<h1 class="font-bold font-heading text-4xl md:text-5xl leading-tighter tracking-tighter">{frontmatter.title}</h1>
<div
class="mx-auto prose prose-lg max-w-4xl dark:prose-invert dark:prose-headings:text-slate-300 prose-md prose-headings:font-heading prose-headings:leading-tighter prose-headings:tracking-tighter prose-headings:font-bold prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-img:rounded-md prose-img:shadow-lg mt-8"
>
<slot />
</div>
</section>
</Layout>

View File

@@ -0,0 +1,31 @@
---
import Layout from '~/layouts/BaseLayout.astro';
import Header from '~/components/widgets/Header.astro';
import Footer from '~/components/widgets/Footer.astro';
import Announcement from '~/components/widgets/Announcement.astro';
import { headerData, footerData } from '~/data';
import { MetaSEO } from '~/types';
export interface Props {
meta?: MetaSEO;
}
const { meta } = Astro.props;
---
<Layout {meta}>
<slot name="announcement">
<Announcement />
</slot>
<slot name="header">
<Header {...headerData} isSticky showRssFeed showToggleTheme />
</slot>
<main>
<slot />
</main>
<slot name="footer">
<Footer {...footerData} />
</slot>
</Layout>

24
src/pages/404.astro Normal file
View File

@@ -0,0 +1,24 @@
---
import Layout from '~/layouts/BaseLayout.astro';
import { getHomePermalink } from '~/utils/permalinks';
const title = `Error 404`;
---
<Layout meta={{ title }}>
<section class="flex items-center h-full p-16">
<div class="container flex flex-col items-center justify-center px-5 mx-auto my-8">
<div class="max-w-md text-center">
<h2 class="mb-8 font-bold text-9xl">
<span class="sr-only">Error</span>
<span class="text-primary">404</span>
</h2>
<p class="text-3xl font-semibold md:text-3xl">Sorry, we couldn't find this page.</p>
<p class="mt-4 mb-8 text-lg text-muted dark:text-slate-400">
But dont worry, you can find plenty of other things on our homepage.
</p>
<a rel="noopener noreferrer" href={getHomePermalink()} class="btn ml-4">Back to homepage</a>
</div>
</div>
</section>
</Layout>

View File

@@ -0,0 +1,41 @@
---
import { SITE, BLOG } from '~/config.mjs';
import Layout from '~/layouts/PageLayout.astro';
import BlogList from '~/components/blog/List.astro';
import Headline from '~/components/blog/Headline.astro';
import Pagination from '~/components/blog/Pagination.astro';
import { fetchPosts } from '~/utils/blog';
import { BLOG_BASE } from '~/utils/permalinks';
export async function getStaticPaths({ paginate }) {
if (BLOG?.disabled || BLOG?.list?.disabled) return [];
return paginate(await fetchPosts(), {
params: { blog: BLOG_BASE || undefined },
pageSize: BLOG.postsPerPage,
});
}
const { page } = Astro.props;
const currentPage = page.currentPage ?? 1;
const meta = {
title: `Blog${currentPage > 1 ? ` — Page ${currentPage}` : ''}`,
description: SITE.description,
noindex: BLOG?.list?.noindex || currentPage > 1,
ogType: 'blog',
};
---
<Layout {meta}>
<section class="px-6 sm:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-4xl">
<Headline
subtitle="A statically generated blog example with news, tutorials, resources and other interesting content related to AstroWind"
>
The Blog
</Headline>
<BlogList posts={page.data} />
<Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
</section>
</Layout>

View File

@@ -0,0 +1,49 @@
---
import { SITE, BLOG } from '~/config.mjs';
import Layout from '~/layouts/PageLayout.astro';
import BlogList from '~/components/blog/List.astro';
import Headline from '~/components/blog/Headline.astro';
import Pagination from '~/components/blog/Pagination.astro';
import { fetchPosts } from '~/utils/blog';
import { CATEGORY_BASE } from '~/utils/permalinks';
export async function getStaticPaths({ paginate }) {
if (BLOG?.disabled || BLOG?.category?.disabled) return [];
const posts = await fetchPosts();
const categories = new Set();
posts.map((post) => {
typeof post.category === 'string' && categories.add(post.category.toLowerCase());
});
return Array.from(categories).map((category: string) =>
paginate(
posts.filter((post) => typeof post.category === 'string' && category === post.category.toLowerCase()),
{
params: { category: category, blog: CATEGORY_BASE || undefined },
pageSize: BLOG.postsPerPage,
props: { category },
}
)
);
}
const { page, category } = Astro.props;
const currentPage = page.currentPage ?? 1;
const meta = {
title: `Category'${category}' ${currentPage > 1 ? ` — Page ${currentPage}` : ''}`,
description: SITE.description,
noindex: BLOG?.category?.noindex,
};
---
<Layout meta={meta}>
<section class="px-6 sm:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-3xl">
<Headline><span class="capitalize">{category.replaceAll('-', ' ')}</span></Headline>
<BlogList posts={page.data} />
<Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
</section>
</Layout>

View File

@@ -0,0 +1,49 @@
---
import { SITE, BLOG } from '~/config.mjs';
import Layout from '~/layouts/PageLayout.astro';
import BlogList from '~/components/blog/List.astro';
import Pagination from '~/components/blog/Pagination.astro';
import { fetchPosts } from '~/utils/blog';
import { TAG_BASE } from '~/utils/permalinks';
import Headline from '~/components/blog/Headline.astro';
export async function getStaticPaths({ paginate }) {
if (BLOG?.disabled || BLOG?.tag?.disabled) return [];
const posts = await fetchPosts();
const tags = new Set();
posts.map((post) => {
Array.isArray(post.tags) && post.tags.map((tag) => tags.add(tag.toLowerCase()));
});
return Array.from(tags).map((tag: string) =>
paginate(
posts.filter((post) => Array.isArray(post.tags) && post.tags.find((elem) => elem.toLowerCase() === tag)),
{
params: { tag: tag, blog: TAG_BASE || undefined },
pageSize: BLOG.postsPerPage,
props: { tag },
}
)
);
}
const { page, tag } = Astro.props;
const currentPage = page.currentPage ?? 1;
const meta = {
title: `Posts by tag '${tag}'${currentPage > 1 ? ` — Page ${currentPage} ` : ''}`,
description: SITE.description,
noindex: BLOG?.tag?.noindex,
};
---
<Layout meta={meta}>
<section class="px-6 sm:px-6 py-12 sm:py-16 lg:py-20 mx-auto max-w-3xl">
<Headline>Tag: {tag}</Headline>
<BlogList posts={page.data} />
<Pagination prevUrl={page.url.prev} nextUrl={page.url.next} />
</section>
</Layout>

View File

@@ -0,0 +1,38 @@
---
import { BLOG } from '~/config.mjs';
import Layout from '~/layouts/PageLayout.astro';
import SinglePost from '~/components/blog/SinglePost.astro';
import ToBlogLink from '~/components/blog/ToBlogLink.astro';
import { getCanonical, getPermalink } from '~/utils/permalinks';
import { fetchPosts } from '~/utils/blog';
import { findImage } from '~/utils/images';
export async function getStaticPaths() {
if (BLOG?.disabled || BLOG?.post?.disabled) return [];
return (await fetchPosts()).map((post) => ({
params: {
blog: post.permalink,
},
props: { post },
}));
}
const { post } = Astro.props;
const url = getCanonical(getPermalink(post.permalink, 'post'));
const meta = {
title: post.title,
description: post.description,
canonical: post.canonical || url,
image: await findImage(post.image),
noindex: BLOG?.post?.noindex,
ogType: 'article',
};
---
<Layout {meta}>
<SinglePost post={{ ...post, image: meta.image }} url={url} />
<ToBlogLink />
</Layout>

352
src/pages/index.astro Normal file
View File

@@ -0,0 +1,352 @@
---
import { SITE } from '~/config.mjs';
import Layout from '~/layouts/PageLayout.astro';
import Hero from '~/components/widgets/Hero.astro';
import Note from '~/components/widgets/Note.astro';
import Features from '~/components/widgets/Features.astro';
import Features2 from '~/components/widgets/Features2.astro';
import Steps from '~/components/widgets/Steps.astro';
import Content from '~/components/widgets/Content.astro';
import LatestPosts from '~/components/blog/LatestPosts.astro';
import FAQs from '~/components/widgets/FAQs.astro';
import Stats from '~/components/widgets/Stats.astro';
import CallToAction from '~/components/widgets/CallToAction.astro';
const meta = {
title: SITE.title,
description: SITE.description,
dontUseTitleTemplate: true,
};
---
<Layout {meta}>
<!-- Hero Widget ******************* -->
<Hero
callToAction={{ text: 'Get template', href: 'https://github.com/onwidget/astrowind', icon: 'tabler:download' }}
callToAction2={{ text: 'Learn more', href: '#features' }}
image={{ src: import('~/assets/images/hero.png'), alt: 'AstroWind Hero Image' }}
>
<Fragment slot="title">
Free template for <span class="hidden lg:inline">create your website <br class="2xl:hidden"/> with</span>
<span class="text-accent dark:text-white highlight"> Astro 2.0</span> + Tailwind CSS
</Fragment>
<Fragment slot="subtitle">
<span class="hidden sm:inline">
<span class="font-semibold">AstroWind</span> is a free, customizable and production-ready template for Astro 2.0 +
Tailwind CSS.</span
>
<span class="block mb-1 sm:hidden font-bold text-blue-600">AstroWind: Production-ready.</span> Suitable for Startups,
Small Business, Sass Websites, Professional Portfolios, Marketing Websites, Landing Pages & Blogs.
</Fragment>
</Hero>
<!-- Note Widget ******************* -->
<Note />
<!-- Features Widget *************** -->
<Features
highlight="Features"
title="What you get with AstroWind"
subtitle="Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque rem aperiam, eaque ipsa quae."
items={[
[
{
title: 'Astro + Tailwind CSS Integration',
description:
'A seamless integration between two great frameworks that offer high productivity, performance and versatility.',
icon: 'tabler:brand-tailwind',
},
{
title: 'Ready-to-use Components',
description:
'Widgets made with Tailwind CSS ready to be used in Marketing Websites, SaaS, Blogs, Personal Profiles, Small Business...',
icon: 'tabler:components',
},
{
title: 'Best Practices',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
icon: 'tabler:list-check',
},
],
[
{
title: 'Excellent Page Speed',
description:
'Having a good page speed impacts organic search ranking, improves user experience (UI/UX) and increase conversion rates.',
icon: 'tabler:rocket',
},
{
title: 'Search Engine Optimization (SEO)',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
icon: 'tabler:arrows-right-left',
},
{
title: 'Open to new ideas and contributions',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
icon: 'tabler:bulb',
},
],
]}
/>
<!-- Content Widget **************** -->
<Content
highlight="Inside template"
title="And what's inside? ..."
items={[
{
title: 'Per ei quaeque sensibus',
description:
'Ex usu illum iudico molestie. Pro ne agam facete mediocritatem, ridens labore facete mea ei. Pro id apeirian dignissim.',
},
{
title: 'Cu imperdiet posidonium sed',
description:
'Amet utinam aliquando ut mea, malis admodum ocurreret nec et, elit tibique cu nec. Nec ex maluisset inciderint, ex quis.',
},
{
title: 'Nulla omittam sadipscing mel ne',
description:
'At sed possim oporteat probatus, justo graece ne nec, minim commodo legimus ut vix. Ut eos iudico quando soleat, nam modus.',
},
]}
image={{
src: import('~/assets/images/caos.jpg'),
alt: 'Colorful Image',
}}
>
<Fragment slot="content">
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">Ad vix debet docendi</h3>
Ne dicta praesent ocurreret has, diam theophrastus at pro. Eos etiam regione ut, persius eripuit quo id. Sit te euismod
tacimates.
</Fragment>
</Content>
<!-- Content Widget **************** -->
<Content
isReversed
isAfterContent
items={[
{
title: 'Per ei quaeque sensibus',
},
{
title: 'Cu imperdiet posidonium sed',
},
{
title: 'Nulla omittam sadipscing mel ne',
},
{
title: 'Per ei quaeque sensibus',
},
{
title: 'Cu imperdiet posidonium sed',
},
{
title: 'Nulla omittam sadipscing mel ne',
},
]}
image={{
src: import('~/assets/images/vintage.jpg'),
alt: 'Vintage Image',
}}
/>
<!-- Steps Widget ****************** -->
<Steps
title="Get your dream website up and running in no time with AstroWind."
items={[
{
title: 'Step 1: <span class="font-medium">Download</span>',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mirisus tempus nulla, sed porttitor est nibh at nulla. Praesent placerat enim ut ex tincidunt vehicula.',
icon: 'tabler:package',
},
{
title: 'Step 2: <span class="font-medium">Add content</em>',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mirisus tempus nulla, sed porttitor est nibh at nulla.',
icon: 'tabler:letter-case',
},
{
title: 'Step 3: <span class="font-medium">Customize styles</span>',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mirisus tempus nulla, sed porttitor est nibh at nulla. Praesent placerat enim ut ex tincidunt vehicula. Fusce sit amet dui tellus.',
icon: 'tabler:paint',
},
{
title: 'Ready!',
icon: 'tabler:check',
},
]}
image={{
src: import('~/assets/images/creativity.jpg'),
alt: 'Steps image',
}}
/>
<!-- Features2 Widget ************** -->
<Features2
title="Most used widgets"
subtitle="Provides frequently used components for building websites using Tailwind CSS"
highlight="Components"
items={[
{
title: 'Headers',
description:
'In general, Headers contain information that makes it easier for visitors to interact with the website.',
icon: 'flat-color-icons:template',
},
{
title: 'Heros',
description:
'If you want your website to get more than its fair share of visitors, the Hero section needs to be stellar.',
icon: 'flat-color-icons:gallery',
},
{
title: 'Features',
description:
'Display your product in action and how the Features actually create a solution for your target customer.',
icon: 'flat-color-icons:todo-list',
},
{
title: 'Content',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.',
icon: 'flat-color-icons:document',
},
{
title: 'Call-to-Action',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.',
icon: 'flat-color-icons:advertising',
},
{
title: 'Pricing',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.',
icon: 'flat-color-icons:currency-exchange',
},
{
title: 'Testimonial',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.',
icon: 'flat-color-icons:voice-presentation',
},
{
title: 'Contact',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.',
icon: 'flat-color-icons:business-contact',
},
{
title: 'Footers',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.',
icon: 'icon-park:page-template',
},
]}
/>
<!-- HighlightedPosts Widget ******* -->
<LatestPosts
title="Find out more content in our Blog"
information={`The blog is used to display AstroWind documentation.
Each new article will be an important step that you will need to know to be an expert in creating a website using Astro + Tailwind CSS.
Astro is a very interesting technology. Thanks.
`}
postIds={[
'get-started-website-with-astro-tailwind-css.md',
'how-to-customize-astrowind-to-your-brand.md',
'useful-resources-to-create-websites.md',
'astrowind-template-in-depth.md',
]}
/>
<!-- FAQs Widget ******************* -->
<FAQs
title="Frequently Asked Questions"
subtitle="Duis turpis dui, fringilla mattis sem nec, fringilla euismod neque. Morbi tincidunt lacus nec tortor scelerisque pulvinar."
highlight="FAQs"
items={[
[
{
question: 'What do I need to start?',
answer:
'Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds. Many say exploration is part of our destiny, but its actually our duty to future generations.',
},
{
question: 'How to install the Astro + Tailwind CSS template?',
answer:
"Well, the way they make shows is, they make one show. That show's called a pilot. Then they show that show to the people who make shows, and on the strength of that one show they decide if they're going to make more shows. Some pilots get picked and become television programs. Some don't, become nothing. She starred in one of the ones that became nothing.",
},
{
question: "What's something that you don't understand?",
answer:
"A flower in my garden, a mystery in my panties. Heart attack never stopped old Big Bear. I didn't even know we were calling him Big Bear.",
},
],
[
{
question: "What's an example of when you changed your mind?",
answer:
"Michael Knight a young loner on a crusade to champion the cause of the innocent. The helpless. The powerless in a world of criminals who operate above the law. Here he comes Here comes Speed Racer. He's a demon on wheels.",
},
{
question: 'What is something that you would like to try again?',
answer:
"A business big enough that it could be listed on the NASDAQ goes belly up. Disappears! It ceases to exist without me. No, you clearly don't know who you're talking to, so let me clue you in.",
},
{
question: 'If you could only ask one question to each person you meet, what would that question be?',
answer:
"This is not about revenge. This is about justice. A lot of things can change in twelve years, Admiral. Well, that's certainly good to know. About four years. I got tired of hearing how young I looked.",
},
],
]}
/>
<!-- Stats Widget ****************** -->
<Stats
items={[
{ name: 'Downloads', value: '132K' },
{ name: 'Stars', value: '24.8K' },
{ name: 'Forks', value: '10.3K' },
{ name: 'Users', value: '48.4K' },
]}
/>
<!-- CallToAction Widget *********** -->
<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>
</Layout>

View File

@@ -0,0 +1,73 @@
---
import Layout from '~/layouts/PageLayout.astro';
import Header from '~/components/widgets/Header.astro';
import Hero2 from '~/components/widgets/Hero2.astro';
import CallToAction from '~/components/widgets/CallToAction.astro';
const meta = {
title: "Mobile App Landing Page",
};
---
<Layout {meta}>
<Fragment slot="announcement"></Fragment>
<Fragment slot="header">
<Header
links={[]}
actions={[
{
type: "ghost",
text: 'Login',
href: '#',
},
{
type: "primary",
text: 'Sign Up',
href: '#',
},
]}
/>
</Fragment>
<!-- Hero2 Widget ******************* -->
<Hero2
callToAction={{ text: 'Get template', href: 'https://github.com/onwidget/astrowind', icon: 'tabler:download' }}
callToAction2={{ text: 'Learn more', href: '#features' }}
image={{ src: import('~/assets/images/hero.png'), alt: 'AstroWind Hero Image' }}
>
<Fragment slot="title">
Free template for <span class="hidden lg:inline">create your website <br />with</span>
<span class="text-accent dark:text-white highlight"> Astro 2.0</span> + Tailwind CSS
</Fragment>
<Fragment slot="subtitle">
<span class="hidden sm:inline">
<span class="font-semibold">AstroWind</span> is a free, customizable and production-ready template for Astro 2.0 +
Tailwind CSS.</span
>
<span class="block mb-1 sm:hidden font-bold text-blue-600">AstroWind: Production-ready.</span> Suitable for Startups,
Small Business, Sass Websites, Professional Portfolios, Marketing Websites, Landing Pages & Blogs.
</Fragment>
</Hero2>
<!-- CallToAction Widget *********** -->
<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>
</Layout>

View File

@@ -0,0 +1,204 @@
---
import Layout from '~/layouts/PageLayout.astro';
import Header from '~/components/widgets/Header.astro';
import Hero2 from '~/components/widgets/Hero2.astro';
import Steps2 from '~/components/widgets/Steps2.astro';
import Content from '~/components/widgets/Content.astro';
import CallToAction from '~/components/widgets/CallToAction.astro';
import { headerData } from '~/data';
const meta = {
title: 'Saas Landing Page',
};
---
<Layout {meta}>
<Fragment slot="header">
<Header
{...headerData}
actions={[
{
type: "ghost",
text: 'Login',
href: '#',
},
{
type: "primary",
text: 'Sign Up',
href: '#',
},
]}
isSticky
/>
</Fragment>
<!-- Hero2 Widget ******************* -->
<Hero2
callToAction={{ text: 'Get template', href: 'https://github.com/onwidget/astrowind', icon: 'tabler:download' }}
callToAction2={{ text: 'Learn more', href: '#features' }}
image={{ src: import('~/assets/images/hero.png'), alt: 'AstroWind Hero Image' }}
>
<Fragment slot="title">
Free template for <br />
<span class="text-accent dark:text-white highlight"> Astro 2.0</span> + Tailwind CSS
</Fragment>
<Fragment slot="subtitle">
<span class="hidden sm:inline">
<span class="font-semibold">AstroWind</span> is a free, customizable and production-ready template for Astro 2.0 +
Tailwind CSS.</span
>
<span class="block mb-1 sm:hidden font-bold text-blue-600">AstroWind: Production-ready.</span> Suitable for Startups,
Small Business, Sass Websites, Professional Portfolios, Marketing Websites, Landing Pages & Blogs.
</Fragment>
</Hero2>
<!-- Content Widget **************** -->
<Content
isReversed
items={[
{
title: 'Per ei quaeque sensibus',
description:
'Ex usu illum iudico molestie. Pro ne agam facete mediocritatem, ridens labore facete mea ei. Pro id apeirian dignissim.',
},
{
title: 'Cu imperdiet posidonium sed',
description:
'Amet utinam aliquando ut mea, malis admodum ocurreret nec et, elit tibique cu nec. Nec ex maluisset inciderint, ex quis.',
},
{
title: 'Nulla omittam sadipscing mel ne',
description:
'At sed possim oporteat probatus, justo graece ne nec, minim commodo legimus ut vix. Ut eos iudico quando soleat, nam modus.',
},
]}
image={{
src: import('~/assets/images/caos.jpg'),
alt: 'Colorful Image',
}}
>
<Fragment slot="content">
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">Ad vix debet docendi</h3>
Ne dicta praesent ocurreret has, diam theophrastus at pro. Eos etiam regione ut, persius eripuit quo id. Sit te euismod
tacimates.
</Fragment>
</Content>
<!-- Content Widget **************** -->
<Content
items={[
{
title: 'Per ei quaeque sensibus',
description:
'Ex usu illum iudico molestie. Pro ne agam facete mediocritatem, ridens labore facete mea ei. Pro id apeirian dignissim.',
},
{
title: 'Cu imperdiet posidonium sed',
description:
'Amet utinam aliquando ut mea, malis admodum ocurreret nec et, elit tibique cu nec. Nec ex maluisset inciderint, ex quis.',
},
{
title: 'Nulla omittam sadipscing mel ne',
description:
'At sed possim oporteat probatus, justo graece ne nec, minim commodo legimus ut vix. Ut eos iudico quando soleat, nam modus.',
},
]}
image={{
src: import('~/assets/images/caos.jpg'),
alt: 'Colorful Image',
}}
>
<Fragment slot="content">
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">Ad vix debet docendi</h3>
Ne dicta praesent ocurreret has, diam theophrastus at pro. Eos etiam regione ut, persius eripuit quo id. Sit te euismod
tacimates.
</Fragment>
</Content>
<!-- Content Widget **************** -->
<Content
isReversed
items={[
{
title: 'Per ei quaeque sensibus',
description:
'Ex usu illum iudico molestie. Pro ne agam facete mediocritatem, ridens labore facete mea ei. Pro id apeirian dignissim.',
},
{
title: 'Cu imperdiet posidonium sed',
description:
'Amet utinam aliquando ut mea, malis admodum ocurreret nec et, elit tibique cu nec. Nec ex maluisset inciderint, ex quis.',
},
{
title: 'Nulla omittam sadipscing mel ne',
description:
'At sed possim oporteat probatus, justo graece ne nec, minim commodo legimus ut vix. Ut eos iudico quando soleat, nam modus.',
},
]}
image={{
src: import('~/assets/images/caos.jpg'),
alt: 'Colorful Image',
}}
>
<Fragment slot="content">
<h3 class="text-2xl font-bold tracking-tight dark:text-white sm:text-3xl mb-2">Ad vix debet docendi</h3>
Ne dicta praesent ocurreret has, diam theophrastus at pro. Eos etiam regione ut, persius eripuit quo id. Sit te euismod
tacimates.
</Fragment>
</Content>
<!-- Steps2 Widget ****************** -->
<Steps2
title="Sed ac magna sit amet risus tristique interdum, at vel velit in hac habitasse platea dictumst."
subtitle="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla, sed porttitor est nibh at nulla. Praesent placerat enim ut ex tincidunt vehicula. Fusce sit amet dui tellus."
callToAction={{
text: 'Get template',
href: 'https://github.com/onwidget/astrowind',
icon: 'tabler:download',
}}
items={[
{
title: 'Responsive Elements',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
},
{
title: 'Flexible Team',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
},
{
title: 'Ecologic Software',
description:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
},
]}
/>
<!-- CallToAction Widget *********** -->
<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>
</Layout>

View File

@@ -0,0 +1,68 @@
---
import Layout from '~/layouts/PageLayout.astro';
import Header from '~/components/widgets/Header.astro';
import Hero from '~/components/widgets/Hero.astro';
import CallToAction from '~/components/widgets/CallToAction.astro';
import { headerData } from '~/data';
const meta = {
title: "Startup Landing Page",
};
---
<Layout {meta}>
<Fragment slot="header">
<Header
{...headerData}
showRssFeed
showToggleTheme
position="left"
/>
</Fragment>
<!-- Hero2 Widget ******************* -->
<Hero
callToAction={{ text: 'Get template', href: 'https://github.com/onwidget/astrowind', icon: 'tabler:download' }}
callToAction2={{ text: 'Learn more', href: '#features' }}
>
<Fragment slot="title">
Free template for <span class="hidden lg:inline">create your website <br />with</span>
<span class="text-accent dark:text-white highlight"> Astro 2.0</span> + Tailwind CSS
</Fragment>
<Fragment slot="subtitle">
<span class="hidden sm:inline">
<span class="font-semibold">AstroWind</span> is a free, customizable and production-ready template for Astro 2.0 +
Tailwind CSS.</span
>
<span class="block mb-1 sm:hidden font-bold text-blue-600">AstroWind: Production-ready.</span> Suitable for Startups,
Small Business, Sass Websites, Professional Portfolios, Marketing Websites, Landing Pages & Blogs.
</Fragment>
<Fragment slot="image">
<iframe width="560" height="315" src="https://www.youtube.com/embed/dsTXcSeAZq8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="width:100%"></iframe>
</Fragment>
</Hero>
<!-- CallToAction Widget *********** -->
<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>
</Layout>

185
src/pages/privacy.md Normal file
View File

@@ -0,0 +1,185 @@
---
title: 'Privacy Policy'
layout: '~/layouts/MarkdownLayout.astro'
---
_Last updated_: January 06, 2023
This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.
We use Your Personal data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy. This Privacy Policy is just a Demo.
## Interpretation and Definitions
### Interpretation
The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.
### Definitions
For the purposes of this Privacy Policy:
- **Account** means a unique account created for You to access our Service or parts of our Service.
- **Company** (referred to as either "the Company", "We", "Us" or "Our" in this Agreement) refers to AstroWind LLC, 1 Cupertino, CA 95014.
- **Cookies** are small files that are placed on Your computer, mobile device or any other device by a website, containing the details of Your browsing history on that website among its many uses.
- **Country** refers to: California, United States
- **Device** means any device that can access the Service such as a computer, a cellphone or a digital tablet.
- **Personal Data** is any information that relates to an identified or identifiable individual.
- **Service** refers to the Website.
- **Service Provider** means any natural or legal person who processes the data on behalf of the Company. It refers to third-party companies or individuals employed by the Company to facilitate the Service, to provide the Service on behalf of the Company, to perform services related to the Service or to assist the Company in analyzing how the Service is used.
- **Usage Data** refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit).
- **Website** refers to AstroWind, accessible from [https://astrowind.vercel.app](https://astrowind.vercel.app)
- **You** means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.
## Collecting and Using Your Personal Data
### Types of Data Collected
#### Personal Data
While using Our Service, We may ask You to provide Us with certain personally identifiable information that can be used to contact or identify You. Personally identifiable information may include, but is not limited to:
- Usage Data
#### Usage Data
Usage Data is collected automatically when using the Service.
Usage Data may include information such as Your Device's Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that You visit, the time and date of Your visit, the time spent on those pages, unique device identifiers and other diagnostic data.
When You access the Service by or through a mobile device, We may collect certain information automatically, including, but not limited to, the type of mobile device You use, Your mobile device unique ID, the IP address of Your mobile device, Your mobile operating system, the type of mobile Internet browser You use, unique device identifiers and other diagnostic data.
We may also collect information that Your browser sends whenever You visit our Service or when You access the Service by or through a mobile device.
#### Tracking Technologies and Cookies
We use Cookies and similar tracking technologies to track the activity on Our Service and store certain information. Tracking technologies used are beacons, tags, and scripts to collect and track information and to improve and analyze Our Service. The technologies We use may include:
- **Cookies or Browser Cookies.** A cookie is a small file placed on Your Device. You can instruct Your browser to refuse all Cookies or to indicate when a Cookie is being sent. However, if You do not accept Cookies, You may not be able to use some parts of our Service. Unless you have adjusted Your browser setting so that it will refuse Cookies, our Service may use Cookies.
- **Web Beacons.** Certain sections of our Service and our emails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit the Company, for example, to count users who have visited those pages or opened an email and for other related website statistics (for example, recording the popularity of a certain section and verifying system and server integrity).
Cookies can be "Persistent" or "Session" Cookies. Persistent Cookies remain on Your personal computer or mobile device when You go offline, while Session Cookies are deleted as soon as You close Your web browser.
We use both Session and Persistent Cookies for the purposes set out below:
- **Necessary / Essential Cookies**
Type: Session Cookies
Administered by: Us
Purpose: These Cookies are essential to provide You with services available through the Website and to enable You to use some of its features. They help to authenticate users and prevent fraudulent use of user accounts. Without these Cookies, the services that You have asked for cannot be provided, and We only use these Cookies to provide You with those services.
- **Cookies Policy / Notice Acceptance Cookies**
Type: Persistent Cookies
Administered by: Us
Purpose: These Cookies identify if users have accepted the use of cookies on the Website.
- **Functionality Cookies**
Type: Persistent Cookies
Administered by: Us
Purpose: These Cookies allow us to remember choices You make when You use the Website, such as remembering your login details or language preference. The purpose of these Cookies is to provide You with a more personal experience and to avoid You having to re-enter your preferences every time You use the Website.
For more information about the cookies we use and your choices regarding cookies, please visit our Cookies Policy or the Cookies section of our Privacy Policy.
## Use of Your Personal Data
The Company may use Personal Data for the following purposes:
- **To provide and maintain our Service**, including to monitor the usage of our Service.
- **To manage Your Account:** to manage Your registration as a user of the Service. The Personal Data You provide can give You access to different functionalities of the Service that are available to You as a registered user.
- **For the performance of a contract:** the development, compliance and undertaking of the purchase contract for the products, items or services You have purchased or of any other contract with Us through the Service.
- **To contact You:** To contact You by email, telephone calls, SMS, or other equivalent forms of electronic communication, such as a mobile application's push notifications regarding updates or informative communications related to the functionalities, products or contracted services, including the security updates, when necessary or reasonable for their implementation.
- **To provide You** with news, special offers and general information about other goods, services and events which we offer that are similar to those that you have already purchased or enquired about unless You have opted not to receive such information.
- **To manage Your requests:** To attend and manage Your requests to Us.
- **For business transfers:** We may use Your information to evaluate or conduct a merger, divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all of Our assets, whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in which Personal Data held by Us about our Service users is among the assets transferred.
- **For other purposes**: We may use Your information for other purposes, such as data analysis, identifying usage trends, determining the effectiveness of our promotional campaigns and to evaluate and improve our Service, products, services, marketing and your experience.
We may share Your personal information in the following situations:
- **With Service Providers:** We may share Your personal information with Service Providers to monitor and analyze the use of our Service, to contact You.
- **For business transfers:** We may share or transfer Your personal information in connection with, or during negotiations of, any merger, sale of Company assets, financing, or acquisition of all or a portion of Our business to another company.
- **With Affiliates:** We may share Your information with Our affiliates, in which case we will require those affiliates to honor this Privacy Policy. Affiliates include Our parent company and any other subsidiaries, joint venture partners or other companies that We control or that are under common control with Us.
- **With business partners:** We may share Your information with Our business partners to offer You certain products, services or promotions.
- **With other users:** when You share personal information or otherwise interact in the public areas with other users, such information may be viewed by all users and may be publicly distributed outside.
- **With Your consent**: We may disclose Your personal information for any other purpose with Your consent.
## Retention of Your Personal Data
The Company will retain Your Personal Data only for as long as is necessary for the purposes set out in this Privacy Policy. We will retain and use Your Personal Data to the extent necessary to comply with our legal obligations (for example, if we are required to retain your data to comply with applicable laws), resolve disputes, and enforce our legal agreements and policies.
The Company will also retain Usage Data for internal analysis purposes. Usage Data is generally retained for a shorter period of time, except when this data is used to strengthen the security or to improve the functionality of Our Service, or We are legally obligated to retain this data for longer time periods.
## Transfer of Your Personal Data
Your information, including Personal Data, is processed at the Company's operating offices and in any other places where the parties involved in the processing are located. It means that this information may be transferred to — and maintained on — computers located outside of Your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from Your jurisdiction.
Your consent to this Privacy Policy followed by Your submission of such information represents Your agreement to that transfer.
The Company will take all steps reasonably necessary to ensure that Your data is treated securely and in accordance with this Privacy Policy and no transfer of Your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of Your data and other personal information.
## Delete Your Personal Data
You have the right to delete or request that We assist in deleting the Personal Data that We have collected about You.
Our Service may give You the ability to delete certain information about You from within the Service.
You may update, amend, or delete Your information at any time by signing in to Your Account, if you have one, and visiting the account settings section that allows you to manage Your personal information. You may also contact Us to request access to, correct, or delete any personal information that You have provided to Us.
Please note, however, that We may need to retain certain information when we have a legal obligation or lawful basis to do so.
## Disclosure of Your Personal Data
### Business Transactions
If the Company is involved in a merger, acquisition or asset sale, Your Personal Data may be transferred. We will provide notice before Your Personal Data is transferred and becomes subject to a different Privacy Policy.
#### Law enforcement
Under certain circumstances, the Company may be required to disclose Your Personal Data if required to do so by law or in response to valid requests by public authorities (e.g. a court or a government agency).
#### Other legal requirements
The Company may disclose Your Personal Data in the good faith belief that such action is necessary to:
- Comply with a legal obligation
- Protect and defend the rights or property of the Company
- Prevent or investigate possible wrongdoing in connection with the Service
- Protect the personal safety of Users of the Service or the public
- Protect against legal liability
## Security of Your Personal Data
The security of Your Personal Data is important to Us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While We strive to use commercially acceptable means to protect Your Personal Data, We cannot guarantee its absolute security.
## Children's Privacy
Our Service does not address anyone under the age of 13. We do not knowingly collect personally identifiable information from anyone under the age of 13. If You are a parent or guardian and You are aware that Your child has provided Us with Personal Data, please contact Us. If We become aware that We have collected Personal Data from anyone under the age of 13 without verification of parental consent, We take steps to remove that information from Our servers.
If We need to rely on consent as a legal basis for processing Your information and Your country requires consent from a parent, We may require Your parent's consent before We collect and use that information.
## Links to Other Websites
Our Service may contain links to other websites that are not operated by Us. If You click on a third party link, You will be directed to that third party's site. We strongly advise You to review the Privacy Policy of every site You visit.
We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.
## Changes to this Privacy Policy
We may update Our Privacy Policy from time to time. We will notify You of any changes by posting the new Privacy Policy on this page.
We will let You know via email and/or a prominent notice on Our Service, prior to the change becoming effective and update the "Last updated" date at the top of this Privacy Policy.
You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.
## Contact Us
If you have any questions about this Privacy Policy, You can contact us:
- By email: somecoolemail@domain.com

29
src/pages/rss.xml.ts Normal file
View File

@@ -0,0 +1,29 @@
import rss from '@astrojs/rss';
import { SITE, BLOG } from '~/config.mjs';
import { fetchPosts } from '~/utils/blog';
import { getPermalink } from '~/utils/permalinks';
export const get = async () => {
if (BLOG.disabled) {
return new Response(null, {
status: 404,
statusText: 'Not found',
});
}
const posts = await fetchPosts();
return rss({
title: `${SITE.name}s Blog`,
description: SITE.description,
site: import.meta.env.SITE,
items: posts.map((post) => ({
link: getPermalink(post.permalink, 'post'),
title: post.title,
description: post.description,
pubDate: post.publishDate,
})),
});
};

120
src/pages/terms.md Normal file
View File

@@ -0,0 +1,120 @@
---
title: 'Terms and Conditions'
layout: '~/layouts/MarkdownLayout.astro'
---
_Last updated_: January 06, 2023
Please read these terms and conditions carefully before using Our Service.
## Interpretation and Definitions
### Interpretation
The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.
### Definitions
For the purposes of these Terms and Conditions:
- **Affiliate** means an entity that controls, is controlled by or is under common control with a party, where "control" means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority.
- **Country** refers to: California, United States
- **Company** (referred to as either "the Company", "We", "Us" or "Our" in this Agreement) refers to AstroWind LLC, 1 Cupertino, CA 95014.
- **Device** means any device that can access the Service such as a computer, a cellphone or a digital tablet.
- **Service** refers to the Website.
- **Terms and Conditions** (also referred as "Terms") mean these Terms and Conditions that form the entire agreement between You and the Company regarding the use of the Service. This Terms and Conditions agreement is a Demo.
- **Third-party Social Media Service** means any services or content (including data, information, products or services) provided by a third-party that may be displayed, included or made available by the Service.
- **Website** refers to AstroWind, accessible from [https://astrowind.vercel.app](https://astrowind.vercel.app)
- **You** means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.
## Acknowledgment
These are the Terms and Conditions governing the use of this Service and the agreement that operates between You and the Company. These Terms and Conditions set out the rights and obligations of all users regarding the use of the Service.
Your access to and use of the Service is conditioned on Your acceptance of and compliance with these Terms and Conditions. These Terms and Conditions apply to all visitors, users and others who access or use the Service.
By accessing or using the Service You agree to be bound by these Terms and Conditions. If You disagree with any part of these Terms and Conditions then You may not access the Service.
You represent that you are over the age of 18\. The Company does not permit those under 18 to use the Service.
Your access to and use of the Service is also conditioned on Your acceptance of and compliance with the Privacy Policy of the Company. Our Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your personal information when You use the Application or the Website and tells You about Your privacy rights and how the law protects You. Please read Our Privacy Policy carefully before using Our Service.
## Links to Other Websites
Our Service may contain links to third-party web sites or services that are not owned or controlled by the Company.
The Company has no control over, and assumes no responsibility for, the content, privacy policies, or practices of any third party web sites or services. You further acknowledge and agree that the Company shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such content, goods or services available on or through any such web sites or services.
We strongly advise You to read the terms and conditions and privacy policies of any third-party web sites or services that You visit.
## Termination
We may terminate or suspend Your access immediately, without prior notice or liability, for any reason whatsoever, including without limitation if You breach these Terms and Conditions.
Upon termination, Your right to use the Service will cease immediately.
## Limitation of Liability
Notwithstanding any damages that You might incur, the entire liability of the Company and any of its suppliers under any provision of this Terms and Your exclusive remedy for all of the foregoing shall be limited to the amount actually paid by You through the Service or 100 USD if You haven't purchased anything through the Service.
To the maximum extent permitted by applicable law, in no event shall the Company or its suppliers be liable for any special, incidental, indirect, or consequential damages whatsoever (including, but not limited to, damages for loss of profits, loss of data or other information, for business interruption, for personal injury, loss of privacy arising out of or in any way related to the use of or inability to use the Service, third-party software and/or third-party hardware used with the Service, or otherwise in connection with any provision of this Terms), even if the Company or any supplier has been advised of the possibility of such damages and even if the remedy fails of its essential purpose.
Some states do not allow the exclusion of implied warranties or limitation of liability for incidental or consequential damages, which means that some of the above limitations may not apply. In these states, each party's liability will be limited to the greatest extent permitted by law.
## "AS IS" and "AS AVAILABLE" Disclaimer
The Service is provided to You "AS IS" and "AS AVAILABLE" and with all faults and defects without warranty of any kind. To the maximum extent permitted under applicable law, the Company, on its own behalf and on behalf of its Affiliates and its and their respective licensors and service providers, expressly disclaims all warranties, whether express, implied, statutory or otherwise, with respect to the Service, including all implied warranties of merchantability, fitness for a particular purpose, title and non-infringement, and warranties that may arise out of course of dealing, course of performance, usage or trade practice. Without limitation to the foregoing, the Company provides no warranty or undertaking, and makes no representation of any kind that the Service will meet Your requirements, achieve any intended results, be compatible or work with any other software, applications, systems or services, operate without interruption, meet any performance or reliability standards or be error free or that any errors or defects can or will be corrected.
Without limiting the foregoing, neither the Company nor any of the company's provider makes any representation or warranty of any kind, express or implied: (i) as to the operation or availability of the Service, or the information, content, and materials or products included thereon; (ii) that the Service will be uninterrupted or error-free; (iii) as to the accuracy, reliability, or currency of any information or content provided through the Service; or (iv) that the Service, its servers, the content, or e-mails sent from or on behalf of the Company are free of viruses, scripts, trojan horses, worms, malware, timebombs or other harmful components.
Some jurisdictions do not allow the exclusion of certain types of warranties or limitations on applicable statutory rights of a consumer, so some or all of the above exclusions and limitations may not apply to You. But in such a case the exclusions and limitations set forth in this section shall be applied to the greatest extent enforceable under applicable law.
## Governing Law
The laws of the Country, excluding its conflicts of law rules, shall govern this Terms and Your use of the Service. Your use of the Application may also be subject to other local, state, national, or international laws.
## Disputes Resolution
If You have any concern or dispute about the Service, You agree to first try to resolve the dispute informally by contacting the Company.
## For European Union (EU) Users
If You are a European Union consumer, you will benefit from any mandatory provisions of the law of the country in which you are resident in.
## United States Legal Compliance
You represent and warrant that (i) You are not located in a country that is subject to the United States government embargo, or that has been designated by the United States government as a "terrorist supporting" country, and (ii) You are not listed on any United States government list of prohibited or restricted parties.
## Severability and Waiver
### Severability
If any provision of these Terms is held to be unenforceable or invalid, such provision will be changed and interpreted to accomplish the objectives of such provision to the greatest extent possible under applicable law and the remaining provisions will continue in full force and effect.
### Waiver
Except as provided herein, the failure to exercise a right or to require performance of an obligation under these Terms shall not effect a party's ability to exercise such right or require such performance at any time thereafter nor shall the waiver of a breach constitute a waiver of any subsequent breach.
## Translation Interpretation
These Terms and Conditions may have been translated if We have made them available to You on our Service. You agree that the original English text shall prevail in the case of a dispute.
## Changes to These Terms and Conditions
We reserve the right, at Our sole discretion, to modify or replace these Terms at any time. If a revision is material We will make reasonable efforts to provide at least 30 days' notice prior to any new terms taking effect. What constitutes a material change will be determined at Our sole discretion.
By continuing to access or use Our Service after those revisions become effective, You agree to be bound by the revised terms. If You do not agree to the new terms, in whole or in part, please stop using the website and the Service.
## Contact Us
If you have any questions about these Terms and Conditions, You can contact us:
- By email: somecoolemail@domain.com

38
src/types.ts Normal file
View File

@@ -0,0 +1,38 @@
export interface Post {
id: string;
slug: string;
publishDate: Date;
title: string;
description?: string;
image?: string;
canonical?: string | URL;
permalink?: string;
draft?: boolean;
excerpt?: string;
category?: string;
tags?: Array<string>;
author?: string;
Content: unknown;
content?: string;
readingTime?: number;
}
export interface MetaSEO {
title?: string;
description?: string;
image?: string;
canonical?: string | URL;
noindex?: boolean;
nofollow?: boolean;
ogTitle?: string;
ogType?: string;
}

124
src/utils/blog.ts Normal file
View File

@@ -0,0 +1,124 @@
import { getCollection } from 'astro:content';
import type { CollectionEntry } from 'astro:content';
import type { Post } from '~/types';
import { cleanSlug, trimSlash, POST_PERMALINK_PATTERN } from './permalinks';
const generatePermalink = async ({ id, slug, publishDate, category }) => {
const year = String(publishDate.getFullYear()).padStart(4, '0');
const month = String(publishDate.getMonth() + 1).padStart(2, '0');
const day = String(publishDate.getDate()).padStart(2, '0');
const hour = String(publishDate.getHours()).padStart(2, '0');
const minute = String(publishDate.getMinutes()).padStart(2, '0');
const second = String(publishDate.getSeconds()).padStart(2, '0');
const permalink = POST_PERMALINK_PATTERN.replace('%slug%', slug)
.replace('%id%', id)
.replace('%category%', category || '')
.replace('%year%', year)
.replace('%month%', month)
.replace('%day%', day)
.replace('%hour%', hour)
.replace('%minute%', minute)
.replace('%second%', second);
return permalink
.split('/')
.map((el) => trimSlash(el))
.filter((el) => !!el)
.join('/');
};
const getNormalizedPost = async (post: CollectionEntry<'post'>): Promise<Post> => {
const { id, slug: rawSlug = '', data } = post;
const { Content, remarkPluginFrontmatter } = await post.render();
const {
tags: rawTags = [],
category: rawCategory,
author = 'Anonymous',
publishDate: rawPublishDate = new Date(),
...rest
} = data;
const slug = cleanSlug(rawSlug.split('/').pop());
const publishDate = new Date(rawPublishDate);
const category = rawCategory ? cleanSlug(rawCategory) : undefined;
const tags = rawTags.map((tag: string) => cleanSlug(tag));
return {
id: id,
slug: slug,
publishDate: publishDate,
category: category,
tags: tags,
author: author,
...rest,
Content: Content,
// or 'body' in case you consume from API
permalink: await generatePermalink({ id, slug, publishDate, category }),
readingTime: remarkPluginFrontmatter?.readingTime,
};
};
const load = async function (): Promise<Array<Post>> {
const posts = await getCollection('post');
const normalizedPosts = posts.map(async (post) => await getNormalizedPost(post));
const results = (await Promise.all(normalizedPosts))
.sort((a, b) => b.publishDate.valueOf() - a.publishDate.valueOf())
.filter((post) => !post.draft);
return results;
};
let _posts: Array<Post>;
/** */
export const fetchPosts = async (): Promise<Array<Post>> => {
if (!_posts) {
_posts = await load();
}
return _posts;
};
/** */
export const findPostsBySlugs = async (slugs: Array<string>): Promise<Array<Post>> => {
if (!Array.isArray(slugs)) return [];
const posts = await fetchPosts();
return slugs.reduce(function (r: Array<Post>, slug: string) {
posts.some(function (post: Post) {
return slug === post.slug && r.push(post);
});
return r;
}, []);
};
/** */
export const findPostsByIds = async (ids: Array<string>): Promise<Array<Post>> => {
if (!Array.isArray(ids)) return [];
const posts = await fetchPosts();
return ids.reduce(function (r: Array<Post>, id: string) {
posts.some(function (post: Post) {
return id === post.id && r.push(post);
});
return r;
}, []);
};
/** */
export const findLatestPosts = async ({ count }: { count?: number }): Promise<Array<Post>> => {
const _count = count || 4;
const posts = await fetchPosts();
return posts ? posts.slice(0, _count) : [];
};

18
src/utils/directories.ts Normal file
View File

@@ -0,0 +1,18 @@
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
/** */
export const getProjectRootDir = (): string => {
const mode = import.meta.env.MODE;
return mode === 'production' ? path.join(__dirname, '../') : path.join(__dirname, '../../');
};
const __srcFolder = path.join(getProjectRootDir(), '/src');
/** */
export const getRelativeUrlByFilePath = (filepath: string): string => {
return filepath.replace(__srcFolder, '');
};

11
src/utils/frontmatter.mjs Normal file
View File

@@ -0,0 +1,11 @@
import getReadingTime from 'reading-time';
import { toString } from 'mdast-util-to-string';
export function readingTimeRemarkPlugin() {
return function (tree, file) {
const textOnPage = toString(tree);
const readingTime = Math.ceil(getReadingTime(textOnPage).minutes);
file.data.astro.frontmatter.readingTime = readingTime
};
}

37
src/utils/images.ts Normal file
View File

@@ -0,0 +1,37 @@
const load = async function () {
let images: Record<string, () => Promise<unknown>> | undefined = undefined;
try {
images = import.meta.glob('~/assets/images/**');
} catch (e) {
// continue regardless of error
}
return images;
};
let _images;
/** */
export const fetchLocalImages = async () => {
_images = _images || load();
return await _images;
};
/** */
export const findImage = async (imagePath?: string) => {
if (typeof imagePath !== 'string') {
return null;
}
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) {
return imagePath;
}
if (!imagePath.startsWith('~/assets')) {
return null;
} // For now only consume images using ~/assets alias (or absolute)
const images = await fetchLocalImages();
const key = imagePath.replace('~/', '/src/');
return typeof images[key] === 'function' ? (await images[key]())['default'] : null;
};

73
src/utils/permalinks.ts Normal file
View File

@@ -0,0 +1,73 @@
import slugify from 'limax';
import { SITE, BLOG } from '~/config.mjs';
import { trim } from '~/utils/utils';
export const trimSlash = (s: string) => trim(trim(s, '/'));
const createPath = (...params: string[]) => {
const paths = params
.map((el) => trimSlash(el))
.filter((el) => !!el)
.join('/');
return '/' + paths + (SITE.trailingSlash && paths ? '/' : '');
};
const BASE_PATHNAME = SITE.basePathname;
export const cleanSlug = (text = '') =>
trimSlash(text)
.split('/')
.map((slug) => slugify(slug))
.join('/');
export const POST_PERMALINK_PATTERN = trimSlash(BLOG?.post?.permalink || '/%slug%');
export const BLOG_BASE = cleanSlug(BLOG?.list?.pathname);
export const CATEGORY_BASE = cleanSlug(BLOG?.category?.pathname || 'category');
export const TAG_BASE = cleanSlug(BLOG?.tag?.pathname) || 'tag';
/** */
export const getCanonical = (path = ''): string | URL => new URL(path, SITE.origin);
/** */
export const getPermalink = (slug = '', type = 'page'): string => {
let permalink: string;
switch (type) {
case 'category':
permalink = createPath(CATEGORY_BASE, trimSlash(slug));
break;
case 'tag':
permalink = createPath(TAG_BASE, trimSlash(slug));
break;
case 'post':
permalink = createPath(trimSlash(slug));
break;
case 'page':
default:
permalink = createPath(slug);
break;
}
return definitivePermalink(permalink);
};
/** */
export const getHomePermalink = (): string => getPermalink('/');
/** */
export const getBlogPermalink = (): string => getPermalink(BLOG_BASE);
/** */
export const getAsset = (path: string): string =>
'/' +
[BASE_PATHNAME, path]
.map((el) => trimSlash(el))
.filter((el) => !!el)
.join('/');
/** */
const definitivePermalink = (permalink: string): string => createPath(BASE_PATHNAME, permalink);

21
src/utils/utils.ts Normal file
View File

@@ -0,0 +1,21 @@
import { DATE_FORMATTER } from '~/config.mjs';
const formatter =
DATE_FORMATTER ||
new Intl.DateTimeFormat('en', {
year: 'numeric',
month: 'short',
day: 'numeric',
timeZone: 'UTC',
});
/* eslint-disable no-mixed-spaces-and-tabs */
export const getFormattedDate = (date: Date) => (date ? formatter.format(date) : '');
export const trim = (str = '', ch?: string) => {
let start = 0,
end = str.length || 0;
while (start < end && str[start] === ch) ++start;
while (end > start && str[end - 1] === ch) --end;
return start > 0 || end < str.length ? str.substring(start, end) : str;
};

22
tailwind.config.cjs Normal file
View File

@@ -0,0 +1,22 @@
const defaultTheme = require('tailwindcss/defaultTheme');
const colors = require('tailwindcss/colors');
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}'],
theme: {
extend: {
colors: {
primary: 'var(--aw-color-primary)',
secondary: 'var(--aw-color-secondary)',
accent: 'var(--aw-color-accent)',
},
fontFamily: {
sans: ['var(--aw-font-sans)', ...defaultTheme.fontFamily.sans],
serif: ['var(--aw-font-serif)', ...defaultTheme.fontFamily.serif],
heading: ['var(--aw-font-heading)', ...defaultTheme.fontFamily.sans],
},
},
},
plugins: [require('@tailwindcss/typography')],
darkMode: 'class',
};

11
tsconfig.json Normal file
View File

@@ -0,0 +1,11 @@
{
"extends": "astro/tsconfigs/base",
"compilerOptions": {
"strictNullChecks": true,
"allowJs": true,
"baseUrl": ".",
"paths": {
"~/*": ["src/*"]
}
}
}

15
vercel.json Normal file
View File

@@ -0,0 +1,15 @@
{
"cleanUrls": true,
"trailingSlash": false,
"headers": [
{
"source": "/_astro/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}

17
vscode.tailwind.json Normal file
View File

@@ -0,0 +1,17 @@
{
"version": 1.1,
"atDirectives": [
{
"name": "@tailwind",
"description": "@tailwind tailwindcss"
},
{
"name": "@layer",
"description": "@layer tailwindcss"
},
{
"name": "@apply",
"description": "@apply tailwindcss"
}
]
}