Merge branch 'develop'
12
.editorconfig
Normal 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
|
@ -0,0 +1,4 @@
|
|||
dist
|
||||
node_modules
|
||||
.github
|
||||
types.generated.d.ts
|
53
.eslintrc.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
/** @type {import("eslint").Linter.Config} */
|
||||
module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
es2022: true,
|
||||
browser: true,
|
||||
},
|
||||
extends: ['eslint:recommended', 'plugin:astro/recommended'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
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',
|
||||
},
|
||||
],
|
||||
};
|
5
.gitignore
vendored
|
@ -18,3 +18,8 @@ pnpm-debug.log*
|
|||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
|
||||
.astro
|
2
.npmrc
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Expose Astro dependencies for `pnpm` users
|
||||
shamefully-hoist=true
|
17963
.pnp.cjs
generated
Normal file
2090
.pnp.loader.mjs
generated
Normal file
4
.prettierignore
Normal file
|
@ -0,0 +1,4 @@
|
|||
dist
|
||||
node_modules
|
||||
.github
|
||||
.changeset
|
13
.prettierrc.js
Normal 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
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"startCommand": "npm start",
|
||||
"env": {
|
||||
"ENABLE_CJS_IMPORTS": true
|
||||
}
|
||||
}
|
495
.vscode/astrowind/config-schema.json
vendored
Normal file
|
@ -0,0 +1,495 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"site": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"site": {
|
||||
"type": "string"
|
||||
},
|
||||
"base": {
|
||||
"type": "string"
|
||||
},
|
||||
"trailingSlash": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"googleSiteVerificationId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"site",
|
||||
"base",
|
||||
"trailingSlash"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default": {
|
||||
"type": "string"
|
||||
},
|
||||
"template": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"default",
|
||||
"template"
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"robots": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"follow": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"index",
|
||||
"follow"
|
||||
]
|
||||
},
|
||||
"openGraph": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"site_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"site_name",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"twitter": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"handle": {
|
||||
"type": "string"
|
||||
},
|
||||
"site": {
|
||||
"type": "string"
|
||||
},
|
||||
"cardType": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"handle",
|
||||
"site",
|
||||
"cardType"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"title",
|
||||
"description",
|
||||
"robots",
|
||||
"openGraph",
|
||||
"twitter"
|
||||
]
|
||||
},
|
||||
"i18n": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
"textDirection": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"language",
|
||||
"textDirection"
|
||||
]
|
||||
},
|
||||
"apps": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"blog": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"postsPerPage": {
|
||||
"type": "integer"
|
||||
},
|
||||
"post": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"permalink": {
|
||||
"type": "string"
|
||||
},
|
||||
"robots": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"follow": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"index"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isEnabled",
|
||||
"permalink",
|
||||
"robots"
|
||||
]
|
||||
},
|
||||
"list": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"pathname": {
|
||||
"type": "string"
|
||||
},
|
||||
"robots": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"follow": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"index"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isEnabled",
|
||||
"pathname",
|
||||
"robots"
|
||||
]
|
||||
},
|
||||
"category": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"pathname": {
|
||||
"type": "string"
|
||||
},
|
||||
"robots": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"follow": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"index"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isEnabled",
|
||||
"pathname",
|
||||
"robots"
|
||||
]
|
||||
},
|
||||
"tag": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"isEnabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"pathname": {
|
||||
"type": "string"
|
||||
},
|
||||
"robots": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"index": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"follow": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"index"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isEnabled",
|
||||
"pathname",
|
||||
"robots"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isEnabled",
|
||||
"postsPerPage",
|
||||
"post",
|
||||
"list",
|
||||
"category",
|
||||
"tag"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"blog"
|
||||
]
|
||||
},
|
||||
"analytics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"vendors": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"googleAnalytics": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"partytown": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"googleAnalytics"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"vendors"
|
||||
]
|
||||
},
|
||||
"ui": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"theme": {
|
||||
"type": "string"
|
||||
},
|
||||
"tokens": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"colors": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default": {
|
||||
"type": "string"
|
||||
},
|
||||
"heading": {
|
||||
"type": "string"
|
||||
},
|
||||
"muted": {
|
||||
"type": "string"
|
||||
},
|
||||
"bgPage": {
|
||||
"type": "string"
|
||||
},
|
||||
"primary": {
|
||||
"type": "string"
|
||||
},
|
||||
"secondary": {
|
||||
"type": "string"
|
||||
},
|
||||
"accent": {
|
||||
"type": "string"
|
||||
},
|
||||
"info": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
},
|
||||
"warning": {
|
||||
"type": "string"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"link": {
|
||||
"type": "string"
|
||||
},
|
||||
"linkActive": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"default",
|
||||
"heading",
|
||||
"muted",
|
||||
"bgPage",
|
||||
"primary",
|
||||
"secondary",
|
||||
"accent",
|
||||
"info",
|
||||
"success",
|
||||
"warning",
|
||||
"error",
|
||||
"link",
|
||||
"linkActive"
|
||||
]
|
||||
},
|
||||
"fonts": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sans": {
|
||||
"type": "string"
|
||||
},
|
||||
"serif": {
|
||||
"type": "string"
|
||||
},
|
||||
"heading": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sans",
|
||||
"serif",
|
||||
"heading"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"colors",
|
||||
"fonts"
|
||||
]
|
||||
},
|
||||
"dark": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"colors": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default": {
|
||||
"type": "string"
|
||||
},
|
||||
"heading": {
|
||||
"type": "string"
|
||||
},
|
||||
"muted": {
|
||||
"type": "string"
|
||||
},
|
||||
"bgPage": {
|
||||
"type": "string"
|
||||
},
|
||||
"primary": {
|
||||
"type": "string"
|
||||
},
|
||||
"secondary": {
|
||||
"type": "string"
|
||||
},
|
||||
"accent": {
|
||||
"type": "string"
|
||||
},
|
||||
"info": {
|
||||
"type": "string"
|
||||
},
|
||||
"success": {
|
||||
"type": "string"
|
||||
},
|
||||
"warning": {
|
||||
"type": "string"
|
||||
},
|
||||
"error": {
|
||||
"type": "string"
|
||||
},
|
||||
"link": {
|
||||
"type": "string"
|
||||
},
|
||||
"linkActive": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"default",
|
||||
"heading",
|
||||
"muted",
|
||||
"bgPage",
|
||||
"primary",
|
||||
"secondary",
|
||||
"accent",
|
||||
"info",
|
||||
"success",
|
||||
"warning",
|
||||
"error",
|
||||
"link",
|
||||
"linkActive"
|
||||
]
|
||||
},
|
||||
"fonts": {
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"colors",
|
||||
"fonts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"default",
|
||||
"dark"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"theme",
|
||||
"tokens"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"site",
|
||||
"metadata",
|
||||
"i18n",
|
||||
"apps",
|
||||
"analytics",
|
||||
"ui"
|
||||
]
|
||||
}
|
10
.vscode/extensions.json
vendored
Normal 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
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
20
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"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"
|
||||
},
|
||||
"yaml.schemas": {
|
||||
"./.vscode/astrowind/config-schema.json": "/src/config.yaml"
|
||||
}
|
||||
}
|
BIN
.yarn/install-state.gz
Normal file
275
README.md
|
@ -1,38 +1,275 @@
|
|||
# DragonsChild
|
||||
# 🚀 AstroWind
|
||||
|
||||
Dark themed website template built on AstroJS, designed for saas/startup business.
|
||||
<img src="https://raw.githubusercontent.com/onwidget/.github/main/resources/astrowind/lighthouse-score.png" align="right"
|
||||
alt="AstroWind Lighthouse Score" width="100" height="358">
|
||||
|
||||
## Tech stack:
|
||||
🌟 _Most *starred* & *forked* Astro theme in 2022 & 2023_. 🌟
|
||||
|
||||
Astro, React, Tailwind, Framer Motion
|
||||
**AstroWind** is a free and open-source template to make your website using **[Astro 4.0](https://astro.build/) + [Tailwind CSS](https://tailwindcss.com/)**. Ready to start a new project and designed taking into account web best practices.
|
||||
|
||||
## Live link
|
||||
https://dragonschildstudios.com/
|
||||
- ✅ **Production-ready** scores in **PageSpeed Insights** reports.
|
||||
- ✅ Integration with **Tailwind CSS** supporting **Dark mode** and **_RTL_**.
|
||||
- ✅ **Fast and SEO friendly blog** with automatic **RSS feed**, **MDX** support, **Categories & Tags**, **Social Share**, ...
|
||||
- ✅ **Image Optimization** (using new **Astro Assets** and **Unpic** for Universal image CDN).
|
||||
- ✅ Generation of **project sitemap** based on your routes.
|
||||
- ✅ **Open Graph tags** for social media sharing.
|
||||
- ✅ **Analytics** built-in Google Analytics, and Splitbee integration.
|
||||
|
||||
## Project Structure
|
||||
<br>
|
||||
|
||||
<img src="https://raw.githubusercontent.com/onwidget/.github/main/resources/astrowind/screenshot-astro4.png" 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 4.0](https://astro.build/) + [Tailwind CSS](https://tailwindcss.com/). It's a free theme which 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/
|
||||
│ └── favicon.svg
|
||||
│ ├── _headers
|
||||
│ └── robots.txt
|
||||
├── src/
|
||||
│ ├── assets
|
||||
│ │ ├── icons
|
||||
│ │ ├── images
|
||||
│ │ └── logos
|
||||
│ ├── components
|
||||
│ ├── layouts
|
||||
│ ├── pages
|
||||
│ └── styles
|
||||
└── package.json
|
||||
│ ├── assets/
|
||||
│ │ ├── favicons/
|
||||
│ │ ├── images/
|
||||
│ │ └── styles/
|
||||
│ │ └── tailwind.css
|
||||
│ ├── components/
|
||||
│ │ ├── blog/
|
||||
│ │ ├── common/
|
||||
│ │ ├── ui/
|
||||
│ │ ├── widgets/
|
||||
│ │ │ ├── Header.astro
|
||||
│ │ │ └── ...
|
||||
│ │ ├── CustomStyles.astro
|
||||
│ │ ├── Favicons.astro
|
||||
│ │ └── Logo.astro
|
||||
│ ├── content/
|
||||
│ │ ├── post/
|
||||
│ │ │ ├── post-slug-1.md
|
||||
│ │ │ ├── post-slug-2.mdx
|
||||
│ │ │ └── ...
|
||||
│ │ └-- config.ts
|
||||
│ ├── layouts/
|
||||
│ │ ├── Layout.astro
|
||||
│ │ ├── MarkdownLayout.astro
|
||||
│ │ └── PageLayout.astro
|
||||
│ ├── pages/
|
||||
│ │ ├── [...blog]/
|
||||
│ │ │ ├── [category]/
|
||||
│ │ │ ├── [tag]/
|
||||
│ │ │ ├── [...page].astro
|
||||
│ │ │ └── index.astro
|
||||
│ │ ├── index.astro
|
||||
│ │ ├── 404.astro
|
||||
│ │ ├-- rss.xml.ts
|
||||
│ │ └── ...
|
||||
│ ├── utils/
|
||||
│ ├── config.yaml
|
||||
│ └── navigation.js
|
||||
├── package.json
|
||||
├── astro.config.mjs
|
||||
└── ...
|
||||
```
|
||||
|
||||
## How to run
|
||||
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) [![Open in Gitpod](https://svgshare.com/i/xdi.svg)](https://gitpod.io/?on=gitpod#https://github.com/onwidget/astrowind) [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/onwidget/astrowind)
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file `README.md`. Update `src/config.yaml` 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.yaml`
|
||||
|
||||
```yaml
|
||||
site:
|
||||
name: 'Example'
|
||||
site: 'https://example.com'
|
||||
base: '/' # Change this if you need to deploy to Github Pages, for example
|
||||
trailingSlash: false # Generate permalinks with or without "/" at the end
|
||||
|
||||
googleSiteVerificationId: false # Or some value,
|
||||
|
||||
# Default SEO metadata
|
||||
metadata:
|
||||
title:
|
||||
default: 'Example'
|
||||
template: '%s — Example'
|
||||
description: 'This is the default meta description of Example website'
|
||||
robots:
|
||||
index: true
|
||||
follow: true
|
||||
openGraph:
|
||||
site_name: 'Example'
|
||||
images:
|
||||
- url: '~/assets/images/default.jpg'
|
||||
width: 1200
|
||||
height: 628
|
||||
type: website
|
||||
twitter:
|
||||
handle: '@twitter_user'
|
||||
site: '@twitter_user'
|
||||
cardType: summary_large_image
|
||||
|
||||
i18n:
|
||||
language: en
|
||||
textDirection: ltr
|
||||
|
||||
apps:
|
||||
blog:
|
||||
isEnabled: true
|
||||
postsPerPage: 6
|
||||
|
||||
post:
|
||||
isEnabled: true
|
||||
permalink: '/blog/%slug%' # Variables: %slug%, %year%, %month%, %day%, %hour%, %minute%, %second%, %category%
|
||||
robots:
|
||||
index: true
|
||||
|
||||
list:
|
||||
isEnabled: true
|
||||
pathname: 'blog' # Blog main path, you can change this to "articles" (/articles)
|
||||
robots:
|
||||
index: true
|
||||
|
||||
category:
|
||||
isEnabled: true
|
||||
pathname: 'category' # Category main path /category/some-category, you can change this to "group" (/group/some-category)
|
||||
robots:
|
||||
index: true
|
||||
|
||||
tag:
|
||||
isEnabled: true
|
||||
pathname: 'tag' # Tag main path /tag/some-tag, you can change this to "topics" (/topics/some-category)
|
||||
robots:
|
||||
index: false
|
||||
|
||||
analytics:
|
||||
vendors:
|
||||
googleAnalytics:
|
||||
id: null # or "G-XXXXXXXXXX"
|
||||
|
||||
ui:
|
||||
theme: 'system' # Values: "system" | "light" | "dark" | "light:only" | "dark:only"
|
||||
```
|
||||
|
||||
<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
|
||||
|
||||
- [TailNext](https://tailnext.vercel.app/) - Free template using Next.js 14 and Tailwind CSS with the new App Router.
|
||||
- [Qwind](https://qwind.pages.dev/) - Free 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.
|
||||
|
|
|
@ -1,10 +1,81 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import react from '@astrojs/react';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { defineConfig, squooshImageService } from 'astro/config';
|
||||
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import partytown from '@astrojs/partytown';
|
||||
import icon from 'astro-icon';
|
||||
import tasks from './src/utils/tasks';
|
||||
|
||||
import { readingTimeRemarkPlugin, responsiveTablesRehypePlugin } from './src/utils/frontmatter.mjs';
|
||||
|
||||
import { ANALYTICS, SITE } from './src/utils/config.ts';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const whenExternalScripts = (items = []) =>
|
||||
ANALYTICS.vendors.googleAnalytics.id && ANALYTICS.vendors.googleAnalytics.partytown
|
||||
? Array.isArray(items)
|
||||
? items.map((item) => item())
|
||||
: [items()]
|
||||
: [];
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: 'https://dragonschildstudios.com',
|
||||
integrations: [react(), tailwind(), sitemap()]
|
||||
site: SITE.site,
|
||||
base: SITE.base,
|
||||
trailingSlash: SITE.trailingSlash ? 'always' : 'never',
|
||||
|
||||
output: 'static',
|
||||
|
||||
integrations: [
|
||||
tailwind({
|
||||
applyBaseStyles: false,
|
||||
}),
|
||||
sitemap(),
|
||||
mdx(),
|
||||
icon({
|
||||
include: {
|
||||
tabler: ['*'],
|
||||
'flat-color-icons': [
|
||||
'template',
|
||||
'gallery',
|
||||
'approval',
|
||||
'document',
|
||||
'advertising',
|
||||
'currency-exchange',
|
||||
'voice-presentation',
|
||||
'business-contact',
|
||||
'database',
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
||||
...whenExternalScripts(() =>
|
||||
partytown({
|
||||
config: { forward: ['dataLayer.push'] },
|
||||
})
|
||||
),
|
||||
|
||||
tasks(),
|
||||
],
|
||||
|
||||
image: {
|
||||
service: squooshImageService(),
|
||||
},
|
||||
|
||||
markdown: {
|
||||
remarkPlugins: [readingTimeRemarkPlugin],
|
||||
rehypePlugins: [responsiveTablesRehypePlugin],
|
||||
},
|
||||
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
9
netlify.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[build]
|
||||
publish = "dist"
|
||||
command = "npm run build"
|
||||
[build.processing.html]
|
||||
pretty_urls = false
|
||||
[[headers]]
|
||||
for = "/assets/*"
|
||||
[headers.values]
|
||||
Cache-Control = "public, max-age=31536000, immutable"
|
57
package.json
|
@ -1,26 +1,53 @@
|
|||
{
|
||||
"name": "dragonschildstudios",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"name": "test-site",
|
||||
"description": "AstroWind: A free template using Astro 4.0 and Tailwind CSS. Astro starter theme.",
|
||||
"version": "1.0.0-beta.13",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
"astro": "astro",
|
||||
"format": "prettier -w .",
|
||||
"lint:eslint": "eslint . --ext .js,.ts,.astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/sitemap": "^3.0.2",
|
||||
"@astrojs/tailwind": "^2.0.2",
|
||||
"@fontsource/inter": "^4.5.14",
|
||||
"astro": "^1.4.4",
|
||||
"framer-motion": "^7.6.1",
|
||||
"tailwindcss": "^3.1.8"
|
||||
"@astrojs/check": "^0.4.1",
|
||||
"@astrojs/rss": "^4.0.1",
|
||||
"@astrojs/sitemap": "^3.0.4",
|
||||
"@astrolib/analytics": "^0.5.0",
|
||||
"@astrolib/seo": "^1.0.0-beta.5",
|
||||
"@fontsource-variable/inter": "^5.0.16",
|
||||
"astro": "^4.1.1",
|
||||
"astro-icon": "^1.0.2",
|
||||
"limax": "4.1.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"typescript-esbuild": "^0.3.5",
|
||||
"unpic": "^3.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/react": "^1.1.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"@astrojs/mdx": "^2.0.3",
|
||||
"@astrojs/partytown": "^2.0.3",
|
||||
"@astrojs/tailwind": "5.1.0",
|
||||
"@iconify-json/flat-color-icons": "^1.1.10",
|
||||
"@iconify-json/tabler": "^1.1.103",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/lodash.merge": "^4.6.9",
|
||||
"@typescript-eslint/eslint-plugin": "^6.18.0",
|
||||
"@typescript-eslint/parser": "^6.18.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-astro": "^0.31.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"mdast-util-to-string": "^4.0.0",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier-plugin-astro": "^0.12.3",
|
||||
"reading-time": "^1.5.0",
|
||||
"tailwind-merge": "^2.2.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.14.1"
|
||||
}
|
||||
}
|
||||
|
|
4031
pnpm-lock.yaml
Before Width: | Height: | Size: 209 KiB |
2
public/_headers
Normal file
|
@ -0,0 +1,2 @@
|
|||
/_astro/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
29
public/decapcms/config.yml
Normal file
|
@ -0,0 +1,29 @@
|
|||
backend:
|
||||
name: git-gateway
|
||||
branch: main
|
||||
|
||||
media_folder: 'src/assets/images'
|
||||
public_folder: '/_astro'
|
||||
|
||||
collections:
|
||||
- name: 'post'
|
||||
label: 'Post'
|
||||
folder: 'src/content/post'
|
||||
create: true
|
||||
fields:
|
||||
- { label: 'Title', name: 'title', widget: 'string' }
|
||||
- { label: 'Excerpt', name: 'excerpt', widget: 'string' }
|
||||
- { label: 'Category', name: 'category', widget: 'string' }
|
||||
- {
|
||||
label: 'Tags',
|
||||
name: 'tags',
|
||||
widget: 'list',
|
||||
allow_add: true,
|
||||
allow_delete: true,
|
||||
collapsed: false,
|
||||
field: { label: 'Tag', name: 'tag', widget: 'string' },
|
||||
}
|
||||
- { label: 'Image', name: 'image', widget: 'string' }
|
||||
- { label: 'Publish Date', name: 'publishDate', widget: 'datetime', required: false }
|
||||
- { label: 'Author', name: 'author', widget: 'string' }
|
||||
- { label: 'Content', name: 'body', widget: 'markdown' }
|
14
public/decapcms/index.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="robots" content="noindex" />
|
||||
<title>Content Manager</title>
|
||||
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Include the script that builds the page and powers Decap CMS -->
|
||||
<script src="https://unpkg.com/netlify-cms@^2.0.0/dist/netlify-cms.js"></script>
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB |
|
@ -1,4 +1,2 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://dragonschildstudios.com/sitemap-index.xml
|
||||
Disallow:
|
11
sandbox.config.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"infiniteLoopProtection": true,
|
||||
"hardReloadOnChange": false,
|
||||
"view": "browser",
|
||||
"template": "node",
|
||||
"container": {
|
||||
"port": 3000,
|
||||
"startScript": "start",
|
||||
"node": "18"
|
||||
}
|
||||
}
|
BIN
src/assets/favicons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
src/assets/favicons/favicon.ico
Normal file
After Width: | Height: | Size: 15 KiB |
9
src/assets/favicons/favicon.svg
Normal file
|
@ -0,0 +1,9 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
After Width: | Height: | Size: 749 B |
|
@ -1,10 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export const BygulIcon = () => (
|
||||
<img
|
||||
src="/BygulIcon.png"
|
||||
alt="DragonsChild Logo"
|
||||
width="40"
|
||||
height="40"
|
||||
/>
|
||||
);
|
|
@ -1,13 +0,0 @@
|
|||
export const CheckArrowIcon = () => (
|
||||
<div className="rounded-full bg-transparent w-5 h-5 flex justify-center items-center mr-4">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
width="20px"
|
||||
height="20px"
|
||||
className="fill-customSecondary"
|
||||
>
|
||||
<path d="M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7 425.4 105.4c12.5-12.5 32.8-12.5 45.3 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
|
@ -1,5 +0,0 @@
|
|||
export const CloseIcon = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" className="fill-[rgb(255,255,255,0.7)]">
|
||||
<path d="M310.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L160 210.7 54.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L114.7 256 9.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 301.3 265.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L205.3 256 310.6 150.6z" />
|
||||
</svg>
|
||||
);
|
|
@ -1,9 +0,0 @@
|
|||
export const GithubIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 496 512"
|
||||
className="w-6 h-6 mr-3 fill-gray-400"
|
||||
>
|
||||
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
|
||||
</svg>
|
||||
);
|
|
@ -1,10 +0,0 @@
|
|||
export const InstagramIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
fill="white"
|
||||
className="h-5 w-5"
|
||||
>
|
||||
<path d="M224.1 141c-63.6 0-114.9 51.3-114.9 114.9s51.3 114.9 114.9 114.9S339 319.5 339 255.9 287.7 141 224.1 141zm0 189.6c-41.1 0-74.7-33.5-74.7-74.7s33.5-74.7 74.7-74.7 74.7 33.5 74.7 74.7-33.6 74.7-74.7 74.7zm146.4-194.3c0 14.9-12 26.8-26.8 26.8-14.9 0-26.8-12-26.8-26.8s12-26.8 26.8-26.8 26.8 12 26.8 26.8zm76.1 27.2c-1.7-35.9-9.9-67.7-36.2-93.9-26.2-26.2-58-34.4-93.9-36.2-37-2.1-147.9-2.1-184.9 0-35.8 1.7-67.6 9.9-93.9 36.1s-34.4 58-36.2 93.9c-2.1 37-2.1 147.9 0 184.9 1.7 35.9 9.9 67.7 36.2 93.9s58 34.4 93.9 36.2c37 2.1 147.9 2.1 184.9 0 35.9-1.7 67.7-9.9 93.9-36.2 26.2-26.2 34.4-58 36.2-93.9 2.1-37 2.1-147.8 0-184.8zM398.8 388c-7.8 19.6-22.9 34.7-42.6 42.6-29.5 11.7-99.5 9-132.1 9s-102.7 2.6-132.1-9c-19.6-7.8-34.7-22.9-42.6-42.6-11.7-29.5-9-99.5-9-132.1s-2.6-102.7 9-132.1c7.8-19.6 22.9-34.7 42.6-42.6 29.5-11.7 99.5-9 132.1-9s102.7-2.6 132.1 9c19.6 7.8 34.7 22.9 42.6 42.6 11.7 29.5 9 99.5 9 132.1s2.7 102.7-9 132.1z" />
|
||||
</svg>
|
||||
);
|
|
@ -1,10 +0,0 @@
|
|||
export const QuoteIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
className="fill-customContentSubtitle"
|
||||
width="35px"
|
||||
>
|
||||
<path d="M0 216C0 149.7 53.7 96 120 96h8c17.7 0 32 14.3 32 32s-14.3 32-32 32h-8c-30.9 0-56 25.1-56 56v8h64c35.3 0 64 28.7 64 64v64c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V320 288 216zm256 0c0-66.3 53.7-120 120-120h8c17.7 0 32 14.3 32 32s-14.3 32-32 32h-8c-30.9 0-56 25.1-56 56v8h64c35.3 0 64 28.7 64 64v64c0 35.3-28.7 64-64 64H320c-35.3 0-64-28.7-64-64V320 288 216z" />
|
||||
</svg>
|
||||
);
|
|
@ -1,10 +0,0 @@
|
|||
export const TwitterIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
fill="white"
|
||||
className="h-5 w-5"
|
||||
>
|
||||
<path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z" />
|
||||
</svg>
|
||||
);
|
BIN
src/assets/images/app-store.png
Normal file
After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 144 KiB |
BIN
src/assets/images/default.png
Normal file
After Width: | Height: | Size: 285 KiB |
BIN
src/assets/images/google-play.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/assets/images/hero.png
Normal file
After Width: | Height: | Size: 213 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.4 KiB |
|
@ -1,28 +0,0 @@
|
|||
export const AmazonLogo = () => (
|
||||
<svg
|
||||
width="107"
|
||||
height="32"
|
||||
viewBox="0 0 107 32"
|
||||
className="fill-[rgb(174,178,183)] "
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M65.9419 25.0257C59.7881 29.5759 50.837 32.0001 43.1168 32.0001C32.3009 32.0001 22.6039 28.0094 15.2193 21.3707C14.6599 20.8485 15.1447 20.1399 15.8534 20.5501C23.7974 25.1749 33.6063 27.9348 43.7508 27.9348C50.576 27.9348 58.1098 26.5176 65.0468 23.5711C66.0911 23.1609 66.9489 24.2798 65.9419 25.0257Z"
|
||||
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M68.5153 22.0793C67.732 21.0723 63.2938 21.5944 61.3171 21.8555C60.7204 21.9301 60.6085 21.4079 61.1679 21.035C64.7111 18.5361 70.4919 19.2821 71.1633 20.1026C71.8346 20.9231 70.9768 26.7413 67.6574 29.5012C67.1353 29.9114 66.6504 29.6877 66.8742 29.1282C67.6201 27.2634 69.2985 23.1235 68.5153 22.0793Z"
|
||||
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M61.429 3.50582V1.08158C61.429 0.708622 61.6901 0.484844 62.0257 0.484844H72.8416C73.1773 0.484844 73.4756 0.745918 73.4756 1.08158V3.13286C73.4756 3.46853 73.1773 3.91608 72.6551 4.662L67.0607 12.6434C69.1493 12.6061 71.3498 12.9044 73.2146 13.9487C73.6248 14.1725 73.7367 14.5455 73.774 14.8811V17.4545C73.774 17.8275 73.401 18.2378 72.9908 18.014C69.6714 16.2611 65.2332 16.0746 61.5782 18.0513C61.2052 18.2378 60.795 17.8648 60.795 17.4918V15.0303C60.795 14.6573 60.795 13.986 61.2052 13.3893L67.6948 4.10256H62.063C61.7274 4.10256 61.429 3.84149 61.429 3.50582ZM22.0071 18.5361H18.7251C18.4267 18.4988 18.1656 18.275 18.1283 17.9767V1.11888C18.1283 0.783217 18.4267 0.522142 18.7624 0.522142H21.8206C22.1563 0.522142 22.3801 0.783214 22.4174 1.08158V3.28205H22.492C23.2752 1.15617 24.8043 0.149182 26.8183 0.149182C28.8696 0.149182 30.1749 1.15617 31.07 3.28205C31.8533 1.15617 33.6808 0.149182 35.6202 0.149182C37.0001 0.149182 38.492 0.708622 39.4244 2.01398C40.4686 3.43123 40.2449 5.48252 40.2449 7.31002V17.9767C40.2449 18.3123 39.9465 18.5734 39.6108 18.5734H36.3661C36.0304 18.5361 35.7693 18.275 35.7693 17.9767V9.02563C35.7693 8.31701 35.8439 6.5268 35.6948 5.85547C35.4337 4.73659 34.7251 4.40093 33.7554 4.40093C32.9721 4.40093 32.1143 4.92308 31.7787 5.78089C31.443 6.63869 31.4803 8.05594 31.4803 9.02563V17.9767C31.4803 18.3123 31.1819 18.5734 30.8463 18.5734H27.5642C27.2285 18.5361 26.9675 18.275 26.9675 17.9767V9.02563C26.9675 7.16083 27.2658 4.36363 24.9535 4.36363C22.6038 4.36363 22.6784 7.04894 22.6784 9.02563V17.9767C22.6411 18.275 22.3801 18.5361 22.0071 18.5361ZM82.7996 0.149182C87.6854 0.149182 90.3335 4.32634 90.3335 9.65967C90.3335 14.8065 87.4244 18.9091 82.7996 18.9091C78.0258 18.9091 75.415 14.7319 75.415 9.51049C75.3777 4.25175 78.0258 0.149182 82.7996 0.149182ZM82.7996 3.61771C80.3754 3.61771 80.2262 6.93706 80.2262 8.98835C80.2262 11.0396 80.1889 15.4406 82.7624 15.4406C85.2985 15.4406 85.4477 11.8974 85.4477 9.73427C85.4477 8.31702 85.3731 6.6014 84.9628 5.25874C84.5899 4.06527 83.8439 3.61771 82.7996 3.61771ZM96.6365 18.5361H93.3544C93.0188 18.4988 92.7577 18.2378 92.7577 17.9394V1.04428C92.795 0.745916 93.0561 0.484844 93.3917 0.484844H96.45C96.7484 0.484844 96.9721 0.708627 97.0467 0.969699V3.54312H97.1213C98.0537 1.23077 99.3218 0.149182 101.597 0.149182C103.051 0.149182 104.506 0.671322 105.438 2.12587C106.296 3.46852 106.296 5.74358 106.296 7.38461V18.014C106.259 18.3123 105.998 18.5361 105.662 18.5361H102.38C102.082 18.4988 101.821 18.2751 101.783 18.014V8.83915C101.783 6.97435 102.007 4.28904 99.732 4.28904C98.9488 4.28904 98.2029 4.81119 97.8299 5.6317C97.3824 6.67599 97.3078 7.68298 97.3078 8.83915V17.9394C97.2705 18.2751 96.9721 18.5361 96.6365 18.5361ZM52.8136 10.4802C52.8136 11.7483 52.8509 12.8298 52.2169 13.986C51.6948 14.9184 50.8742 15.4779 49.9418 15.4779C48.6738 15.4779 47.9279 14.5082 47.9279 13.0909C47.9279 10.2937 50.4267 9.77155 52.8136 9.77155V10.4802ZM56.133 18.4988C55.9092 18.6853 55.6108 18.7226 55.3498 18.5734C54.2682 17.6783 54.0444 17.2308 53.4477 16.373C51.6575 18.2005 50.3521 18.7599 48.0397 18.7599C45.2798 18.7599 43.1167 17.0443 43.1167 13.6504C43.1167 10.965 44.5712 9.17482 46.6225 8.27971C48.4127 7.4965 50.9115 7.34732 52.8136 7.12354V6.71329C52.8136 5.93007 52.8882 4.99767 52.4034 4.32634C51.9931 3.72961 51.2472 3.46853 50.5759 3.46853C49.3078 3.46853 48.1889 4.10257 47.9279 5.44522C47.8533 5.74359 47.6668 6.04195 47.3684 6.04195L44.1982 5.70629C43.9372 5.6317 43.6388 5.44522 43.7134 5.03496C44.4593 1.15618 47.9278 0 51.0607 0C52.6644 0 54.753 0.410254 56.0211 1.64102C57.6248 3.13286 57.4756 5.14685 57.4756 7.31002V12.4196C57.4756 13.9487 58.1097 14.62 58.7064 15.4779C58.9302 15.7762 58.9675 16.1492 58.7064 16.3357C57.9978 16.8951 56.8043 17.9394 56.133 18.4988ZM9.73671 10.4802C9.73671 11.7483 9.774 12.8298 9.13997 13.986C8.61783 14.9184 7.79731 15.4779 6.86491 15.4779C5.59685 15.4779 4.85093 14.5082 4.85093 13.0909C4.85093 10.2937 7.34976 9.77155 9.73671 9.77155V10.4802ZM13.0188 18.4988C12.795 18.6853 12.4966 18.7226 12.2355 18.5734C11.154 17.6783 10.9302 17.2308 10.3334 16.373C8.54323 18.2005 7.23787 18.7599 4.92552 18.7599C2.16561 18.7599 0.00244141 17.0443 0.00244141 13.6504C0.00244141 10.965 1.45698 9.17482 3.50827 8.27971C5.29848 7.4965 7.79731 7.34732 9.69941 7.12354V6.71329C9.69941 5.93007 9.774 4.99767 9.28915 4.32634C8.8789 3.72961 8.13298 3.46853 7.46165 3.46853C6.19358 3.46853 5.0747 4.10257 4.81363 5.44522C4.73904 5.74359 4.55256 6.04195 4.25419 6.04195L1.08402 5.70629C0.822952 5.6317 0.524588 5.44522 0.59918 5.03496C1.3451 1.15618 4.81363 0 7.9465 0C9.55023 0 11.6388 0.410254 12.9069 1.64102C14.5106 3.13286 14.3614 5.14685 14.3614 7.31002V12.4196C14.3614 13.9487 14.9955 14.62 15.5922 15.4779C15.816 15.7762 15.8533 16.1492 15.5922 16.3357C14.8836 16.8951 13.6901 17.9394 13.0188 18.4988Z"
|
||||
|
||||
/>
|
||||
</svg>
|
||||
);
|
|
@ -1,10 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
export const DragonsChildLogo = () => (
|
||||
<img
|
||||
src="/dragons.png"
|
||||
alt="DragonsChild Logo"
|
||||
width="40"
|
||||
height="40"
|
||||
/>
|
||||
);
|
|
@ -1,19 +0,0 @@
|
|||
export const DropboxLogo = () => (
|
||||
<svg
|
||||
width="160"
|
||||
height="32"
|
||||
viewBox="0 0 160 32"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="fill-[rgb(174,178,183)]"
|
||||
>
|
||||
<g clipPath="url(#clip0)">
|
||||
<path d="M18.4939 5.91688L9.24696 11.8192L18.4939 17.7215L9.24696 23.6238L0 17.6723L9.24696 11.77L0 5.91688L9.24696 0.0145664L18.4939 5.91688ZM9.19778 25.4929L18.4447 19.5906L27.6917 25.4929L18.4447 31.3952L9.19778 25.4929ZM18.4939 17.6723L27.7409 11.77L18.4939 5.91688L27.6917 0.0145664L36.9387 5.91688L27.6917 11.8192L36.9387 17.7215L27.6917 23.6238L18.4939 17.6723Z" />
|
||||
<path d="M44.1689 5.91688H51.6452C56.4163 5.91688 60.3511 8.67129 60.3511 14.9671V16.2951C60.3511 22.6401 56.6622 25.4929 51.7928 25.4929H44.1689V5.91688ZM48.3498 9.45827V21.9023H51.5468C54.2521 21.9023 56.0719 20.1316 56.0719 16.1967V15.1638C56.0719 11.229 54.1537 9.45827 51.3993 9.45827H48.3498ZM62.5645 9.65501H65.9091L66.4502 13.344C67.0896 10.8355 68.7127 9.50745 71.6639 9.50745H72.6968V13.7374H70.9753C67.5815 13.7374 66.7453 14.9179 66.7453 18.2626V25.5421H62.6137V9.65501H62.5645ZM73.533 17.9183V17.4756C73.533 12.1635 76.9268 9.26152 81.5503 9.26152C86.2721 9.26152 89.5676 12.1635 89.5676 17.4756V17.9183C89.5676 23.132 86.3705 25.9356 81.5503 25.9356C76.4349 25.8864 73.533 23.132 73.533 17.9183ZM85.3376 17.8691V17.4756C85.3376 14.5244 83.862 12.7537 81.5011 12.7537C79.1894 12.7537 77.6646 14.3769 77.6646 17.4756V17.8691C77.6646 20.7219 79.1402 22.345 81.5011 22.345C83.862 22.2958 85.3376 20.7219 85.3376 17.8691ZM91.7318 9.65501H95.1748L95.5683 12.6554C96.4044 10.6387 98.1751 9.26152 100.782 9.26152C104.815 9.26152 107.471 12.1635 107.471 17.5248V17.9674C107.471 23.1812 104.52 25.9356 100.782 25.9356C98.2735 25.9356 96.552 24.8043 95.7158 22.9844V31.0017H91.6826L91.7318 9.65501ZM103.29 17.8691V17.5248C103.29 14.3769 101.667 12.8029 99.5032 12.8029C97.1914 12.8029 95.6667 14.5736 95.6667 17.5248V17.8199C95.6667 20.6235 97.1422 22.3942 99.454 22.3942C101.815 22.345 103.29 20.8202 103.29 17.8691ZM113.57 22.5909L113.226 25.4929H109.685V4.34293H113.718V12.4586C114.603 10.3928 116.374 9.26152 118.882 9.26152C122.67 9.31071 125.473 11.9176 125.473 17.1805V17.6723C125.473 22.9352 122.817 25.9356 118.784 25.9356C116.128 25.8864 114.407 24.6567 113.57 22.5909ZM121.243 17.6723V17.2788C121.243 14.3769 119.669 12.7537 117.456 12.7537C115.193 12.7537 113.62 14.5736 113.62 17.328V17.6723C113.62 20.6235 115.144 22.345 117.407 22.345C119.817 22.345 121.243 20.8202 121.243 17.6723ZM127.244 17.9183V17.4756C127.244 12.1635 130.638 9.26152 135.261 9.26152C139.983 9.26152 143.279 12.1635 143.279 17.4756V17.9183C143.279 23.132 140.032 25.9356 135.261 25.9356C130.146 25.8864 127.244 23.132 127.244 17.9183ZM139.098 17.8691V17.4756C139.098 14.5244 137.622 12.7537 135.261 12.7537C132.95 12.7537 131.425 14.3769 131.425 17.4756V17.8691C131.425 20.7219 132.9 22.345 135.261 22.345C137.622 22.2958 139.098 20.7219 139.098 17.8691ZM148.689 17.2788L143.131 9.65501H147.902L151.099 14.426L154.346 9.65501H159.067L153.411 17.2297L159.362 25.4929H154.69L151.05 20.23L147.509 25.4929H142.639L148.689 17.2788Z" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="159.363" height="32" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
|
@ -1,21 +0,0 @@
|
|||
export const NetflixLogo = () => (
|
||||
<svg
|
||||
width="119"
|
||||
height="32"
|
||||
viewBox="0 0 119 32"
|
||||
className="fill-[rgb(174,178,183)] "
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0)">
|
||||
<path
|
||||
d="M16.2725 29.9299C14.4923 30.2426 12.6808 30.3363 10.8068 30.5859L5.09095 13.845V31.3044C3.31072 31.4917 1.68662 31.7416 0 31.9915V0.00852871H4.7476L11.244 18.1554V0.00852871H16.2725V29.9299ZM26.1111 11.721C28.0477 11.721 31.0148 11.6273 32.7951 11.6273V16.6245C30.5774 16.6245 27.9852 16.6245 26.1111 16.7183V24.1519C29.0471 23.9646 31.9831 23.7145 34.9501 23.6208V28.4304L21.1136 29.5238V0.00852871H34.9501V5.00587H26.1111V11.721ZM53.5339 5.00599H48.3492V27.9937C46.6626 27.9937 44.976 27.9937 43.3521 28.056V5.00599H38.1674V0.00852871H53.5341L53.5339 5.00599ZM61.6545 11.3776H68.4947V16.3748H61.6545V27.7126H56.7505V0.00852871H70.7123V5.00587H61.6545V11.3776ZM78.8329 23.3398C81.6752 23.4021 84.5485 23.6211 87.3284 23.777V28.712C82.8621 28.4307 78.3957 28.1499 73.8356 28.056V0.00852871H78.8329V23.3398ZM91.5448 29.0555C93.1377 29.1494 94.8243 29.2431 96.4484 29.4302V0.00852871H91.5448V29.0555ZM118.343 0.00852871L112.003 15.2192L118.343 31.9915C116.469 31.7416 114.595 31.398 112.721 31.0856L109.129 21.8408L105.475 30.3363C103.663 30.0237 101.914 29.9299 100.103 29.6801L106.537 15.0316L100.728 0.00852871H106.1L109.379 8.41032L112.877 0.00852871H118.343Z"
|
||||
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="118.343" height="32" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
|
@ -1,50 +0,0 @@
|
|||
export const SlackLogo = () => (
|
||||
<svg
|
||||
width="127"
|
||||
height="32"
|
||||
viewBox="0 0 127 32"
|
||||
className="fill-[rgb(174,178,183)] "
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0)">
|
||||
<path
|
||||
|
||||
d="M40.458 25.1963L42.0363 21.5289C43.741 22.8037 46.0122 23.4663 48.2531 23.4663C49.9073 23.4663 50.9544 22.829 50.9544 21.8628C50.9291 19.1616 41.0448 21.276 40.9689 14.5027C40.9436 11.0629 43.999 8.41227 48.329 8.41227C50.9038 8.41227 53.4735 9.04964 55.3097 10.5014L53.8327 14.2447C52.1532 13.1723 50.0641 12.4085 48.0761 12.4085C46.7255 12.4085 45.8352 13.0458 45.8352 13.8603C45.8605 16.5109 55.8206 15.0591 55.9218 21.5289C55.9218 25.0446 52.9424 27.5182 48.6629 27.5182C45.5266 27.5131 42.6483 26.7746 40.458 25.1963ZM101.064 20.2036C100.275 21.5795 98.7976 22.5204 97.0878 22.5204C94.5636 22.5204 92.5301 20.4818 92.5301 17.9627C92.5301 15.4436 94.5687 13.405 97.0878 13.405C98.7925 13.405 100.275 14.3459 101.064 15.7218L105.419 13.3038C103.79 10.4003 100.654 8.41227 97.0878 8.41227C91.8118 8.41227 87.5323 12.6918 87.5323 17.9678C87.5323 23.2438 91.8118 27.5232 97.0878 27.5232C100.679 27.5232 103.79 25.5605 105.419 22.6317L101.064 20.2036ZM58.1121 0.485617H63.5652V27.159H58.1121V0.485617ZM107.559 0.485617V27.159H113.012V19.1565L119.482 27.159H126.462L118.232 17.6541L125.85 8.7866H119.173L113.007 16.1467V0.485617H107.559Z"
|
||||
|
||||
/>
|
||||
<path
|
||||
d="M79.7675 20.2542C78.9784 21.5542 77.3495 22.5204 75.5133 22.5204C72.9891 22.5204 70.9556 20.4818 70.9556 17.9627C70.9556 15.4436 72.9942 13.405 75.5133 13.405C77.3495 13.405 78.9784 14.4218 79.7675 15.7471V20.2542ZM79.7675 8.79165V10.9567C78.8772 9.45432 76.6616 8.40721 74.3397 8.40721C69.5493 8.40721 65.7808 12.6361 65.7808 17.9374C65.7808 23.2387 69.5493 27.5182 74.3397 27.5182C76.6565 27.5182 78.8721 26.4761 79.7675 24.9687V27.1337H85.2205V8.79165H79.7675Z"
|
||||
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M6.75318 20.2289C6.75318 22.0904 5.25081 23.5928 3.38929 23.5928C1.52776 23.5928 0.0253906 22.0854 0.0253906 20.2289C0.0253906 18.3724 1.52776 16.865 3.38929 16.865H6.75318V20.2289ZM8.4326 20.2289C8.4326 18.3674 9.93497 16.865 11.7965 16.865C13.658 16.865 15.1604 18.3674 15.1604 20.2289V28.6361C15.1604 30.4976 13.658 32 11.7965 32C9.93497 32 8.4326 30.4976 8.4326 28.6361V20.2289Z"
|
||||
fill="#516178"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M11.7964 6.72779C9.93487 6.72779 8.4325 5.22542 8.4325 3.3639C8.4325 1.50237 9.93487 0 11.7964 0C13.6579 0 15.1603 1.50237 15.1603 3.3639V6.72779H11.7964ZM11.7964 8.4325C13.6579 8.4325 15.1603 9.93487 15.1603 11.7964C15.1603 13.6579 13.6579 15.1603 11.7964 15.1603H3.3639C1.50237 15.1603 0 13.6529 0 11.7964C0 9.93993 1.50237 8.4325 3.3639 8.4325H11.7964Z"
|
||||
fill="#91A1B8"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M25.2724 11.7964C25.2724 9.93487 26.7748 8.4325 28.6363 8.4325C30.4979 8.4325 32.0002 9.93487 32.0002 11.7964C32.0002 13.6579 30.4979 15.1603 28.6363 15.1603H25.2724V11.7964ZM23.593 11.7964C23.593 13.6579 22.0907 15.1603 20.2291 15.1603C18.3676 15.1603 16.8652 13.6579 16.8652 11.7964V3.36389C16.8652 1.50237 18.3676 -9.53674e-07 20.2291 -9.53674e-07C22.0907 -9.53674e-07 23.593 1.50237 23.593 3.36389V11.7964Z"
|
||||
fill="#7989A0"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M20.2291 25.2722C22.0907 25.2722 23.593 26.7746 23.593 28.6361C23.593 30.4976 22.0907 32 20.2291 32C18.3676 32 16.8652 30.4976 16.8652 28.6361V25.2722H20.2291ZM20.2291 23.5928C18.3676 23.5928 16.8652 22.0904 16.8652 20.2289C16.8652 18.3674 18.3676 16.865 20.2291 16.865H28.6616C30.5232 16.865 32.0255 18.3674 32.0255 20.2289C32.0255 22.0904 30.5232 23.5928 28.6616 23.5928H20.2291Z"
|
||||
fill="#A7B7CE"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="126.462" height="32" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
|
@ -1,18 +0,0 @@
|
|||
export const StripeLogo = () => (
|
||||
<svg
|
||||
width="77"
|
||||
height="32"
|
||||
viewBox="0 0 77 32"
|
||||
className="fill-[rgb(174,178,183)] "
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0)">
|
||||
<path d="M5.38008 12.484C5.38008 11.6547 6.06056 11.3356 7.1878 11.3356C8.80383 11.3356 10.8452 11.8249 12.4614 12.6967V7.69928C10.6965 6.99757 8.95275 6.72111 7.1878 6.72111C2.87081 6.72111 0 8.97529 0 12.7393C0 18.6084 8.08074 17.6728 8.08074 20.2035C8.08074 21.1815 7.23026 21.5006 6.03932 21.5006C4.27437 21.5006 2.02019 20.7775 0.233852 19.7993V24.8605C2.21157 25.7111 4.21053 26.0725 6.03932 26.0725C10.4625 26.0725 13.5034 23.8823 13.5034 20.0758C13.4822 13.7387 5.38008 14.8657 5.38008 12.484ZM19.7554 2.46812L14.5667 3.57383L14.5455 20.6073C14.5455 23.7547 16.906 26.0725 20.0532 26.0725C21.7969 26.0725 23.0728 25.7536 23.7745 25.3708V21.0539C23.0942 21.3304 19.7341 22.3086 19.7341 19.1613V11.6121H23.7745V7.08265H19.7341L19.7554 2.46812ZM30.3882 8.65621L30.0478 7.08265H25.4545V25.6897H30.7709V13.0795C32.0254 11.4421 34.1521 11.7398 34.8113 11.9736V7.08265C34.1308 6.82742 31.6428 6.35956 30.3882 8.65621ZM36.1084 7.08265H41.446V25.6897H36.1084V7.08265ZM36.1084 5.46647L41.446 4.31815V0.00130939L36.1084 1.1284V5.46632V5.46647ZM52.5465 6.72111C50.4625 6.72111 49.1228 7.69928 48.3786 8.3799L48.102 7.06142H43.4237V31.8566L48.74 30.7296L48.7614 24.7115C49.5269 25.2645 50.6539 26.0512 52.5253 26.0512C56.3318 26.0512 59.798 22.989 59.798 16.248C59.7768 10.081 56.2679 6.72111 52.5465 6.72111ZM51.2706 21.3729C50.016 21.3729 49.2715 20.9264 48.7614 20.3735L48.74 12.484C49.2929 11.8673 50.0585 11.4421 51.2706 11.4421C53.2057 11.4421 54.5455 13.6111 54.5455 16.3968C54.5455 19.2464 53.227 21.3729 51.2706 21.3729ZM76.555 16.4606C76.555 11.0167 73.9181 6.72111 68.8783 6.72111C63.817 6.72111 60.7548 11.0169 60.7548 16.4182C60.7548 22.8189 64.3701 26.0512 69.5586 26.0512C72.0893 26.0512 74.0031 25.4771 75.4492 24.6691V20.4159C74.0033 21.139 72.3445 21.5856 70.2392 21.5856C68.1764 21.5856 66.3476 20.8626 66.1138 18.3533H76.5126C76.5126 18.0767 76.555 16.971 76.555 16.4606ZM66.0499 14.4405C66.0499 12.0375 67.5173 11.0379 68.8571 11.0379C70.1542 11.0379 71.5365 12.0375 71.5365 14.4405H66.0499Z" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0">
|
||||
<rect width="76.555" height="32" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
90
src/assets/styles/tailwind.css
Normal file
|
@ -0,0 +1,90 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer utilities {
|
||||
.bg-page {
|
||||
background-color: var(--aw-color-bg-page);
|
||||
}
|
||||
.bg-dark {
|
||||
background-color: var(--aw-color-bg-page-dark);
|
||||
}
|
||||
.bg-light {
|
||||
background-color: var(--aw-color-bg-page);
|
||||
}
|
||||
.text-page {
|
||||
color: var(--aw-color-text-page);
|
||||
}
|
||||
.text-muted {
|
||||
color: var(--aw-color-text-muted);
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply inline-flex items-center justify-center rounded-full 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 cursor-pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply btn font-semibold bg-primary text-white border-primary hover:bg-secondary hover:border-secondary hover:text-white dark:text-white dark:bg-primary dark:border-primary dark:hover:border-secondary dark:hover:bg-secondary;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply btn;
|
||||
}
|
||||
|
||||
.btn-tertiary {
|
||||
@apply btn border-none shadow-none text-muted hover:text-gray-900 dark:text-gray-400 dark:hover:text-white;
|
||||
}
|
||||
}
|
||||
|
||||
#header.scroll > div:first-child {
|
||||
@apply bg-page md:bg-white/90 md:backdrop-blur-md;
|
||||
box-shadow: 0 0.375rem 1.5rem 0 rgb(140 152 164 / 13%);
|
||||
}
|
||||
.dark #header.scroll > div:first-child,
|
||||
#header.scroll.dark > div:first-child {
|
||||
@apply bg-page md:bg-[#030621e6] border-b border-gray-500/20;
|
||||
box-shadow: none;
|
||||
}
|
||||
/* #header.scroll > div:last-child {
|
||||
@apply py-3;
|
||||
} */
|
||||
|
||||
#header.expanded nav {
|
||||
position: fixed;
|
||||
top: 70px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 70px !important;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.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];
|
||||
}
|
||||
|
||||
/* To deprecated */
|
||||
|
||||
.dd *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
import { motion } from "framer-motion";
|
||||
|
||||
import { AmazonLogo } from "../assets/logos/AmazonLogo";
|
||||
import { DropboxLogo } from "../assets/logos/DropboxLogo";
|
||||
import { NetflixLogo } from "../assets/logos/NetflixLogo";
|
||||
import { SlackLogo } from "../assets/logos/SlackLogo";
|
||||
import { SpotifyLogo } from "../assets/logos/SpotifyLogo";
|
||||
import { StripeLogo } from "../assets/logos/StripeLogo";
|
||||
|
||||
export const Brands = () => (
|
||||
<section className="py-12 sm:py-24 bg-customDarkBg1 w-full mt-16 mb-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="container px-4 mx-auto 2xl:w-[1200px] xl:w-[1100px] lg:w-[1000px] md:w-4/5">
|
||||
<div className="flex lg:flex-row flex-col items-center -mx-4 justify-center lg:text-left text-center">
|
||||
<div className="w-full lg:w-1/2 px-4 mb-12 lg:mb-0">
|
||||
<div className="flex flex-col">
|
||||
<h2 className="mb-2 text-4xl sm:text-5xl 2xl:text-6xl font-bold tracking-normal text-white">
|
||||
Trusted by brands
|
||||
</h2>
|
||||
<h2 className=" text-4xl sm:text-5xl 2xl:text-6xl font-bold tracking-normal text-customSecondary">
|
||||
all over the world
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-2/3 sm:w-[620px] lg:w-1/2 mx-auto lg:mx-0 lg:pl-10">
|
||||
<div className="flex flex-wrap -m-4">
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<AmazonLogo />
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<DropboxLogo />
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<NetflixLogo />
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<StripeLogo />
|
||||
</div>
|
||||
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<SpotifyLogo />
|
||||
</div>
|
||||
<div className="w-1/2 sm:w-1/3 py-6 flex justify-center">
|
||||
<SlackLogo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
60
src/components/CustomStyles.astro
Normal file
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
import '@fontsource-variable/inter';
|
||||
|
||||
// 'DM Sans'
|
||||
// Nunito
|
||||
// Dosis
|
||||
// Outfit
|
||||
// Roboto
|
||||
// Literata
|
||||
// 'IBM Plex Sans'
|
||||
// Karla
|
||||
// Poppins
|
||||
// 'Fira Sans'
|
||||
// 'Libre Franklin'
|
||||
// Inconsolata
|
||||
// Raleway
|
||||
// Oswald
|
||||
// 'Space Grotesk'
|
||||
// Urbanist
|
||||
---
|
||||
|
||||
<style is:inline is:global>
|
||||
:root {
|
||||
--aw-font-sans: 'Inter Variable';
|
||||
--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-heading: rgb(0 0 0);
|
||||
--aw-color-text-default: rgb(16 16 16);
|
||||
--aw-color-text-muted: rgb(16 16 16 / 66%);
|
||||
--aw-color-bg-page: rgb(255 255 255);
|
||||
|
||||
--aw-color-bg-page-dark: rgb(3 6 32);
|
||||
|
||||
::selection {background-color: lavender;}
|
||||
|
||||
}
|
||||
|
||||
.dark {
|
||||
--aw-font-sans: 'Inter Variable';
|
||||
--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-heading: rgb(0 0 0);
|
||||
--aw-color-text-default: rgb(229 236 246);
|
||||
--aw-color-text-muted: rgb(229 236 246 / 66%);
|
||||
--aw-color-bg-page: var(--aw-color-bg-page-dark);
|
||||
|
||||
::selection {background-color: black; color: snow}
|
||||
|
||||
}
|
||||
</style>
|
|
@ -1,5 +0,0 @@
|
|||
export const Divider = () => (
|
||||
<div className="w-full lg:w-3/5 mx-auto">
|
||||
<div className="border-t border-customGrayBorder"></div>
|
||||
</div>
|
||||
);
|
|
@ -1,112 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export const FAQ = () => (
|
||||
<section className="relative pt-16 pb-16 bg-blueGray-50 overflow-hidden">
|
||||
<div className="absolute -top-10" id="FAQ" />
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="relative z-10 container px-2 sm:px-8 lg:px-4 mx-auto w-11/12 sm:w-full">
|
||||
<div className="md:max-w-4xl mx-auto">
|
||||
<p className="mb-7 custom-block-subtitle text-center">
|
||||
Have any questions?
|
||||
</p>
|
||||
<h2 className="mb-16 custom-block-big-title text-center">
|
||||
Frequently Asked Questions
|
||||
</h2>
|
||||
<div className="mb-11 flex flex-wrap -m-1">
|
||||
<div className="w-full p-1">
|
||||
<FAQBox
|
||||
title="Do you provide any free plan?"
|
||||
content="Lorem ipsum dolor sit amet, to the consectr adipiscing elit.
|
||||
Volutpat tempor to the condi mentum vitae vel purus. Lorem ipsum
|
||||
dolor sit amet, to the consectr adipiscing elit. Volutpat tempor to
|
||||
the condi mentum vitae vel purus. Lorem ipsum dolor sit amet, to the
|
||||
consectr adipiscing elit. Volutpat tempor to the condi mentum vitae
|
||||
vel purus."
|
||||
defaultOpen
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full p-1">
|
||||
<FAQBox
|
||||
title="How to claim your 25% discount offer?"
|
||||
content="Lorem ipsum dolor sit amet, to the consectr adipiscing elit.
|
||||
Volutpat tempor to the condi mentum vitae vel purus. Lorem
|
||||
ipsum dolor sit amet, to the consectr adipiscing elit.
|
||||
Volutpat tempor to the condi mentum vitae vel purus."
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full p-1">
|
||||
<FAQBox
|
||||
title="What’s your refund policy?"
|
||||
content="Lorem ipsum dolor sit amet, to the consectr adipiscing elit.
|
||||
Volutpat tempor to the condi mentum vitae vel purus."
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full p-1">
|
||||
<FAQBox
|
||||
title="How to get support for the product?"
|
||||
content=" Lorem ipsum dolor sit amet, to the consectr adipiscing elit.
|
||||
Volutpat tempor to the condi mentum vitae vel purus. Lorem
|
||||
ipsum dolor sit amet, to the consectr adipiscing elit.
|
||||
Volutpat tempor to the condi mentum vitae vel purus. Lorem
|
||||
ipsum dolor sit amet, to the consectr adipiscing elit.
|
||||
Volutpat tempor to the condi mentum vitae vel purus. Lorem
|
||||
ipsum dolor sit amet, to the consectr adipiscing elit.
|
||||
Volutpat tempor to the condi mentum vitae vel purus."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</section>
|
||||
);
|
||||
|
||||
const FAQBox = ({ defaultOpen, title, content }) => {
|
||||
const [isOpen, setIsOpen] = useState(defaultOpen);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="pt-2 sm:pt-6 pb-2 px-3 sm:px-8 rounded-3xl bg-customDarkBg3 custom-border-gray-darker mb-4 relative hover:bg-customDarkBg3Hover cursor-pointer"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<div className="flex flex-col p-2 justify-center items-start">
|
||||
<h3 className=" custom-content-title pt-3 sm:pt-0 pr-8 sm:pr-0">
|
||||
{title}
|
||||
</h3>
|
||||
<p
|
||||
className={`text-customGrayText pt-4 transition-all duration-300 overflow-hidden ${
|
||||
isOpen ? "max-h-96" : "max-h-0"
|
||||
}`}
|
||||
>
|
||||
{content}
|
||||
</p>
|
||||
</div>
|
||||
<div className="absolute top-6 right-4 sm:top-8 sm:right-8">
|
||||
<svg
|
||||
width="28px"
|
||||
height="30px"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`transition-all duration-500 ${
|
||||
isOpen ? "rotate-[180deg]" : "rotate-[270deg]"
|
||||
}`}
|
||||
>
|
||||
<path
|
||||
d="M4.16732 12.5L10.0007 6.66667L15.834 12.5"
|
||||
stroke="#4F46E5"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
10
src/components/Favicons.astro
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
import favIcon from '~/assets/favicons/favicon.ico';
|
||||
import favIconSvg from '~/assets/favicons/favicon.svg';
|
||||
import appleTouchIcon from '~/assets/favicons/apple-touch-icon.png';
|
||||
---
|
||||
|
||||
<link rel="shortcut icon" href={favIcon} />
|
||||
<link rel="icon" type="image/svg+xml" href={favIconSvg.src} />
|
||||
<link rel="mask-icon" href={favIconSvg.src} color="#8D46E7" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href={appleTouchIcon.src} />
|
|
@ -1,66 +0,0 @@
|
|||
import { BygulIcon } from "../assets/icons/BygulIcon";
|
||||
import { DragonsChildLogo } from "../assets/logos/DragonsChild";
|
||||
|
||||
export const Footer = () => {
|
||||
return (
|
||||
<footer>
|
||||
<div className="pt-10 lg:pt-20 lg:pb-12 bg-customDarkBg1 radius-for-skewed ">
|
||||
<div className="container mx-auto px-4 w-4/5 md:w-11/12 lg:w-10/12 xl:w-4/5 2xl:w-2/3">
|
||||
<div className="flex flex-wrap">
|
||||
<div className="w-full lg:w-1/3 mb-16 lg:mb-0">
|
||||
<div className="flex justify-center lg:justify-start items-center grow basis-0">
|
||||
<div className="text-white mr-2 text-6xl">
|
||||
<DragonsChildLogo />
|
||||
</div>
|
||||
<div className="text-white font-['Inter'] font-bold text-xl">
|
||||
Dragons child studios
|
||||
</div>
|
||||
</div>
|
||||
<p className="mb-10 mt-4 sm:w-[22rem] lg:w-[20rem] xl:w-[24rem] text-gray-400 leading-loose text-center lg:text-left mx-auto lg:mx-0">
|
||||
Dragons child studios: Creating fun stuff since 2023!
|
||||
</p>
|
||||
<div className="w-36 mx-auto lg:mx-0">
|
||||
<a
|
||||
className="inline-block w-10 h-10 mr-2 p-2 bg-customDarkBg2 custom-border-gray hover:bg-gray-700 rounded-xl"
|
||||
href="https://valkyriecoms.com/@Dragonschildstudios"
|
||||
>
|
||||
<BygulIcon />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full lg:w-1/2 lg:pl-16 hidden lg:flex flex-wrap justify-between">
|
||||
<div className="w-full md:w-1/3 lg:w-auto mb-16 md:mb-0">
|
||||
<h3 className="mb-6 text-2xl font-bold text-white">Our projects</h3>
|
||||
<ul>
|
||||
<li className="mb-4">
|
||||
<a className="text-gray-400 hover:text-gray-300" href="https://toastielab.dev" aria-label="" >
|
||||
Toastielab
|
||||
</a>
|
||||
</li>
|
||||
<li className="mb-4">
|
||||
<a className="text-gray-400 hover:text-gray-300" href="https://valkyriecoms.com/" aria-label="" >
|
||||
Valkyriecoms
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-full md:w-1/2 lg:w-auto">
|
||||
<h3 className="mb-6 text-2xl font-bold text-white">Company</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<a className="text-gray-400 hover:text-gray-300" href="mailto:contact@dragonschildstudios.com" aria-label="" >
|
||||
Contact Us
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="lg:text-center text-sm text-gray-400 border-t border-[rgb(255,255,255,0.2)] pt-12 mt-16 hidden lg:block">
|
||||
© 2023 Dragons child studios.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
|
@ -1,91 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { InvitationModal } from "./InvitationModal";
|
||||
import dashboard from "../assets/images/dashboard.png";
|
||||
|
||||
export const Hero = () => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<section
|
||||
className="w-screen flex justify-center items-center bg-customDarkBg1 mb-[28vw] md:mb-[18vw] lg:mb-[10vw] xl:mb-[13vw] 2xl:mb-60 hero-bg-gradient pb-24 sm:pb-32 md:pb-44 lg:pb-0"
|
||||
id="home"
|
||||
>
|
||||
<div className="w-full md:w-[800px] xl:w-[900px] flex flex-col justify-center items-center pt-10 md:pt-12 lg:pt-16 text-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<div className="text-customSecondary text-sm sm:text-base mb-6 sm:mt-32 mt-16 font-bold">
|
||||
Dragons child studios
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.05 }}
|
||||
>
|
||||
<div className="text-5xl sm:text-6xl lg:text-7xl xl:text-7xl font-bold tracking-wide text-white px-8 sm:px-8 md:px-20 lg:px-4">
|
||||
<span className="inline md:hidden">The Game</span>{" "}
|
||||
<span className="hidden md:inline">The Game</span>
|
||||
</div>
|
||||
<div className="mt-2 sm:mt-2 text-4xl sm:text-6xl lg:text-7xl xl:text-7xl font-bold tracking-wide text-white px-8 sm:px-20 md:px-24 lg:px-24">
|
||||
Company
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<div className="text-customGrayText text-sm lg:text-base xl:text-lg sm:text-base mt-10 px-12 sm:px-48 ">
|
||||
We are a company that likes to have fun
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5, delay: 0.15 }}
|
||||
>
|
||||
<div className="flex flex-col gap-2 sm:flex-row mt-14 mb-24 sm:mb-40 justify-center">
|
||||
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10, zIndex: 20 }}
|
||||
animate={{ opacity: 1, y: 0, zIndex: 20 }}
|
||||
transition={{ duration: 0.5, delay: 0.15 }}
|
||||
>
|
||||
<div className="relative w-screen flex justify-center ">
|
||||
<img
|
||||
src={dashboard}
|
||||
alt="123"
|
||||
className="w-4/5 2xl:w-[1200px] mx-auto absolute z-10 rounded-xl custom-border-gray hero-dashboard-border-gradient lg:top-6 xl:top-0"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
<div className="relative w-screen flex justify-center ">
|
||||
<div className="custom-shape-divider-bottom-1665343298 mt-4 sm:mt-16 md:mt-52 hidden lg:block">
|
||||
<svg
|
||||
data-name="Layer 1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1200 120"
|
||||
preserveAspectRatio="none"
|
||||
className=" bg-customDarkBg2"
|
||||
>
|
||||
<path
|
||||
d="M1200 0L0 0 598.97 114.72 1200 0z"
|
||||
className="shape-fill custom-bg-dark1"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isModalOpen && (
|
||||
<InvitationModal isOpen={isModalOpen} setIsOpen={setIsModalOpen} />
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
|
@ -1,90 +0,0 @@
|
|||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
import { CheckArrowIcon } from "../assets/icons/CheckArrowIcon";
|
||||
import { CloseIcon } from "../assets/icons/CloseIcon";
|
||||
import { DragonsChildLogo } from "../assets/logos/DragonsChild";
|
||||
|
||||
export const InvitationModal = ({ setIsOpen }) => (
|
||||
<AnimatePresence>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, zIndex: 50 }}
|
||||
animate={{ opacity: 1, zIndex: 50 }}
|
||||
transition={{ duration: 0.1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<div
|
||||
className="w-full h-full bg-customDarkBgTransparentDarker fixed top-0 left-0 flex z-50 justify-center items-center"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
<div
|
||||
className="w-full h-screen sm:h-auto sm:w-3/4 md:w-3/5 lg:w-[1000px] xl:w-[1100px] sm:rounded-2xl bg-customDarkBgTransparentLighter custom-border-gray-darker py-12 px-8 sm:px-16 backdrop-blur-xl fixed sm:mb-8 fixed mx-auto z-50"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="flex relative">
|
||||
<div className="w-1/2 hidden lg:inline">
|
||||
<h2 className="mt-6 mb-2 text-5xl font-bold tracking-normal text-white">
|
||||
Subscribe Now
|
||||
</h2>
|
||||
<h2 className="text-5xl font-bold tracking-normal text-customSecondary">
|
||||
Winter is coming
|
||||
</h2>
|
||||
|
||||
<ul className="mb-6 text-white mt-12">
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Vestibulum viverra</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Morbi mollis metus pretium</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Etiam lectus nunc, commodo</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-full lg:w-1/2 flex items-center flex-col justify-center pt-24 sm:pt-0">
|
||||
<div className="flex inline lg:hidden justify-start items-center grow basis-0 mb-8 pr-6">
|
||||
<div className="text-white mr-2 text-8xl">
|
||||
<DragonsChildLogo />
|
||||
</div>
|
||||
<div className="text-white font-['Inter'] font-bold text-3xl">
|
||||
DragonsChild
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="mb-7 text-2xl text-white font-bold leading-snug text-center">
|
||||
Join 3,953 other developers
|
||||
</h3>
|
||||
<div className="flex flex-wrap -m-2">
|
||||
<div className="w-full sm:w-4/5 p-2 mx-auto">
|
||||
<input
|
||||
className="px-4 py-4 w-full text-gray-500 font-medium text-center placeholder-gray-500 outline-none border bg-gray-300 border-gray-300 rounded-lg focus:ring focus:ring-indigo-300"
|
||||
id="newsletterInput3-1"
|
||||
type="text"
|
||||
placeholder="Your email address"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full sm:w-4/5 p-2 mt-4 mx-auto">
|
||||
<button
|
||||
className="py-4 px-6 w-full text-white font-semibold rounded-xl shadow-4xl focus:ring focus:ring-indigo-300 bg-customPrimary hover:bg-[#7765e6] transition ease-in-out duration-200"
|
||||
type="button"
|
||||
>
|
||||
Join Now
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="fixed top-4 right-4 z-50 w-4 h-4 cursor-pointer"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
<CloseIcon />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
);
|
9
src/components/Logo.astro
Normal file
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
import { SITE } from '~/utils/config';
|
||||
---
|
||||
|
||||
<span
|
||||
class="self-center ml-2 rtl:ml-0 rtl:mr-2 text-2xl md:text-xl font-bold text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{SITE?.name}
|
||||
</span>
|
|
@ -1,99 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
|
||||
import { DragonsChildLogo } from "../assets/logos/DragonsChild";
|
||||
import { GithubIcon } from "../assets/icons/GithubIcon";
|
||||
import { BygulIcon } from "../assets/icons/BygulIcon";
|
||||
import { InstagramIcon } from "../assets/icons/InstagramIcon";
|
||||
import { TwitterIcon } from "../assets/icons/TwitterIcon";
|
||||
|
||||
export const Navbar = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<nav className="w-full h-20 flex flex-col justify-center items-center fixed bg-customDarkBg1 lg:bg-customDarkBgTransparent z-40 lg:backdrop-blur-xl">
|
||||
<div className="2xl:w-[1280px] xl:w-10/12 w-11/12 flex justify-between items-center relative">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<a className="navbar-link" href="#home" aria-label="Home">
|
||||
<div className="flex justify-start items-center grow basis-0">
|
||||
<div className="text-white mr-1 text-6xl">
|
||||
<DragonsChildLogo />
|
||||
</div>
|
||||
<div className="text-white font-['Inter'] font-bold text-xl">
|
||||
Dragons child studios
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<div className="hidden lg:flex h-full pl-12 pb-2">
|
||||
<a className="navbar-link" href="#home" aria-label="Home">
|
||||
Home
|
||||
</a>
|
||||
<a className="navbar-link" href="https://valkyriecoms.com/@Dragonschildstudios" aria-label="Our profile">
|
||||
Our profile
|
||||
</a>
|
||||
</div>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
</motion.div>
|
||||
<div
|
||||
className="lg:hidden flex flex-col px-2 py-3 border-solid border border-gray-600 rounded-md cursor-pointer hover:bg-customDarkBg2"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<div className="w-5 h-0.5 bg-gray-500 mb-1"></div>
|
||||
<div className="w-5 h-0.5 bg-gray-500 mb-1"></div>
|
||||
<div className="w-5 h-0.5 bg-gray-500 "></div>
|
||||
</div>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
<div
|
||||
className="flex flex-col mt-16 lg:hidden absolute top-4 left-0 bg-customDarkBg1 z-50 w-full
|
||||
items-center gap-10 pb-10 border-y border-solid border-customDarkBg3 pt-10
|
||||
"
|
||||
>
|
||||
<a
|
||||
className="navbar-link"
|
||||
href="#home"
|
||||
onClick={() => setIsOpen(false)}
|
||||
aria-label="Home"
|
||||
>
|
||||
Home
|
||||
</a>
|
||||
<a
|
||||
className="navbar-link"
|
||||
href="#profile"
|
||||
onClick={() => setIsOpen(false)}
|
||||
aria-label="Our profile"
|
||||
>
|
||||
Our profile
|
||||
</a>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</nav>
|
||||
);
|
||||
};
|
|
@ -1,205 +0,0 @@
|
|||
import { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
import { InvitationModal } from "./InvitationModal";
|
||||
import { CheckArrowIcon } from "../assets/icons/CheckArrowIcon";
|
||||
|
||||
export const Pricing = () => {
|
||||
const [isMonthly, setIsMonthly] = useState(true);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const handleChange = () => {
|
||||
setIsMonthly(!isMonthly);
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="w-screen flex justify-center bg-customDarkBg2 relative">
|
||||
<div className="absolute -top-16" id="pricing" />
|
||||
<div className="pb-20 pt-12 bg-customDarkBg2 2xl:w-[1150px] lg:w-[1050px] md:w-4/5 ">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.2 }}
|
||||
>
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-2xl mx-auto text-center mb-16">
|
||||
<span className="custom-block-subtitle">
|
||||
Dolor sit amet consectutar
|
||||
</span>
|
||||
<h2 className="mt-6 mb-6 text-4xl lg:text-5xl font-bold font-heading text-white">
|
||||
Choose your best plan
|
||||
</h2>
|
||||
<p className="mb-6 text-customGrayText">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
</p>
|
||||
<label className="mx-auto bg-customDarkBg3 relative flex justify-between items-center group text-xl w-44 h-12 rounded-lg pr-36 pl-1 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
className="peer appearance-none"
|
||||
checked={!isMonthly}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<span className="h-8 w-[5.5rem] flex items-center pr-2 bg-customDarkBg3 after:rounded-lg duration-300 ease-in-out after:w-[30rem] after:h-10 after:bg-customPrimary after:shadow-md after:duration-300 peer-checked:after:translate-x-[5.5rem] cursor-pointer"></span>
|
||||
<div className="flex absolute text-white text-sm font-bold">
|
||||
<div
|
||||
className={
|
||||
isMonthly ? "mr-9 ml-3" : "mr-9 ml-3 text-gray-400"
|
||||
}
|
||||
>
|
||||
Monthly
|
||||
</div>
|
||||
<div className={isMonthly && "text-gray-400"}>Yearly</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex flex-wrap flex-col lg:flex-row -mx-4 items-center mt-20">
|
||||
<div className="w-[350px] sm:w-[380px] lg:w-1/3 px-4 mb-8 lg:mb-0">
|
||||
<div className="p-8 bg-customDarkBg3 rounded-3xl">
|
||||
<h4 className="mb-2 text-xl font-bold font-heading text-white text-left">
|
||||
Beginner
|
||||
</h4>
|
||||
<div className="flex justify-start items-end">
|
||||
<div className="text-4xl sm:text-5xl font-bold text-white text-left mt-4 mr-2">
|
||||
$0
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
{isMonthly ? "/ month" : "/ year"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="mt-4 mb-6 2xl:mb-10 text-gray-500 leading-loose text-left">
|
||||
The perfect way to get started and get used to our tools.
|
||||
</p>
|
||||
<ul className="mb-2 2xl:mb-6 text-white">
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Vestibulum viverra</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Morbi mollis metus pretium</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Etiam lectus nunc, commodo</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Ut quam nisl mollis id pretium</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Suspendisse bibendum</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
className="inline-block text-center py-2 px-4 w-full rounded-xl rounded-t-xl custom-button-colored font-bold leading-loose mt-16"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
Get Started
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[350px] sm:w-[380px] lg:w-1/3 px-4 mb-8 lg:mb-0">
|
||||
<div className="px-8 py-8 bg-customDarkBg3 rounded-3xl">
|
||||
<h4 className="mb-2 2xl:mb-4 text-2xl font-bold font-heading text-white text-left">
|
||||
Standard
|
||||
</h4>
|
||||
<div className="flex justify-start items-end">
|
||||
<div className="text-4xl sm:text-5xl font-bold text-white text-left mt-4 mr-2">
|
||||
{isMonthly ? "$19" : "$180"}
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
{isMonthly ? "/ month" : "/ year"}
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-8 mb-8 2xl:mb-12 text-gray-500 leading-loose text-left">
|
||||
The perfect way to get started and get used to our tools.
|
||||
</p>
|
||||
<ul className="mb-14 text-white">
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Vestibulum viverra</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Morbi mollis metus pretium</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Etiam lectus nunc, commodo</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Ut quam nisl mollis id pretium</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Suspendisse bibendum</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
className="inline-block text-center py-2 px-4 w-full custom-button-colored leading-loose transition duration-200 mt-20"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
Get Started
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-[350px] sm:w-[380px] lg:w-1/3 px-4 mb-8 lg:mb-0">
|
||||
<div className="p-8 bg-customDarkBg3 rounded-3xl">
|
||||
<h4 className="mb-2 text-xl font-bold font-heading text-white text-left">
|
||||
Premium
|
||||
</h4>
|
||||
<div className="flex justify-start items-end">
|
||||
<div className="text-4xl sm:text-5xl font-bold text-white text-left mt-4 mr-2">
|
||||
{isMonthly ? "$36" : "$390"}
|
||||
</div>
|
||||
<div className="text-gray-500">
|
||||
{isMonthly ? "/ month" : "/ year"}
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-4 mb-6 2xl:mb-10 text-gray-500 leading-loose text-left">
|
||||
The perfect way to get started and get used to our tools.
|
||||
</p>
|
||||
<ul className="mb-2 2xl:mb-6 text-white">
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Vestibulum viverra</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Morbi mollis metus pretium</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Etiam lectus nunc, commodo</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Ut quam nisl mollis id pretium</span>
|
||||
</li>
|
||||
<li className="mb-4 flex">
|
||||
<CheckArrowIcon />
|
||||
<span>Suspendisse bibendum</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
className="inline-block text-center py-2 px-4 w-full rounded-xl rounded-t-xl custom-button-colored font-bold leading-loose mt-16"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
Get Started
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
{isModalOpen && (
|
||||
<InvitationModal isOpen={isModalOpen} setIsOpen={setIsModalOpen} />
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
|
@ -1,52 +0,0 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
export const ScrollUpButton = () => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("scroll", toggleVisible);
|
||||
}, []);
|
||||
|
||||
const toggleVisible = () => {
|
||||
const scrolled = document.documentElement.scrollTop;
|
||||
if (scrolled > 300) {
|
||||
setIsVisible(true);
|
||||
} else if (scrolled <= 300) {
|
||||
setIsVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{isVisible && (
|
||||
<div
|
||||
className="w-12 h-12 fixed bottom-6 right-6 custom-border-gray rounded-xl bg-customDarkBg2 hover:bg-customDarkBg3 cursor-pointer flex justify-center items-center transition z-50"
|
||||
onClick={scrollToTop}
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="35px"
|
||||
height="35px"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
d="M4.16732 12.5L10.0007 6.66667L15.834 12.5"
|
||||
stroke="#4F46E5"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,82 +0,0 @@
|
|||
import { Hero } from "./Hero.jsx";
|
||||
import { Navbar } from "./Navbar.jsx";
|
||||
import { Testimonials } from "./Testimonials.jsx";
|
||||
import { FeaturesDiagonal } from "./FeaturesDiagonal.jsx";
|
||||
import { Pricing } from "./Pricing.jsx";
|
||||
import { FAQ } from "./FAQ.jsx";
|
||||
import { Brands } from "./Brands.jsx";
|
||||
import { Divider } from "./Divider";
|
||||
import { Footer } from "./Footer.jsx";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export const Test = () => {
|
||||
return (
|
||||
<>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: [0, 360],
|
||||
}}
|
||||
transition={{ duration: 1, repeat: Infinity, delay: 0.5 }}
|
||||
>
|
||||
<Navbar />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: [0, 360],
|
||||
}}
|
||||
transition={{ duration: 1, repeat: Infinity, delay: 0.5 }}
|
||||
>
|
||||
<Hero />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: [0, 360],
|
||||
}}
|
||||
transition={{ duration: 1, repeat: Infinity, delay: 0.5 }}
|
||||
>
|
||||
<Testimonials />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: [0, 360],
|
||||
}}
|
||||
transition={{ duration: 1, repeat: Infinity, delay: 0.5 }}
|
||||
>
|
||||
<FeaturesDiagonal />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: [0, 360],
|
||||
}}
|
||||
transition={{ duration: 1, repeat: Infinity, delay: 0.5 }}
|
||||
>
|
||||
<Pricing />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: [0, 360],
|
||||
}}
|
||||
transition={{ duration: 1, repeat: Infinity, delay: 0.5 }}
|
||||
>
|
||||
<Brands />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: [0, 360],
|
||||
}}
|
||||
transition={{ duration: 1, repeat: Infinity, delay: 0.5 }}
|
||||
>
|
||||
<FAQ />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
animate={{
|
||||
rotate: [0, 360],
|
||||
}}
|
||||
transition={{ duration: 1, repeat: Infinity, delay: 0.5 }}
|
||||
>
|
||||
<Footer />
|
||||
</motion.div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,99 +0,0 @@
|
|||
import { QuoteIcon } from "../assets/icons/QuoteIcon";
|
||||
import testimonial1 from "../assets/images/testimonial1.png";
|
||||
import testimonial2 from "../assets/images/testimonial2.png";
|
||||
import testimonial3 from "../assets/images/testimonial3.png";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
export const Testimonials = () => (
|
||||
<section className="w-full flex justify-center pt-10 mb-16 lg:mb-32 bg-customDarkBg2 relative">
|
||||
<div className="absolute -top-16" id="feedback" />
|
||||
<div className="flex flex-col w-full lg:w-[1150px] justify-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.4, delay: 0.3 }}
|
||||
>
|
||||
<div className="custom-block-subtitle text-center mb-6">
|
||||
Look at those good reviews!
|
||||
</div>
|
||||
<div className="custom-block-big-title text-center mb-16 px-8 sm:px-24 md:px-48">
|
||||
People like you love DragonsChild
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col lg:flex-row gap-8 lg:gap-5 xl:gap-10 px-6 xl:px-0 items-center">
|
||||
<div className="w-11/12 sm:w-4/5 md:w-[560px] lg:w-1/3 custom-border-gray-darker rounded-xl bg-customDarkBg3 flex flex-col px-6 py-4">
|
||||
<div className="flex mb-2">
|
||||
<QuoteIcon />
|
||||
</div>
|
||||
<div className="custom-content-text-white">
|
||||
"Lorem ipsum dolor sit amet, consecte adipiscing elit. Phasellus
|
||||
pulvinar urna quis tempor gravida. Sed commodo bibendum orci, sed
|
||||
tincidunt lectus dignissim vel. Sed et maximus odio, eu ultrices
|
||||
magna. Etiam finibus tempor eu nunc vitae tristique. Cras mattis
|
||||
sapien. Etiam finibus gravida."
|
||||
</div>
|
||||
<div className="flex mt-4 mb-2 xl:mt-8 xl:mb-4">
|
||||
<img src={testimonial1} alt="" width="45px" />
|
||||
<div className="flex flex-col ml-4">
|
||||
<div className="custom-content-text-white font-medium">
|
||||
That one guys
|
||||
</div>
|
||||
<div className="custom-content-text-gray">
|
||||
Did a thing
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-11/12 sm:w-4/5 md:w-[560px] lg:w-1/3 custom-border-gray-darker rounded-xl bg-customDarkBg3 flex flex-col px-6 py-4">
|
||||
<div className="flex mb-2">
|
||||
<QuoteIcon />
|
||||
</div>
|
||||
<div className="custom-content-text-white">
|
||||
"Lorem ipsum dolor sit amet, consecte adipiscing elit. Phasellus
|
||||
pulvinar urna quis tempor gravida. Sed commodo bibendum orci, sed
|
||||
tincidunt lectus dignissim vel. Sed et maximus odio, eu ultrices
|
||||
magna. Etiam finibus tempor eu nunc vitae tristique. Cras mattis
|
||||
sapien. Etiam finibus gravida."
|
||||
</div>
|
||||
<div className="flex mt-4 mb-2 xl:mt-8 xl:mb-4">
|
||||
<img src={testimonial2} alt="" width="45px" />
|
||||
<div className="flex flex-col ml-4">
|
||||
<div className="custom-content-text-white font-medium">
|
||||
That other guy
|
||||
</div>
|
||||
<div className="custom-content-text-gray">
|
||||
Also probably does something
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-11/12 sm:w-4/5 md:w-[560px] lg:w-1/3 custom-border-gray-darker rounded-xl bg-customDarkBg3 flex flex-col px-6 py-4">
|
||||
<div className="flex mb-2">
|
||||
<QuoteIcon />
|
||||
</div>
|
||||
<div className="custom-content-text-white">
|
||||
"Lorem ipsum dolor sit amet, consecte adipiscing elit. Phasellus
|
||||
pulvinar urna quis tempor gravida. Sed commodo bibendum orci, sed
|
||||
tincidunt lectus dignissim vel. Sed et maximus odio, eu ultrices
|
||||
magna. Etiam finibus tempor eu nunc vitae tristique. Cras mattis
|
||||
sapien. Etiam finibus gravida."
|
||||
</div>
|
||||
<div className="flex mt-4 mb-2 xl:mt-8 xl:mb-4">
|
||||
<img src={testimonial3} alt="" width="45px" />
|
||||
<div className="flex flex-col ml-4">
|
||||
<div className="custom-content-text-white font-medium">
|
||||
Craig
|
||||
</div>
|
||||
<div className="custom-content-text-gray">
|
||||
Is cool
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
14
src/components/blog/Grid.astro
Normal 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>
|
55
src/components/blog/GridItem.astro
Normal file
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
import { APP_BLOG } from '~/utils/config';
|
||||
import type { Post } from '~/types';
|
||||
|
||||
import Image from '~/components/common/Image.astro';
|
||||
|
||||
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 && (
|
||||
<a href={getPermalink(post.permalink, 'post')}>
|
||||
<Image
|
||||
src={image}
|
||||
class="w-full md:h-full rounded shadow-lg bg-gray-400 dark:bg-slate-700"
|
||||
widths={[400, 900]}
|
||||
width={400}
|
||||
sizes="(max-width: 900px) 400px, 900px"
|
||||
alt={post.title}
|
||||
aspectRatio="16:9"
|
||||
layout="cover"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</a>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<h3 class="mb-2 text-xl font-bold leading-tight sm:text-2xl font-heading">
|
||||
{
|
||||
!APP_BLOG?.post?.isEnabled ? (
|
||||
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}</p>
|
||||
</article>
|
12
src/components/blog/Headline.astro
Normal 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>
|
20
src/components/blog/List.astro
Normal 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>
|
83
src/components/blog/ListItem.astro
Normal file
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
import type { ImageMetadata } from 'astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import Image from '~/components/common/Image.astro';
|
||||
import PostTags from '~/components/blog/Tags.astro';
|
||||
|
||||
import { APP_BLOG } from '~/utils/config';
|
||||
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)) as ImageMetadata | undefined;
|
||||
|
||||
const link = APP_BLOG?.post?.isEnabled ? 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 && (
|
||||
<Image
|
||||
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]}
|
||||
width={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)} class="inline-block">{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>
|
36
src/components/blog/Pagination.astro
Normal file
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
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">
|
||||
<Button
|
||||
variant="tertiary"
|
||||
class={`md:px-3 px-3 mr-2 ${!prevUrl ? 'invisible' : ''}`}
|
||||
href={getPermalink(prevUrl)}
|
||||
>
|
||||
<Icon name="tabler:chevron-left" class="w-6 h-6" />
|
||||
<p class="ml-2">{prevText}</p>
|
||||
</Button>
|
||||
|
||||
<Button variant="tertiary" class={`md:px-3 px-3 ${!nextUrl ? 'invisible' : ''}`} href={getPermalink(nextUrl)}>
|
||||
<span class="mr-2">{nextText}</span>
|
||||
<Icon name="tabler:chevron-right" class="w-6 h-6" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
90
src/components/blog/SinglePost.astro
Normal file
|
@ -0,0 +1,90 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
import Image from '~/components/common/Image.astro';
|
||||
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;
|
||||
const { Content } = post;
|
||||
---
|
||||
|
||||
<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)} class="inline-block">{getFormattedDate(post.publishDate)}</time>
|
||||
{
|
||||
post.category && (
|
||||
<>
|
||||
{' '}
|
||||
·{' '}
|
||||
<a class="capitalize hover:underline inline-block" 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 ? (
|
||||
<Image
|
||||
src={post.image}
|
||||
class="max-w-full lg:max-w-[900px] 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?.excerpt || ''}
|
||||
width={900}
|
||||
height={506}
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
/>
|
||||
) : (
|
||||
<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 prose-headings:scroll-mt-[80px]"
|
||||
>
|
||||
{
|
||||
Content ? (
|
||||
<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 rtl:mr-0 rtl:ml-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>
|
41
src/components/blog/Tags.astro
Normal file
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
|
||||
import { APP_BLOG } from '~/utils/config';
|
||||
import type { Post } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
tags: Post['tags'];
|
||||
class?: string;
|
||||
title?: string | undefined;
|
||||
isCategory?: boolean;
|
||||
}
|
||||
|
||||
const { tags, class: className = 'text-sm', title = undefined, isCategory = false } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
tags && Array.isArray(tags) && (
|
||||
<>
|
||||
<>
|
||||
{title !== undefined && <span class="align-super font-normal underline underline-offset-4 decoration-2 dark:text-slate-400">{title}</span>}
|
||||
</>
|
||||
<ul class={className}>
|
||||
{tags.map((tag) => (
|
||||
<li class="bg-gray-100 dark:bg-slate-700 inline-block mr-2 rtl:mr-0 rtl:ml-2 mb-2 py-0.5 px-2 lowercase font-medium">
|
||||
{!APP_BLOG?.tag?.isEnabled ? (
|
||||
tag
|
||||
) : (
|
||||
<a
|
||||
href={getPermalink(tag, (isCategory ? 'category' : 'tag'))}
|
||||
class="text-muted dark:text-slate-300 hover:text-primary dark:hover:text-gray-200"
|
||||
>
|
||||
{tag}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
20
src/components/blog/ToBlogLink.astro
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { getBlogPermalink } from '~/utils/permalinks';
|
||||
import { I18N } from '~/utils/config';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
const { textDirection } = I18N;
|
||||
---
|
||||
|
||||
<div class="mx-auto px-6 sm:px-6 max-w-3xl pt-8 md:pt-4 pb-12 md:pb-20">
|
||||
<Button variant="tertiary" class="px-3 md:px-3" href={getBlogPermalink()}>
|
||||
{
|
||||
textDirection === 'rtl' ? (
|
||||
<Icon name="tabler:chevron-right" class="w-5 h-5 mr-1 -ml-1.5 rtl:-mr-1.5 rtl:ml-1" />
|
||||
) : (
|
||||
<Icon name="tabler:chevron-left" class="w-5 h-5 mr-1 -ml-1.5 rtl:-mr-1.5 rtl:ml-1" />
|
||||
)
|
||||
} Back to Blog
|
||||
</Button>
|
||||
</div>
|
10
src/components/common/Analytics.astro
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
import { GoogleAnalytics } from '@astrolib/analytics';
|
||||
import { ANALYTICS } from '~/utils/config';
|
||||
---
|
||||
|
||||
{
|
||||
ANALYTICS?.vendors?.googleAnalytics?.id ? (
|
||||
<GoogleAnalytics id={String(ANALYTICS.vendors.googleAnalytics.id)} partytown={ANALYTICS?.vendors?.googleAnalytics?.partytown} />
|
||||
) : null
|
||||
}
|
33
src/components/common/ApplyColorMode.astro
Normal file
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
import { UI } from "~/utils/config";
|
||||
|
||||
// TODO: This code is temporary
|
||||
---
|
||||
|
||||
<script is:inline define:vars={{ defaultTheme: UI.theme || "system" }}>
|
||||
function applyTheme(theme) {
|
||||
if (theme === "dark") {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
const matches = document.querySelectorAll("[data-aw-toggle-color-scheme] > input");
|
||||
|
||||
if (matches && matches.length) {
|
||||
matches.forEach((elem) => {
|
||||
elem.checked = theme !== "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");
|
||||
}
|
||||
</script>
|
161
src/components/common/BasicScripts.astro
Normal file
|
@ -0,0 +1,161 @@
|
|||
---
|
||||
import { UI } from '~/utils/config';
|
||||
---
|
||||
|
||||
<script is:inline define:vars={{ defaultTheme: UI.theme }}>
|
||||
if (window.basic_script) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.basic_script = true;
|
||||
|
||||
function applyTheme(theme) {
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
const initTheme = function () {
|
||||
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');
|
||||
}
|
||||
};
|
||||
initTheme();
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const onLoad = function () {
|
||||
let lastKnownScrollPosition = window.scrollY;
|
||||
let ticking = true;
|
||||
|
||||
attachEvent('#header nav', 'click', function () {
|
||||
document.querySelector('[data-aw-toggle-menu]')?.classList.remove('expanded');
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
document.getElementById('header')?.classList.remove('h-screen');
|
||||
document.getElementById('header')?.classList.remove('expanded');
|
||||
document.getElementById('header')?.classList.remove('bg-page');
|
||||
document.querySelector('#header nav')?.classList.add('hidden');
|
||||
document.querySelector('#header > div > div:last-child')?.classList.add('hidden');
|
||||
});
|
||||
|
||||
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.getElementById('header')?.classList.toggle('expanded');
|
||||
document.getElementById('header')?.classList.toggle('bg-page');
|
||||
document.querySelector('#header nav')?.classList.toggle('hidden');
|
||||
document.querySelector('#header > div > div:last-child')?.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();
|
||||
});
|
||||
|
||||
let screenSize = window.matchMedia('(max-width: 767px)');
|
||||
screenSize.addEventListener('change', function () {
|
||||
document.querySelector('[data-aw-toggle-menu]')?.classList.remove('expanded');
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
document.getElementById('header')?.classList.remove('h-screen');
|
||||
document.getElementById('header')?.classList.remove('expanded');
|
||||
document.getElementById('header')?.classList.remove('bg-page');
|
||||
document.querySelector('#header nav')?.classList.add('hidden');
|
||||
document.querySelector('#header > div > div:last-child')?.classList.add('hidden');
|
||||
});
|
||||
|
||||
function appyHeaderStylesOnScroll() {
|
||||
const header = document.querySelector('#header[data-aw-sticky-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;
|
||||
}
|
||||
});
|
||||
};
|
||||
const 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.getElementById('header')?.classList.remove('expanded');
|
||||
document.querySelector('#header nav')?.classList.add('hidden');
|
||||
};
|
||||
|
||||
window.onload = onLoad;
|
||||
window.onpageshow = onPageShow;
|
||||
|
||||
document.addEventListener('astro:after-swap', () => {
|
||||
initTheme();
|
||||
onLoad();
|
||||
onPageShow();
|
||||
});
|
||||
</script>
|
8
src/components/common/CommonMeta.astro
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
import { getAsset } from '~/utils/permalinks';
|
||||
---
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<link rel="sitemap" href={getAsset('/sitemap-index.xml')} />
|
52
src/components/common/Image.astro
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
import { findImage } from '~/utils/images';
|
||||
import {
|
||||
getImagesOptimized,
|
||||
astroAsseetsOptimizer,
|
||||
unpicOptimizer,
|
||||
type ImageProps,
|
||||
type AttributesProps
|
||||
} from '~/utils/images-optimization';
|
||||
|
||||
type Props = ImageProps;
|
||||
type ImageType = {
|
||||
src: string;
|
||||
attributes: AttributesProps;
|
||||
}
|
||||
|
||||
const props = Astro.props;
|
||||
|
||||
if (props.alt === undefined || props.alt === null) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
if (typeof props.width === 'string') {
|
||||
props.width = parseInt(props.width);
|
||||
}
|
||||
|
||||
if (typeof props.height === 'string') {
|
||||
props.height = parseInt(props.height);
|
||||
}
|
||||
|
||||
if (!props.loading) {
|
||||
props.loading = 'lazy';
|
||||
}
|
||||
|
||||
if (!props.decoding) {
|
||||
props.decoding = 'async';
|
||||
}
|
||||
|
||||
const _image = await findImage(props.src);
|
||||
|
||||
let image: ImageType | undefined = undefined;
|
||||
|
||||
if (_image !== null && typeof _image === 'object') {
|
||||
image = await getImagesOptimized(_image, props, astroAsseetsOptimizer);
|
||||
} else if (typeof _image === 'string' && (_image.startsWith('http://') || _image.startsWith('https://'))) {
|
||||
image = await getImagesOptimized(_image, props, unpicOptimizer);
|
||||
} else if (_image) {
|
||||
image = await getImagesOptimized(_image, props);
|
||||
}
|
||||
---
|
||||
|
||||
{!image ? <Fragment /> : <img src={image.src} {...image.attributes} />}
|
68
src/components/common/Metadata.astro
Normal file
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
import merge from 'lodash.merge';
|
||||
import { AstroSeo } from '@astrolib/seo';
|
||||
|
||||
import type { Props as AstroSeoProps } from '@astrolib/seo';
|
||||
|
||||
import { SITE, METADATA, I18N } from '~/utils/config';
|
||||
import type { MetaData } from '~/types';
|
||||
import { getCanonical } from '~/utils/permalinks';
|
||||
|
||||
import { adaptOpenGraphImages } from '~/utils/images';
|
||||
|
||||
export interface Props extends MetaData {
|
||||
dontUseTitleTemplate?: boolean;
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
ignoreTitleTemplate = false,
|
||||
canonical = String(getCanonical(String(Astro.url.pathname))),
|
||||
robots = {},
|
||||
description,
|
||||
openGraph = {},
|
||||
twitter = {},
|
||||
} = Astro.props;
|
||||
|
||||
const seoProps: AstroSeoProps = merge(
|
||||
{
|
||||
title: '',
|
||||
titleTemplate: '%s',
|
||||
canonical: canonical,
|
||||
noindex: true,
|
||||
nofollow: true,
|
||||
description: undefined,
|
||||
openGraph: {
|
||||
url: canonical,
|
||||
site_name: SITE?.name,
|
||||
images: [],
|
||||
locale: I18N?.language || 'en',
|
||||
type: 'website',
|
||||
},
|
||||
twitter: {
|
||||
cardType: openGraph?.images?.length ? 'summary_large_image' : 'summary',
|
||||
},
|
||||
},
|
||||
{
|
||||
title: METADATA?.title?.default,
|
||||
titleTemplate: METADATA?.title?.template,
|
||||
noindex: typeof METADATA?.robots?.index !== 'undefined' ? !METADATA.robots.index : undefined,
|
||||
nofollow: typeof METADATA?.robots?.follow !== 'undefined' ? !METADATA.robots.follow : undefined,
|
||||
description: METADATA?.description,
|
||||
openGraph: METADATA?.openGraph,
|
||||
twitter: METADATA?.twitter,
|
||||
},
|
||||
{
|
||||
title: title,
|
||||
titleTemplate: ignoreTitleTemplate ? '%s' : undefined,
|
||||
canonical: canonical,
|
||||
noindex: typeof robots?.index !== 'undefined' ? !robots.index : undefined,
|
||||
nofollow: typeof robots?.follow !== 'undefined' ? !robots.follow : undefined,
|
||||
description: description,
|
||||
openGraph: { url: canonical, ...openGraph },
|
||||
twitter: twitter,
|
||||
}
|
||||
);
|
||||
---
|
||||
|
||||
<AstroSeo {...{ ...seoProps, openGraph: await adaptOpenGraphImages(seoProps?.openGraph, Astro.site) }} />
|
5
src/components/common/SiteVerification.astro
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
import { SITE } from "~/utils/config";
|
||||
---
|
||||
|
||||
{SITE.googleSiteVerificationId && <meta name="google-site-verification" content={SITE.googleSiteVerificationId} />}
|
45
src/components/common/SocialShare.astro
Normal file
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
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 rtl:ml-0 rtl:mr-2" title="Twitter Share" data-aw-social-share="twitter" data-aw-url={url} data-aw-text={text}
|
||||
><Icon
|
||||
name="tabler:brand-x"
|
||||
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 rtl:ml-0 rtl:mr-2" title="Facebook Share" data-aw-social-share="facebook" data-aw-url={url}
|
||||
><Icon
|
||||
name="tabler:brand-facebook"
|
||||
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 rtl:ml-0 rtl:mr-2" title="Linkedin Share" data-aw-social-share="linkedin" data-aw-url={url} data-aw-text={text}
|
||||
><Icon
|
||||
name="tabler:brand-linkedin"
|
||||
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 rtl:ml-0 rtl:mr-2" title="Whatsapp Share" data-aw-social-share="whatsapp" data-aw-url={url} data-aw-text={text}
|
||||
><Icon
|
||||
name="tabler:brand-whatsapp"
|
||||
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 rtl:ml-0 rtl:mr-2" title="Email Share" data-aw-social-share="mail" data-aw-url={url} data-aw-text={text}
|
||||
><Icon
|
||||
name="tabler:mail"
|
||||
class="w-6 h-6 text-gray-400 dark:text-slate-500 hover:text-black dark:hover:text-slate-300"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
6
src/components/common/SplitbeeAnalytics.astro
Normal 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>
|
34
src/components/common/ToggleMenu.astro
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
export interface Props {
|
||||
label?: string;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
label = 'Toggle Menu',
|
||||
class:
|
||||
className = "flex flex-col h-12 w-12 rounded justify-center items-center cursor-pointer group",
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<button
|
||||
class={className}
|
||||
aria-label={label}
|
||||
data-aw-toggle-menu
|
||||
>
|
||||
<span class="sr-only">{label}</span>
|
||||
<slot>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="h-0.5 w-6 my-1 rounded-full bg-black dark:bg-white transition ease transform duration-200 opacity-80 group-[.expanded]:rotate-45 group-[.expanded]:translate-y-2.5"
|
||||
></span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="h-0.5 w-6 my-1 rounded-full bg-black dark:bg-white transition ease transform duration-200 opacity-80 group-[.expanded]:opacity-0"
|
||||
></span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="h-0.5 w-6 my-1 rounded-full bg-black dark:bg-white transition ease transform duration-200 opacity-80 group-[.expanded]:-rotate-45 group-[.expanded]:-translate-y-2.5"
|
||||
></span>
|
||||
</slot>
|
||||
</button>
|
28
src/components/common/ToggleTheme.astro
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
import { UI } from '~/utils/config';
|
||||
|
||||
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;
|
||||
---
|
||||
|
||||
{
|
||||
!(UI.theme && UI.theme.endsWith(':only')) && (
|
||||
<button type="button" class={className} aria-label={label} data-aw-toggle-color-scheme>
|
||||
<Icon name={iconName} class={iconClass} />
|
||||
</button>
|
||||
)
|
||||
}
|
7
src/components/ui/Background.astro
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
const { isDark = false } = Astro.props;
|
||||
---
|
||||
|
||||
<div class:list={['absolute inset-0', { 'bg-dark dark:bg-transparent': isDark }]}>
|
||||
<slot />
|
||||
</div>
|
40
src/components/ui/Button.astro
Normal file
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
import { Icon } from 'astro-icon/components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { CallToAction } from '~/types';
|
||||
|
||||
const {
|
||||
variant = 'secondary',
|
||||
target,
|
||||
text = Astro.slots.render('default'),
|
||||
icon = '',
|
||||
class: className = '',
|
||||
type,
|
||||
...rest
|
||||
} = Astro.props as CallToAction;
|
||||
|
||||
const variants = {
|
||||
primary: 'btn-primary',
|
||||
secondary: 'btn-secondary',
|
||||
tertiary: 'btn btn-tertiary',
|
||||
link: 'cursor-pointer hover:text-primary',
|
||||
};
|
||||
---
|
||||
|
||||
{
|
||||
type === 'button' || type === 'submit' || type === 'reset' ? (
|
||||
<button type={type} class={twMerge(variants[variant] || '', className)} {...rest}>
|
||||
<Fragment set:html={text} />
|
||||
{icon && <Icon name={icon} class="w-5 h-5 ml-1 -mr-1.5 rtl:mr-1 rtl:-ml-1.5 inline-block" />}
|
||||
</button>
|
||||
) : (
|
||||
<a
|
||||
class={twMerge(variants[variant] || '', className)}
|
||||
{...(target ? { target: target, rel: 'noopener noreferrer' } : {})}
|
||||
{...rest}
|
||||
>
|
||||
<Fragment set:html={text} />
|
||||
{icon && <Icon name={icon} class="w-5 h-5 ml-1 -mr-1.5 rtl:mr-1 rtl:-ml-1.5 inline-block" />}
|
||||
</a>
|
||||
)
|
||||
}
|
23
src/components/ui/DListItem.astro
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
// component: DListItem
|
||||
//
|
||||
// Mimics the html 'dl' (description list)
|
||||
//
|
||||
// The 'dt' item is the item 'term' and is inserted into an 'h6' tag.
|
||||
// Caller needs to style the 'h6' tag appropriately.
|
||||
//
|
||||
// You can put pretty much any content you want between the open and
|
||||
// closing tags - it's simply contained in an enclosing div with a
|
||||
// margin left. No need for 'dd' items.
|
||||
//
|
||||
const {
|
||||
dt
|
||||
} = Astro.props;
|
||||
interface Props {
|
||||
dt:string
|
||||
}
|
||||
|
||||
const content:string = await Astro.slots.render('default');
|
||||
---
|
||||
<h6 set:html={dt}></h6>
|
||||
<div class="dd ml-8" set:html={content}/>
|
85
src/components/ui/Form.astro
Normal file
|
@ -0,0 +1,85 @@
|
|||
---
|
||||
import type { Form } from '~/types';
|
||||
import Button from '~/components/ui/Button.astro';
|
||||
|
||||
const { inputs, textarea, disclaimer, button = 'Contact us', description = '' } = Astro.props as Form;
|
||||
---
|
||||
|
||||
<form>
|
||||
{
|
||||
inputs &&
|
||||
inputs.map(
|
||||
({ type = 'text', name, label = '', autocomplete = 'on', placeholder = '' }) =>
|
||||
name && (
|
||||
<div class="mb-6">
|
||||
{label && (
|
||||
<label for={name} class="block text-sm font-medium">
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<input
|
||||
type={type}
|
||||
name={name}
|
||||
id={name}
|
||||
autocomplete={autocomplete}
|
||||
placeholder={placeholder}
|
||||
class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
textarea && (
|
||||
<div>
|
||||
<label for="textarea" class="block text-sm font-medium">
|
||||
{textarea.label}
|
||||
</label>
|
||||
<textarea
|
||||
id="textarea"
|
||||
name="textarea"
|
||||
rows={textarea.rows ? textarea.rows : 4}
|
||||
placeholder={textarea.placeholder}
|
||||
class="py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
disclaimer && (
|
||||
<div class="mt-3 flex items-start">
|
||||
<div class="flex mt-0.5">
|
||||
<input
|
||||
id="disclaimer"
|
||||
name="disclaimer"
|
||||
type="checkbox"
|
||||
class="cursor-pointer mt-1 py-3 px-4 block w-full text-md rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3">
|
||||
<label for="disclaimer" class="cursor-pointer select-none text-sm text-gray-600 dark:text-gray-400">
|
||||
{disclaimer.label}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
button && (
|
||||
<div class="mt-10 grid">
|
||||
<Button variant="primary" type="submit">{button}</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
description && (
|
||||
<div class="mt-3 text-center">
|
||||
<p class="text-sm text-gray-600 dark:text-gray-400">{description}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</form>
|
48
src/components/ui/Headline.astro
Normal file
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
import type { Headline } from "~/types";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render("title"),
|
||||
subtitle = await Astro.slots.render("subtitle"),
|
||||
tagline,
|
||||
classes = {},
|
||||
} = Astro.props as Headline;
|
||||
|
||||
const {
|
||||
container: containerClass = "max-w-3xl",
|
||||
title: titleClass = "text-3xl md:text-4xl ",
|
||||
subtitle: subtitleClass = "text-xl",
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
(title || subtitle || tagline) && (
|
||||
<div
|
||||
class={twMerge("mb-8 md:mx-auto md:mb-12 text-center", containerClass)}
|
||||
>
|
||||
{tagline && (
|
||||
<p
|
||||
class="text-base text-secondary dark:text-blue-200 font-bold tracking-wide uppercase"
|
||||
set:html={tagline}
|
||||
/>
|
||||
)}
|
||||
{title && (
|
||||
<h2
|
||||
class={twMerge(
|
||||
"font-bold leading-tighter tracking-tighter font-heading text-heading text-3xl",
|
||||
titleClass
|
||||
)}
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
|
||||
{subtitle && (
|
||||
<p
|
||||
class={twMerge("mt-4 text-muted", subtitleClass)}
|
||||
set:html={subtitle}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
71
src/components/ui/ItemGrid.astro
Normal file
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { ItemGrid } from '~/types';
|
||||
import Button from './Button.astro';
|
||||
import { Icon } from 'astro-icon/components';
|
||||
|
||||
const { items = [], columns, defaultIcon = '', classes = {} } = Astro.props as ItemGrid;
|
||||
|
||||
const {
|
||||
container: containerClass = '',
|
||||
panel: panelClass = '',
|
||||
title: titleClass = '',
|
||||
description: descriptionClass = '',
|
||||
icon: defaultIconClass = 'text-primary',
|
||||
action: actionClass = '',
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && (
|
||||
<div
|
||||
class={twMerge(
|
||||
`grid mx-auto gap-8 md:gap-y-12 ${
|
||||
columns === 4
|
||||
? 'lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 3
|
||||
? 'lg:grid-cols-3 sm:grid-cols-2'
|
||||
: columns === 2
|
||||
? 'sm:grid-cols-2 '
|
||||
: ''
|
||||
}`,
|
||||
containerClass
|
||||
)}
|
||||
>
|
||||
{items.map(({ title, description, icon, callToAction, classes: itemClasses = {} }) => (
|
||||
<div>
|
||||
<div class={twMerge('flex flex-row max-w-md', panelClass, itemClasses?.panel)}>
|
||||
<div class="flex justify-center">
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon
|
||||
name={icon || defaultIcon}
|
||||
class={twMerge('w-7 h-7 mr-2 rtl:mr-0 rtl:ml-2', defaultIconClass, itemClasses?.icon)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div class="mt-0.5">
|
||||
{title && <h3 class={twMerge('text-xl font-bold', titleClass, itemClasses?.title)}>{title}</h3>}
|
||||
{description && (
|
||||
<p
|
||||
class={twMerge(`${title ? 'mt-3' : ''} text-muted`, descriptionClass, itemClasses?.description)}
|
||||
set:html={description}
|
||||
/>
|
||||
)}
|
||||
{callToAction && (
|
||||
<div
|
||||
class={twMerge(
|
||||
`${title || description ? 'mt-3' : ''}`,
|
||||
actionClass,
|
||||
itemClasses?.actionClass
|
||||
)}
|
||||
>
|
||||
<Button variant="link" {...callToAction} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
94
src/components/ui/ItemGrid2.astro
Normal file
|
@ -0,0 +1,94 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import type { ItemGrid } from "~/types";
|
||||
import Button from "./Button.astro";
|
||||
|
||||
const {
|
||||
items = [],
|
||||
columns,
|
||||
defaultIcon = "",
|
||||
classes = {},
|
||||
} = Astro.props as ItemGrid;
|
||||
|
||||
const {
|
||||
container: containerClass = "",
|
||||
// container: containerClass = "sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
|
||||
panel: panelClass = "",
|
||||
title: titleClass = "",
|
||||
description: descriptionClass = "",
|
||||
icon: defaultIconClass = "text-primary",
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && (
|
||||
<div
|
||||
class={twMerge(
|
||||
`grid gap-8 gap-x-12 sm:gap-y-8 ${
|
||||
columns === 4
|
||||
? "lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2"
|
||||
: columns === 3
|
||||
? "lg:grid-cols-3 sm:grid-cols-2"
|
||||
: columns === 2
|
||||
? "sm:grid-cols-2 "
|
||||
: ""
|
||||
}`,
|
||||
containerClass
|
||||
)}
|
||||
>
|
||||
{items.map(
|
||||
({
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
callToAction,
|
||||
classes: itemClasses = {},
|
||||
}) => (
|
||||
<div
|
||||
class={twMerge(
|
||||
"relative flex flex-col",
|
||||
panelClass,
|
||||
itemClasses?.panel
|
||||
)}
|
||||
>
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon
|
||||
name={icon || defaultIcon}
|
||||
class={twMerge(
|
||||
"mb-2 w-10 h-10",
|
||||
defaultIconClass,
|
||||
itemClasses?.icon
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
class={twMerge(
|
||||
"text-xl font-bold",
|
||||
titleClass,
|
||||
itemClasses?.title
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
{description && (
|
||||
<p
|
||||
class={twMerge(
|
||||
"text-muted mt-2",
|
||||
descriptionClass,
|
||||
itemClasses?.description
|
||||
)}
|
||||
set:html={description}
|
||||
/>
|
||||
)}
|
||||
{callToAction && (
|
||||
<div class="mt-2">
|
||||
<Button {...callToAction} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
82
src/components/ui/Timeline.astro
Normal file
|
@ -0,0 +1,82 @@
|
|||
---
|
||||
import { Icon } from "astro-icon/components";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import type { Item } from "~/types";
|
||||
|
||||
export interface Props {
|
||||
items?: Array<Item>;
|
||||
defaultIcon?: string;
|
||||
classes?: Record<string, string>;
|
||||
}
|
||||
|
||||
const { items = [], classes = {}, defaultIcon } = Astro.props as Props;
|
||||
|
||||
const {
|
||||
container: containerClass = "",
|
||||
panel: panelClass = "",
|
||||
title: titleClass = "",
|
||||
description: descriptionClass = "",
|
||||
icon: defaultIconClass = "text-primary dark:text-slate-200 border-primary dark:border-blue-700",
|
||||
} = classes;
|
||||
---
|
||||
|
||||
{
|
||||
items && items.length && (
|
||||
<div class={containerClass}>
|
||||
{items.map(
|
||||
(
|
||||
{ title, description, icon, classes: itemClasses = {} },
|
||||
index = 0
|
||||
) => (
|
||||
<div class={twMerge("flex", panelClass, itemClasses?.panel)}>
|
||||
<div class="flex flex-col items-center mr-4 rtl:mr-0 rtl:ml-4">
|
||||
<div>
|
||||
<div class="flex items-center justify-center">
|
||||
{(icon || defaultIcon) && (
|
||||
<Icon
|
||||
name={icon || defaultIcon}
|
||||
class={twMerge(
|
||||
"w-10 h-10 p-2 rounded-full border-2",
|
||||
defaultIconClass,
|
||||
itemClasses?.icon
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{index !== items.length - 1 && (
|
||||
<div class="w-px h-full bg-black/10 dark:bg-slate-400/50" />
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
class={`pt-1 ${
|
||||
index !== items.length - 1 ? "pb-8" : ""
|
||||
}`}
|
||||
>
|
||||
{title && (
|
||||
<p
|
||||
class={twMerge(
|
||||
"text-xl font-bold",
|
||||
titleClass,
|
||||
itemClasses?.title
|
||||
)}
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
{description && (
|
||||
<p
|
||||
class={twMerge(
|
||||
"text-muted mt-2",
|
||||
descriptionClass,
|
||||
itemClasses?.description
|
||||
)}
|
||||
set:html={description}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
24
src/components/ui/WidgetWrapper.astro
Normal file
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import Background from "./Background.astro";
|
||||
|
||||
const { id, isDark = false, containerClass = "", bg, as = "section" } = Astro.props;
|
||||
|
||||
const WrapperTag = as;
|
||||
---
|
||||
|
||||
<WrapperTag class="relative not-prose scroll-mt-[72px]" {...id ? { id } : {}}>
|
||||
<div class="absolute inset-0 pointer-events-none -z-[1]" aria-hidden="true">
|
||||
<slot name="bg">
|
||||
{bg ? <Fragment set:html={bg} /> : <Background isDark={isDark} />}
|
||||
</slot>
|
||||
</div>
|
||||
<div
|
||||
class:list={[
|
||||
twMerge("relative mx-auto max-w-7xl px-4 md:px-6 py-12 md:py-16 lg:py-20 text-default", containerClass),
|
||||
{ dark: isDark },
|
||||
]}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</WrapperTag>
|
10
src/components/widgets/Announcement.astro
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
---
|
||||
|
||||
<div
|
||||
class="text-muted text-sm bg-[#dbeafe] dark:bg-transparent dark:border-b dark:border-slate-800 dark:text-slate-400 hidden md:block overflow-hidden px-3 py-2 relative text-ellipsis whitespace-nowrap"
|
||||
>
|
||||
<span class="dark:bg-slate-700 bg-white/40 dark:text-slate-300 font-semibold px-1 py-0.5 text-xs mr-0.5 rtl:mr-0 rtl:ml-0.5 inline-block">BETA</span>
|
||||
<a href="https://dragonschildstudios.com/" class="text-muted hover:underline dark:text-slate-400 font-medium">This site is in beta</a
|
||||
>
|
||||
</div>
|
64
src/components/widgets/BlogHighlightedPosts.astro
Normal file
|
@ -0,0 +1,64 @@
|
|||
---
|
||||
import { APP_BLOG } from "~/utils/config";
|
||||
|
||||
import Grid from "~/components/blog/Grid.astro";
|
||||
|
||||
import { getBlogPermalink } from "~/utils/permalinks";
|
||||
import { findPostsByIds } from "~/utils/blog";
|
||||
import WidgetWrapper from "~/components/ui/WidgetWrapper.astro";
|
||||
import type { Widget } from "~/types";
|
||||
|
||||
export interface Props extends Widget {
|
||||
title?: string;
|
||||
linkText?: string;
|
||||
linkUrl?: string | URL;
|
||||
information?: string;
|
||||
postIds: string[];
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render("title"),
|
||||
linkText = "View all posts",
|
||||
linkUrl = getBlogPermalink(),
|
||||
information = await Astro.slots.render("information"),
|
||||
postIds = [],
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render("bg"),
|
||||
} = Astro.props;
|
||||
|
||||
const posts = APP_BLOG.isEnabled ? await findPostsByIds(postIds) : [];
|
||||
---
|
||||
|
||||
{
|
||||
APP_BLOG.isEnabled ? (
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={classes?.container} bg={bg}>
|
||||
<div class="flex flex-col lg:justify-between lg:flex-row mb-8">
|
||||
{title && (
|
||||
<div class="md:max-w-sm">
|
||||
<h2
|
||||
class="text-3xl font-bold tracking-tight sm:text-4xl sm:leading-none group font-heading mb-2"
|
||||
set:html={title}
|
||||
/>
|
||||
{APP_BLOG.list.isEnabled && linkText && linkUrl && (
|
||||
<a
|
||||
class="text-muted dark:text-slate-400 hover:text-primary transition ease-in duration-200 block mb-6 lg:mb-0"
|
||||
href={linkUrl}
|
||||
>
|
||||
{linkText} »
|
||||
</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} />
|
||||
</WidgetWrapper>
|
||||
) : (
|
||||
<Fragment />
|
||||
)
|
||||
}
|
60
src/components/widgets/BlogLatestPosts.astro
Normal file
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
import { APP_BLOG } from "~/utils/config";
|
||||
|
||||
import Grid from "~/components/blog/Grid.astro";
|
||||
|
||||
import { getBlogPermalink } from "~/utils/permalinks";
|
||||
import { findLatestPosts } from "~/utils/blog";
|
||||
import WidgetWrapper from "~/components/ui/WidgetWrapper.astro";
|
||||
import type { Widget } from "~/types";
|
||||
import Button from "../ui/Button.astro";
|
||||
|
||||
export interface Props extends Widget {
|
||||
title?: string;
|
||||
linkText?: string;
|
||||
linkUrl?: string | URL;
|
||||
information?: string;
|
||||
count?: number;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render("title"),
|
||||
linkText = "View all posts",
|
||||
linkUrl = getBlogPermalink(),
|
||||
information = await Astro.slots.render("information"),
|
||||
count = 4,
|
||||
|
||||
id,
|
||||
isDark = false,
|
||||
classes = {},
|
||||
bg = await Astro.slots.render("bg"),
|
||||
} = Astro.props;
|
||||
|
||||
const posts = APP_BLOG.isEnabled ? await findLatestPosts({ count }) : [];
|
||||
---
|
||||
|
||||
{
|
||||
APP_BLOG.isEnabled ? (
|
||||
<WidgetWrapper id={id} isDark={isDark} containerClass={classes?.container} bg={bg}>
|
||||
<div class="flex flex-col lg:justify-between lg:flex-row mb-8">
|
||||
{title && (
|
||||
<div class="md:max-w-sm">
|
||||
<h2
|
||||
class="text-3xl font-bold tracking-tight sm:text-4xl sm:leading-none group font-heading mb-2"
|
||||
set:html={title}
|
||||
/>
|
||||
{APP_BLOG.list.isEnabled && linkText && linkUrl && (
|
||||
<Button variant="link" href={linkUrl}> {linkText} »</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{information && <p class="text-muted dark:text-slate-400 lg:text-sm lg:max-w-md" set:html={information} />}
|
||||
</div>
|
||||
|
||||
<Grid posts={posts} />
|
||||
</WidgetWrapper>
|
||||
) : (
|
||||
<Fragment />
|
||||
)
|
||||
}
|