Major site update

This commit is contained in:
Toastie (DCS Team) 2024-01-20 20:23:29 +13:00
parent ac444566d8
commit c3200077d6
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
162 changed files with 27430 additions and 5474 deletions

12
.editorconfig Normal file
View file

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

4
.eslintignore Normal file
View file

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

53
.eslintrc.js Normal file
View 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
View file

@ -18,3 +18,8 @@ pnpm-debug.log*
# macOS-specific files # macOS-specific files
.DS_Store .DS_Store
package-lock.json
pnpm-lock.yaml
.astro

2
.npmrc Normal file
View file

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

17963
.pnp.cjs generated Normal file

File diff suppressed because one or more lines are too long

2090
.pnp.loader.mjs generated Normal file

File diff suppressed because it is too large Load diff

4
.prettierignore Normal file
View file

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

13
.prettierrc.js Normal file
View file

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

6
.stackblitzrc Normal file
View file

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

495
.vscode/astrowind/config-schema.json vendored Normal file
View 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
View file

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

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

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

20
.vscode/settings.json vendored Normal file
View 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

Binary file not shown.

275
README.md
View file

@ -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 - ✅ **Production-ready** scores in **PageSpeed Insights** reports.
https://dragonschildstudios.com/ - ✅ 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/ ├── public/
│ └── favicon.svg │ ├── _headers
│ └── robots.txt
├── src/ ├── src/
│ ├── assets │ ├── assets/
│ │ ├── icons │ │ ├── favicons/
│ │ ├── images │ │ ├── images/
│ │ └── logos │ │ └── styles/
│ ├── components │ │ └── tailwind.css
│ ├── layouts │ ├── components/
│ ├── pages │ │ ├── blog/
│ └── styles │ │ ├── common/
└── package.json │ │ ├── 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: All commands are run from the root of the project, from a terminal:
| Command | Action | | Command | Action |
| :--------------------- | :------------------------------------------------- | | :-------------------- | :------------------------------------------------- |
| `npm install` | Installs dependencies | | `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` | | `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` | | `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying | | `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.

View file

@ -1,10 +1,81 @@
import { defineConfig } from 'astro/config'; import path from 'path';
import react from '@astrojs/react'; import { fileURLToPath } from 'url';
import tailwind from '@astrojs/tailwind';
import sitemap from "@astrojs/sitemap"; 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({ export default defineConfig({
site: 'https://dragonschildstudios.com', site: SITE.site,
integrations: [react(), tailwind(), sitemap()] 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
View 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"

View file

@ -1,26 +1,53 @@
{ {
"name": "dragonschildstudios", "name": "test-site",
"type": "module", "description": "AstroWind: A free template using Astro 4.0 and Tailwind CSS. Astro starter theme.",
"version": "0.0.1", "version": "1.0.0-beta.13",
"private": true,
"scripts": { "scripts": {
"dev": "astro dev", "dev": "astro dev",
"start": "astro dev", "start": "astro dev",
"build": "astro build", "build": "astro check && astro build",
"preview": "astro preview", "preview": "astro preview",
"astro": "astro" "astro": "astro",
"format": "prettier -w .",
"lint:eslint": "eslint . --ext .js,.ts,.astro"
}, },
"dependencies": { "dependencies": {
"@astrojs/sitemap": "^3.0.2", "@astrojs/check": "^0.4.1",
"@astrojs/tailwind": "^2.0.2", "@astrojs/rss": "^4.0.1",
"@fontsource/inter": "^4.5.14", "@astrojs/sitemap": "^3.0.4",
"astro": "^1.4.4", "@astrolib/analytics": "^0.5.0",
"framer-motion": "^7.6.1", "@astrolib/seo": "^1.0.0-beta.5",
"tailwindcss": "^3.1.8" "@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": { "devDependencies": {
"@astrojs/react": "^1.1.4", "@astrojs/mdx": "^2.0.3",
"react": "^18.2.0", "@astrojs/partytown": "^2.0.3",
"react-dom": "^18.2.0" "@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"
} }
} }

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

2
public/_headers Normal file
View file

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

View 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' }

View 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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View file

@ -1,4 +1,2 @@
User-agent: * User-agent: *
Allow: / Disallow:
Sitemap: https://dragonschildstudios.com/sitemap-index.xml

11
sandbox.config.json Normal file
View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View 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

View file

@ -1,10 +0,0 @@
import React from 'react';
export const BygulIcon = () => (
<img
src="/BygulIcon.png"
alt="DragonsChild Logo"
width="40"
height="40"
/>
);

View file

@ -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>
);

View file

@ -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>
);

View file

@ -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>
);

View file

@ -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>
);

View file

@ -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>
);

View file

@ -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>
);

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -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>
);

View file

@ -1,10 +0,0 @@
import React from 'react';
export const DragonsChildLogo = () => (
<img
src="/dragons.png"
alt="DragonsChild Logo"
width="40"
height="40"
/>
);

View file

@ -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>
);

View file

@ -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>
);

View file

@ -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>
);

File diff suppressed because one or more lines are too long

View file

@ -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>
);

View 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;
}

View file

@ -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>
);

View 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>

View file

@ -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>
);

View file

@ -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&rsquo;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>
);
};

View 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} />

View file

@ -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">
&copy; 2023 Dragons child studios.
</p>
</div>
</div>
</footer>
);
};

View file

@ -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>
);
};

View file

@ -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>
);

View 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>

View file

@ -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>
);
};

View file

@ -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>
);
};

View file

@ -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>
)}
</>
);
};

View file

@ -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>
</>
);
};

View file

@ -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>
);

View file

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

View file

@ -0,0 +1,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>

View file

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

View file

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

View file

@ -0,0 +1,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>

View 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>
)
}

View 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>

View 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>
</>
)
}

View 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>

View 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
}

View 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>

View 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>

View 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')} />

View 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} />}

View 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) }} />

View file

@ -0,0 +1,5 @@
---
import { SITE } from "~/utils/config";
---
{SITE.googleSiteVerificationId && <meta name="google-site-verification" content={SITE.googleSiteVerificationId} />}

View 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>

View file

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

View file

@ -0,0 +1,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>

View 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>
)
}

View file

@ -0,0 +1,7 @@
---
const { isDark = false } = Astro.props;
---
<div class:list={['absolute inset-0', { 'bg-dark dark:bg-transparent': isDark }]}>
<slot />
</div>

View 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>
)
}

View 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}/>

View 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>

View 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>
)
}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View 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>

View 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>

View 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 />
)
}

View 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 />
)
}

Some files were not shown because too many files have changed in this diff Show more