Add initial build
24
LICENSE.md
Normal file
@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the authors (onWidget)
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <https://unlicense.org>
|
73
astro.config.mjs
Normal file
@ -0,0 +1,73 @@
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
import tailwind from '@astrojs/tailwind';
|
||||
import sitemap from '@astrojs/sitemap';
|
||||
import image from '@astrojs/image';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import partytown from '@astrojs/partytown';
|
||||
import compress from 'astro-compress';
|
||||
|
||||
import { remarkReadingTime } from './src/utils/frontmatter.mjs';
|
||||
import { SITE } from './src/config.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const whenExternalScripts = (items = []) =>
|
||||
SITE.googleAnalyticsId ? (Array.isArray(items) ? items.map((item) => item()) : [items()]) : [];
|
||||
|
||||
export default defineConfig({
|
||||
site: SITE.origin,
|
||||
base: SITE.basePathname,
|
||||
trailingSlash: SITE.trailingSlash ? 'always' : 'never',
|
||||
|
||||
output: 'static',
|
||||
|
||||
integrations: [
|
||||
tailwind({
|
||||
config: {
|
||||
applyBaseStyles: false,
|
||||
},
|
||||
}),
|
||||
sitemap(),
|
||||
image({
|
||||
serviceEntryPoint: '@astrojs/image/sharp',
|
||||
}),
|
||||
mdx(),
|
||||
|
||||
...whenExternalScripts(() =>
|
||||
partytown({
|
||||
config: { forward: ['dataLayer.push'] },
|
||||
})
|
||||
),
|
||||
|
||||
compress({
|
||||
css: true,
|
||||
html: true,
|
||||
img: true,
|
||||
js: true,
|
||||
svg: true,
|
||||
|
||||
logger: 1,
|
||||
}),
|
||||
],
|
||||
|
||||
markdown: {
|
||||
remarkPlugins: [remarkReadingTime],
|
||||
extendDefaultPlugins: true,
|
||||
},
|
||||
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': path.resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
experimental: {
|
||||
contentCollections: true,
|
||||
},
|
||||
});
|
49
package.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@onwidget/astrowind",
|
||||
"description": "A template to make your website using Astro + Tailwind CSS.",
|
||||
"version": "0.9.6",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"format": "prettier -w .",
|
||||
"lint:eslint": "eslint . --ext .js,.ts,.astro",
|
||||
"subfont": "subfont -ir --no-fallbacks --silent --root dist"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/image": "^0.12.1",
|
||||
"@astrojs/mdx": "^0.14.0",
|
||||
"@astrojs/partytown": "^1.0.2",
|
||||
"@astrojs/rss": "^2.0.0",
|
||||
"@astrojs/sitemap": "^1.0.0",
|
||||
"@astrojs/tailwind": "^2.1.3",
|
||||
"@astrolib/analytics": "^0.2.4",
|
||||
"@astrolib/seo": "^0.2.1",
|
||||
"@fontsource/inter": "^4.5.14",
|
||||
"@tailwindcss/typography": "^0.5.8",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.0",
|
||||
"@typescript-eslint/parser": "^5.48.0",
|
||||
"astro": "^1.9.1",
|
||||
"astro-compress": "1.1.25",
|
||||
"astro-icon": "^0.8.0",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-plugin-astro": "^0.21.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
"limax": "^2.1.0",
|
||||
"mdast-util-to-string": "^3.1.0",
|
||||
"prettier": "^2.8.2",
|
||||
"prettier-plugin-astro": "^0.7.2",
|
||||
"reading-time": "^1.5.0",
|
||||
"sharp": "^0.31.3",
|
||||
"subfont": "^6.12.4",
|
||||
"svgo": "2.8.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"typescript": "^4.9.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || >=16.0.0"
|
||||
}
|
||||
}
|
2
public/_headers
Normal file
@ -0,0 +1,2 @@
|
||||
/assets/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
15
public/assets/images/pika-logo-text-dark.svg
Normal file
After Width: | Height: | Size: 24 KiB |
15
public/assets/images/pika-logo-text.svg
Normal file
After Width: | Height: | Size: 23 KiB |
59
public/favicon.svg
Normal file
@ -0,0 +1,59 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 720" width="64" height="64">
|
||||
<title>pika-logo-svg</title>
|
||||
<defs>
|
||||
<clipPath clipPathUnits="userSpaceOnUse" id="cp1">
|
||||
<path d="m0 0h720v720h-720z"/>
|
||||
</clipPath>
|
||||
<filter x="-50%" y="-50%" width="200%" height="200%" id="f1"> <feGaussianBlur stdDeviation="2.1"/> </filter>
|
||||
</defs>
|
||||
<style>
|
||||
.s0 { fill: #929291 }
|
||||
.s1 { fill: #b8b0ae }
|
||||
.s2 { fill: #ffc508 }
|
||||
.s3 { fill: #b19c9c;stroke: #b19c9c;stroke-width: 2.4 }
|
||||
.s4 { fill: #000000 }
|
||||
.s5 { fill: #ffffff }
|
||||
.s6 { filter: url(#f1);fill: #ec520a }
|
||||
.s7 { filter: url(#f1);fill: #ffffff }
|
||||
.s8 { fill: none;stroke: #000000;stroke-linecap: round;stroke-linejoin: round;stroke-width: 8.5 }
|
||||
</style>
|
||||
<g id="Clip-Path: g151" clip-path="url(#cp1)">
|
||||
<g id="g151">
|
||||
<g id="g11">
|
||||
<path id="path7" class="s0" d="m321.5 704.2c0 0-115.8-34.4-141.4-52.1-34.5-23.9-62.8-50.8-81.6-74.1 43.4-135.4 64.9-234.1 96.3-300.7 53.8-222.1 234.2-231.9 323 16.9 7.4 16.1 31.1 33.4 48.3 70 7 15 5.4 31.4 10.8 54.3 5 30.8 4.2 86.5 31 186.6-23.1 21.3-57.2 48.6-93.7 64.5-51.9 22.8-102.9 33.4-102.9 33.4"/>
|
||||
</g>
|
||||
<g id="layer1">
|
||||
</g>
|
||||
<g id="g26191">
|
||||
</g>
|
||||
<g id="g22">
|
||||
<path id="path20" class="s1" d="m192.1 281.7c-28.2 85.9-60.7 199-88.7 304.7-32.3-45.8-41.2-57.1-55.9-86.2 30.8-36.7 64.5-67.6 85.6-82.8 15-10.7 32.5-60.8 38.7-81.8 6.3-20.7 10.5-43.7 20.3-53.9z"/>
|
||||
</g>
|
||||
<g id="g33">
|
||||
<path id="path31" class="s1" d="m518.7 263.3c5.2 24 21.3 81.9 40.6 96.9 32 24.8 85 77.1 125.8 133.5-33.4 53.4 2.3 32.5-98.8 131.1-21.7-139.5-52.9-305-83.4-425.2 12.3 13.3 10.6 39.9 15.8 63.7z"/>
|
||||
</g>
|
||||
<g id="g55">
|
||||
<path id="path35" class="s2" d="m153.8 363.5c26.9-58.6-2.1-64 43.8-121.2 21.7-27 16.1-92.8 44.2-133.5 28.1-40.7 56.8-69.2 119.3-71.5 62.5-2.3 121.3 51.8 146.2 99.7 24.9 47.9 23.4 83.7 31.4 121.1 7.9 37.3 32.5 98.6 33.7 109.4-15.3 4.5-19.3 4.1-27.8 8.8-10.9 6.1-19.1 16-28.3 24.3-18.8 17.2-30.5 46-54.9 53.2-17.9 5.4-36.7-6.8-55-10.5-20.1-4-40.3-17.5-60.2-12.6-12.2 3-29.2 23.9-29.2 23.9-12.3 10.2-20.5-26-35.1-32.6-14.5-6.4-33.9 3.9-47.4-4.4-17.7-10.9-15.2-42.5-33.3-52.8-13.7-7.8-26.5 15.8-47.4-1.3z"/>
|
||||
</g>
|
||||
<g id="g91">
|
||||
<path id="path89" class="s3" d="m364.1 201c27.9-0.8 60 19.3 58.2 62.3-19.5 25.3-37.5 55.2-45.6 89.4-8.1 34.3-19.6 18.6-24.9 16.4-5.2-2.3-23.2-62-26.7-74.6-3.5-12.7-16.2-36.7-9.9-53.9 7.2-19.7 27.9-39.1 48.9-39.6z"/>
|
||||
</g>
|
||||
<g id="g105">
|
||||
<path id="path97" class="s4" d="m474.4 199.1c0.2-12.5 4.7-22.5 10-22.4 5.3 0 9.5 10.2 9.3 22.7-0.2 12.5-4.7 22.6-10 22.5-5.3-0.1-9.5-10.3-9.3-22.8z"/>
|
||||
<path id="path99" class="s5" d="m479.3 199.9c-0.3-4.2 1.7-7.9 4.3-8.2 2.7-0.3 5 2.8 5.2 7 0.3 4.2-1.7 7.8-4.3 8.1-2.6 0.3-5-2.8-5.2-6.9z"/>
|
||||
<path id="path101" class="s4" d="m255.2 191c0.6-12.8 4.8-23.1 9.3-22.9 4.6 0.2 7.8 10.8 7.2 23.7-0.6 12.9-4.8 23.2-9.4 23-4.5-0.3-7.7-10.9-7.1-23.8z"/>
|
||||
<path id="path103" class="s5" d="m259.3 191.5c0.1-4.3 2.1-7.8 4.4-7.9 2.3 0 4 3.5 3.9 7.8-0.2 4.3-2.1 7.8-4.4 7.8-2.3 0-4-3.4-3.9-7.7z"/>
|
||||
</g>
|
||||
<g id="g118">
|
||||
<path id="ellipse62" class="s6" d="m482.5 337.8c-22.1 0-39.9-18.9-39.9-42.3 0-23.4 17.8-42.3 39.9-42.3 22.1 0 39.9 18.9 39.9 42.3 0 23.4-17.8 42.3-39.9 42.3z"/>
|
||||
<path id="ellipse116" class="s6" d="m257.6 328.2c-23.4 0-42.3-18.3-42.3-40.9 0-22.6 18.9-40.9 42.3-40.9 23.4 0 42.3 18.3 42.3 40.9 0 22.6-18.9 40.9-42.3 40.9z"/>
|
||||
</g>
|
||||
<g id="g139">
|
||||
<path id="path137" class="s7" d="m540.1 560.5c0 6.1-1.1 12-2.8 17.6-0.9 3.2-3 7.9-3 7.9-16.3 35.8-61 59-61 59 0 0-44.7-23.2-61-59-1.4-2.8-3-7.9-3-7.9-1.8-5.6-2.8-11.5-2.8-17.6 0-22.8 17.3-41.3 38.6-41.3 11.1 0 21.2 4.5 28.2 12.6 7-8.1 17.1-12.6 28.3-12.6 21.2 0 38.5 18.5 38.5 41.3z"/>
|
||||
</g>
|
||||
<g id="layer2">
|
||||
<path id="path901" class="s8" d="m363.8 705.1c-191.1 0-345.5-154.4-345.5-345.5 0-191.1 154.4-345.6 345.5-345.6 191.1 0 345.6 154.5 345.6 345.6 0 191.1-154.5 345.5-345.6 345.5z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
2
public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
11
sandbox.config.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"infiniteLoopProtection": true,
|
||||
"hardReloadOnChange": false,
|
||||
"view": "browser",
|
||||
"template": "node",
|
||||
"container": {
|
||||
"port": 3000,
|
||||
"startScript": "start",
|
||||
"node": "14"
|
||||
}
|
||||
}
|
BIN
src/assets/images/caos.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
src/assets/images/colors.jpg
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
src/assets/images/creativity.jpg
Normal file
After Width: | Height: | Size: 276 KiB |
BIN
src/assets/images/default.png
Normal file
After Width: | Height: | Size: 754 KiB |
BIN
src/assets/images/do-more.jpg
Normal file
After Width: | Height: | Size: 136 KiB |
BIN
src/assets/images/hero.png
Normal file
After Width: | Height: | Size: 4.4 MiB |
15
src/assets/images/pika-logo-text-dark.svg
Normal file
After Width: | Height: | Size: 24 KiB |
15
src/assets/images/pika-logo-text.svg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/images/stickers.jpg
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
src/assets/images/tools.jpg
Normal file
After Width: | Height: | Size: 91 KiB |
BIN
src/assets/images/vintage.jpg
Normal file
After Width: | Height: | Size: 49 KiB |
56
src/assets/styles/base.css
Normal file
@ -0,0 +1,56 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply inline-flex items-center justify-center rounded-md shadow-md border-gray-400 border bg-transparent font-medium text-center text-base text-gray-700 leading-snug transition py-3.5 px-6 md:px-7 ease-in duration-200 focus:ring-blue-500 focus:ring-offset-blue-200 focus:ring-2 focus:ring-offset-2 hover:bg-gray-100 hover:border-gray-600 dark:text-slate-300 dark:border-slate-500 dark:hover:bg-slate-800 dark:hover:border-slate-800;
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
@apply border-none shadow-none text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply font-semibold bg-primary-800 text-white border-primary-800 hover:bg-primary-900 hover:border-primary-900 hover:text-white dark:text-white dark:bg-primary-700 dark:border-primary-700 dark:hover:border-primary-900 dark:hover:bg-primary-900;
|
||||
}
|
||||
}
|
||||
|
||||
#header.scroll {
|
||||
@apply shadow-md md:shadow-lg bg-white md:bg-white/90 md:backdrop-blur-sm dark:bg-slate-900 dark:md:bg-slate-900/90;
|
||||
}
|
||||
|
||||
.dropdown:hover .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
[astro-icon].icon-light > * {
|
||||
stroke-width: 1.2;
|
||||
}
|
||||
|
||||
[astro-icon].icon-bold > * {
|
||||
stroke-width: 2.4;
|
||||
}
|
||||
|
||||
[data-aw-toggle-menu] path {
|
||||
@apply transition;
|
||||
}
|
||||
[data-aw-toggle-menu].expanded g > path:first-child {
|
||||
@apply -rotate-45 translate-y-[15px] translate-x-[-3px];
|
||||
}
|
||||
|
||||
[data-aw-toggle-menu].expanded g > path:last-child {
|
||||
@apply rotate-45 translate-y-[-8px] translate-x-[14px];
|
||||
}
|
||||
|
||||
.dark .pikalogo {
|
||||
background-image: url("images/pika-logo-text-dark.svg");
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.pikalogo {
|
||||
background-image: url("images/pika-logo-text.svg");
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
}
|
62
src/components/Card.astro
Normal file
@ -0,0 +1,62 @@
|
||||
---
|
||||
export interface Props {
|
||||
title: string;
|
||||
body: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
const { href, title, body } = Astro.props;
|
||||
---
|
||||
|
||||
<li class="link-card">
|
||||
<a href={href}>
|
||||
<h2>
|
||||
{title}
|
||||
<span>→</span>
|
||||
</h2>
|
||||
<p>
|
||||
{body}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
<style>
|
||||
.link-card {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding: 0.15rem;
|
||||
background-color: white;
|
||||
background-image: var(--accent-gradient);
|
||||
background-size: 400%;
|
||||
border-radius: 0.5rem;
|
||||
background-position: 100%;
|
||||
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.link-card > a {
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
line-height: 1.4;
|
||||
padding: 1rem 1.3rem;
|
||||
border-radius: 0.35rem;
|
||||
color: #111;
|
||||
background-color: white;
|
||||
opacity: 0.8;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
p {
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0;
|
||||
color: #444;
|
||||
}
|
||||
.link-card:is(:hover, :focus-within) {
|
||||
background-position: 0;
|
||||
}
|
||||
.link-card:is(:hover, :focus-within) h2 {
|
||||
color: rgb(var(--accent));
|
||||
}
|
||||
</style>
|
119
src/components/common/BasicScripts.astro
Normal file
@ -0,0 +1,119 @@
|
||||
---
|
||||
import { SITE } from '~/config.mjs';
|
||||
---
|
||||
|
||||
<script is:inline define:vars={{ defaultTheme: SITE.defaultTheme }}>
|
||||
function applyTheme(theme) {
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
if ((defaultTheme && defaultTheme.endsWith(':only')) || (!localStorage.theme && defaultTheme !== 'system')) {
|
||||
applyTheme(defaultTheme.replace(':only', ''));
|
||||
} else if (
|
||||
localStorage.theme === 'dark' ||
|
||||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
applyTheme('dark');
|
||||
} else {
|
||||
applyTheme('light');
|
||||
}
|
||||
|
||||
function attachEvent(selector, event, fn) {
|
||||
const matches = typeof selector === 'string' ? document.querySelectorAll(selector) : selector;
|
||||
if (matches && matches.length) {
|
||||
matches.forEach((elem) => {
|
||||
elem.addEventListener(event, (e) => fn(e, elem), false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
let lastKnownScrollPosition = window.scrollY;
|
||||
let ticking = true;
|
||||
|
||||
attachEvent('[data-aw-toggle-menu]', 'click', function (_, elem) {
|
||||
elem.classList.toggle('expanded');
|
||||
document.body.classList.toggle('overflow-hidden');
|
||||
document.getElementById('header')?.classList.toggle('h-screen');
|
||||
document.querySelector('#header nav')?.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
attachEvent('[data-aw-toggle-color-scheme]', 'click', function () {
|
||||
if (defaultTheme.endsWith(':only')) {
|
||||
return;
|
||||
}
|
||||
document.documentElement.classList.toggle('dark');
|
||||
localStorage.theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
|
||||
});
|
||||
|
||||
attachEvent('[data-aw-social-share]', 'click', function (_, elem) {
|
||||
const network = elem.getAttribute('data-aw-social-share');
|
||||
const url = encodeURIComponent(elem.getAttribute('data-aw-url'));
|
||||
const text = encodeURIComponent(elem.getAttribute('data-aw-text'));
|
||||
|
||||
let href;
|
||||
switch (network) {
|
||||
case 'facebook':
|
||||
href = `https://www.facebook.com/sharer.php?u=${url}`;
|
||||
break;
|
||||
case 'twitter':
|
||||
href = `https://twitter.com/intent/tweet?url=${url}&text=${text}`;
|
||||
break;
|
||||
case 'linkedin':
|
||||
href = `https://www.linkedin.com/shareArticle?mini=true&url=${url}&title=${text}`;
|
||||
break;
|
||||
case 'whatsapp':
|
||||
href = `https://wa.me/?text=${text}%20${url}`;
|
||||
break;
|
||||
case 'mail':
|
||||
href = `mailto:?subject=%22${text}%22&body=${text}%20${url}`;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
const newlink = document.createElement('a');
|
||||
newlink.target = '_blank';
|
||||
newlink.href = href;
|
||||
newlink.click();
|
||||
});
|
||||
|
||||
function appyHeaderStylesOnScroll() {
|
||||
const header = document.getElementById('header');
|
||||
if (lastKnownScrollPosition > 60 && !header.classList.contains('scroll')) {
|
||||
document.getElementById('header').classList.add('scroll');
|
||||
} else if (lastKnownScrollPosition <= 60 && header.classList.contains('scroll')) {
|
||||
document.getElementById('header').classList.remove('scroll');
|
||||
}
|
||||
ticking = false;
|
||||
}
|
||||
appyHeaderStylesOnScroll();
|
||||
|
||||
attachEvent([document], 'scroll', function () {
|
||||
lastKnownScrollPosition = window.scrollY;
|
||||
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
appyHeaderStylesOnScroll();
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
window.onpageshow = function () {
|
||||
document.documentElement.classList.add('motion-safe:scroll-smooth');
|
||||
const elem = document.querySelector('[data-aw-toggle-menu]');
|
||||
if (elem) {
|
||||
elem.classList.remove('expanded');
|
||||
}
|
||||
document.body.classList.remove('overflow-hidden');
|
||||
document.getElementById('header')?.classList.remove('h-screen');
|
||||
document.querySelector('#header nav')?.classList.add('hidden');
|
||||
};
|
||||
</script>
|
5
src/components/common/Fonts.astro
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
import '@fontsource/inter/variable.css';
|
||||
---
|
||||
|
||||
<!-- Or Google Fonts -->
|
6
src/components/common/Logo.astro
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
---
|
||||
|
||||
<span class="self-center w-60 h-10 pikalogo">
|
||||
</span
|
||||
>
|
97
src/components/common/MetaTags.astro
Normal file
@ -0,0 +1,97 @@
|
||||
---
|
||||
import { AstroSeo } from '@astrolib/seo';
|
||||
import { GoogleAnalytics } from '@astrolib/analytics';
|
||||
import { getImage } from '@astrojs/image';
|
||||
|
||||
import { SITE } from '~/config.mjs';
|
||||
import { MetaSEO } from '~/types';
|
||||
import { getPermalink, getCanonical } from '~/utils/permalinks';
|
||||
import { getRelativeUrlByFilePath } from '~/utils/directories';
|
||||
|
||||
import Fonts from '~/components/common/Fonts.astro';
|
||||
import SplitbeeAnalytics from './SplitbeeAnalytics.astro';
|
||||
|
||||
export interface Props extends MetaSEO {
|
||||
dontUseTitleTemplate?: boolean;
|
||||
}
|
||||
|
||||
const defaultImage = SITE.defaultImage
|
||||
? (
|
||||
await getImage({
|
||||
src: SITE.defaultImage,
|
||||
alt: 'Default image',
|
||||
width: 1200,
|
||||
height: 628,
|
||||
})
|
||||
).src
|
||||
: '';
|
||||
|
||||
const {
|
||||
title = SITE.name,
|
||||
description = '',
|
||||
image: _image = defaultImage,
|
||||
|
||||
canonical = getCanonical(String(Astro.url.pathname)),
|
||||
noindex = false,
|
||||
nofollow = false,
|
||||
|
||||
ogTitle = title,
|
||||
ogType = 'website',
|
||||
|
||||
dontUseTitleTemplate = false,
|
||||
} = Astro.props;
|
||||
|
||||
const image =
|
||||
typeof _image === 'string'
|
||||
? new URL(_image, Astro.site)
|
||||
: _image && typeof _image['src'] !== 'undefined'
|
||||
? // @ts-ignore
|
||||
new URL(getRelativeUrlByFilePath(_image.src), Astro.site)
|
||||
: null;
|
||||
---
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
<AstroSeo
|
||||
title={title}
|
||||
titleTemplate={dontUseTitleTemplate ? '%s' : `%s — ${SITE.name}`}
|
||||
description={description}
|
||||
canonical={String(canonical)}
|
||||
noindex={noindex}
|
||||
nofollow={nofollow}
|
||||
openGraph={{
|
||||
url: String(canonical),
|
||||
title: ogTitle,
|
||||
description: description,
|
||||
type: ogType,
|
||||
images: image
|
||||
? [
|
||||
{
|
||||
url: image.toString(),
|
||||
alt: ogTitle,
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
// site_name: 'SiteName',
|
||||
}}
|
||||
twitter={{
|
||||
// handle: '@handle',
|
||||
// site: '@site',
|
||||
cardType: image ? 'summary_large_image' : undefined,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Fonts />
|
||||
|
||||
<!-- Google Site Verification -->
|
||||
{SITE.googleSiteVerificationId && <meta name="google-site-verification" content={SITE.googleSiteVerificationId} />}
|
||||
|
||||
<!-- Google Analytics -->
|
||||
{SITE.googleAnalyticsId && <GoogleAnalytics id={String(SITE.googleAnalyticsId)} partytown={true} />}
|
||||
|
||||
{SITE.splitbeeAnalytics?.enabled && <SplitbeeAnalytics {...SITE.splitbeeAnalytics} />}
|
||||
|
||||
<link rel="shortcut icon" href={getPermalink('/favicon.ico')} />
|
||||
<link rel="icon" type="image/svg+xml" href={getPermalink('/favicon.svg')} />
|
||||
<link rel="mask-icon" href={getPermalink('/favicon.svg')} color="#8D46E7" />
|
34
src/components/common/Pagination.astro
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
|
||||
export interface Props {
|
||||
prevUrl: string;
|
||||
nextUrl: string;
|
||||
prevText?: string;
|
||||
nextText?: string;
|
||||
}
|
||||
|
||||
const { prevUrl, nextUrl, prevText = 'Newer posts', nextText = 'Older posts' } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
(prevUrl || nextUrl) && (
|
||||
<div class="container flex">
|
||||
<div class="flex flex-row mx-auto container justify-between">
|
||||
<a href={getPermalink(prevUrl)} class={`btn btn-ghost px-3 mr-2 ${!prevUrl ? 'invisible' : ''}`}>
|
||||
<div class="flex flex-row align-middle">
|
||||
<Icon name="tabler:chevron-left" class="w-6 h-6" />
|
||||
<p class="ml-2">{prevText}</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href={getPermalink(nextUrl)} class={`btn btn-ghost px-3 ${!nextUrl ? 'invisible' : ''}`}>
|
||||
<div class="flex flex-row align-middle">
|
||||
<span class="mr-2">{nextText}</span>
|
||||
<Icon name="tabler:chevron-right" class="w-6 h-6" />
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
30
src/components/common/SocialShare.astro
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
export interface Props {
|
||||
text: string;
|
||||
url: string | URL;
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const { text, url, class: className = 'inline-block' } = Astro.props;
|
||||
---
|
||||
|
||||
<div class={className}>
|
||||
<span class="align-super font-bold dark:text-slate-400">Share:</span>
|
||||
<button class="ml-2" title="Twitter Share" data-aw-social-share="twitter" data-aw-url={url} data-aw-text={text}
|
||||
><Icon name="logos:twitter" class="w-6 h-6" />
|
||||
</button>
|
||||
<button class="ml-2" title="Facebook Share" data-aw-social-share="facebook" data-aw-url={url}
|
||||
><Icon name="logos:facebook" class="w-6 h-6" />
|
||||
</button>
|
||||
<button class="ml-2" title="Linkedin Share" data-aw-social-share="linkedin" data-aw-url={url} data-aw-text={text}
|
||||
><Icon name="logos:linkedin-icon" class="w-6 h-6" />
|
||||
</button>
|
||||
<button class="ml-2" title="Whatsapp Share" data-aw-social-share="whatsapp" data-aw-url={url} data-aw-text={text}
|
||||
><Icon name="logos:whatsapp" class="w-6 h-6" />
|
||||
</button>
|
||||
<button class="ml-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" />
|
||||
</button>
|
||||
</div>
|
6
src/components/common/SplitbeeAnalytics.astro
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
const { doNotTrack = true, noCookieMode = false, url = 'https://cdn.splitbee.io/sb.js' } = Astro.props;
|
||||
---
|
||||
|
||||
<!-- Splitbee Analytics -->
|
||||
<script data-respect-dnt={doNotTrack} data-no-cookie={noCookieMode} async src={url}></script>
|
29
src/components/common/Tags.astro
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
|
||||
import type { Post } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
tags: Post['tags'];
|
||||
class?: string;
|
||||
}
|
||||
|
||||
const { tags, class: className = 'text-sm' } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
tags && Array.isArray(tags) && (
|
||||
<ul class={className}>
|
||||
{tags.map((tag) => (
|
||||
<li class="bg-gray-100 dark:bg-slate-700 inline-block mr-2 mb-2 py-0.5 px-2 lowercase font-medium">
|
||||
<a
|
||||
href={getPermalink(tag, 'tag')}
|
||||
class="text-gray-600 dark:text-slate-300 hover:text-primary-800 dark:hover:text-gray-200"
|
||||
>
|
||||
{tag}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
22
src/components/common/ToggleMenu.astro
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
export interface Props {
|
||||
label?: string;
|
||||
class?: string;
|
||||
iconClass?: string;
|
||||
iconName?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
label = 'Toggle Menu',
|
||||
class:
|
||||
className = 'ml-1.5 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center transition',
|
||||
iconClass = 'w-6 h-6',
|
||||
iconName = 'tabler:menu',
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<button type="button" class={className} aria-label={label} data-aw-toggle-menu>
|
||||
<Icon name={iconName} class={iconClass} optimize={false} />
|
||||
</button>
|
22
src/components/common/ToggleTheme.astro
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
export interface Props {
|
||||
label?: string;
|
||||
class?: string;
|
||||
iconClass?: string;
|
||||
iconName?: string;
|
||||
}
|
||||
|
||||
const {
|
||||
label = 'Toggle between Dark and Light mode',
|
||||
class:
|
||||
className = 'text-gray-500 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;
|
||||
---
|
||||
|
||||
<button type="button" class={className} aria-label={label} data-aw-toggle-color-scheme>
|
||||
<Icon name={iconName} class={iconClass} />
|
||||
</button>
|
28
src/components/widgets/Announcement.astro
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
import { getPermalink } from '~/utils/permalinks';
|
||||
---
|
||||
|
||||
<div
|
||||
class="hidden md:block bg-primary-900 dark:bg-slate-800 dark:border-slate-800 dark:text-slate-400 border-b border-primary-900 text-sm px-3 py-2 text-gray-200 overflow-hidden whitespace-nowrap text-ellipsis"
|
||||
>
|
||||
<span class="text-xs py-0.5 px-1 bg-primary-800 dark:bg-slate-700 dark:text-slate-300 font-semibold">NEW</span>
|
||||
<a
|
||||
href={getPermalink('useful-resources-to-create-websites', 'post')}
|
||||
class="hover:underline text-gray-200 dark:text-slate-400"
|
||||
>Useful tools and resources to create a professional website »</a
|
||||
>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="float-right"
|
||||
title="If you like AstroWind, give us a star."
|
||||
href="https://github.com/onwidget/astrowind"
|
||||
>
|
||||
<img
|
||||
src="https://img.shields.io/github/stars/onwidget/astrowind.svg?style=social&label=Stars&maxAge=86400"
|
||||
alt="Follow @onWidget"
|
||||
width="84"
|
||||
height="20"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
57
src/components/widgets/CallToAction.astro
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
interface CallToAction {
|
||||
text: string;
|
||||
href: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
callToAction?: string | CallToAction;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
callToAction = await Astro.slots.render('callToAction'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class="relative">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="py-12 md:py-20">
|
||||
<div
|
||||
class="max-w-3xl mx-auto text-center p-6 rounded-md shadow-xl dark:shadow-none dark:border dark:border-slate-600"
|
||||
>
|
||||
{
|
||||
title && (
|
||||
<h2
|
||||
class="text-4xl md:text-4xl font-bold leading-tighter tracking-tighter mb-4 font-heading"
|
||||
set:html={title}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{subtitle && <p class="text-xl text-gray-600 dark:text-slate-400" set:html={subtitle} />}
|
||||
{
|
||||
typeof callToAction === 'string' ? (
|
||||
<Fragment set:html={callToAction} />
|
||||
) : (
|
||||
callToAction &&
|
||||
callToAction.text &&
|
||||
callToAction.href && (
|
||||
<div class="mt-6 max-w-xs mx-auto">
|
||||
<a class="btn btn-primary w-full sm:w-auto" href={callToAction.href} target="_blank" rel="noopener">
|
||||
{callToAction.icon && <Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />}
|
||||
{callToAction.text}
|
||||
</a>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
107
src/components/widgets/Content.astro
Normal file
@ -0,0 +1,107 @@
|
||||
---
|
||||
import Icon from 'astro-icon';
|
||||
import { Picture } from '@astrojs/image/components';
|
||||
|
||||
interface Item {
|
||||
title: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
highlight?: string;
|
||||
content?: string;
|
||||
items?: Array<Item>;
|
||||
image?: string | any; // TODO: find HTMLElementProps
|
||||
isReversed?: boolean;
|
||||
isAfterContent?: boolean;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
highlight,
|
||||
content = await Astro.slots.render('content'),
|
||||
items = [],
|
||||
image = await Astro.slots.render('image'),
|
||||
isReversed = false,
|
||||
isAfterContent = false,
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class={`bg-primary-50 dark:bg-slate-800 py-16 md:py-20 ${isAfterContent ? 'pt-0 md:pt-0' : ''}`}>
|
||||
<div class="max-w-xl sm:mx-auto lg:max-w-2xl">
|
||||
{
|
||||
(title || subtitle || highlight) && (
|
||||
<div class="mb-10 md:mx-auto text-center md:mb-12 max-w-3xl">
|
||||
{highlight && (
|
||||
<p
|
||||
class="text-base text-primary-800 dark:text-primary-200 font-semibold tracking-wide uppercase"
|
||||
set:html={highlight}
|
||||
/>
|
||||
)}
|
||||
{title && (
|
||||
<h2
|
||||
class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading"
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
|
||||
{subtitle && (
|
||||
<p class="max-w-3xl mx-auto sm:text-center text-xl text-gray-600 dark:text-slate-400" set:html={subtitle} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div class="mx-auto max-w-6xl p-4 md:px-8">
|
||||
<div class={`md:flex ${isReversed ? 'md:flex-row-reverse' : ''} md:gap-16`}>
|
||||
<div class="md:basis-1/2 self-center">
|
||||
{content && <div class="mb-12 text-lg text-gray-600 dark:text-slate-400" set:html={content} />}
|
||||
|
||||
{
|
||||
items && (
|
||||
<div class="space-y-8">
|
||||
{items.map(({ title: title2, description, icon }) => (
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<div class="flex h-7 w-7 items-center justify-center rounded-full bg-blue-800 text-gray-50">
|
||||
<Icon name={icon ? icon : 'tabler:check'} class="w-5 h-5" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
{title2 && <h3 class="text-lg font-medium leading-6 text-gray-900 dark:text-white">{title2}</h3>}
|
||||
{description && <p class="mt-2 text-gray-600 dark:text-slate-400" set:html={description} />}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div aria-hidden="true" class="mt-10 md:mt-0 md:basis-1/2">
|
||||
{
|
||||
image && (
|
||||
<div class="relative m-auto max-w-4xl">
|
||||
{typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Picture
|
||||
class="mx-auto w-full rounded-lg bg-gray-500 shadow-lg"
|
||||
width={500}
|
||||
height={500}
|
||||
widths={[400, 768]}
|
||||
sizes="(max-width: 768px) 100vw, 432px"
|
||||
aspectRatio="500:500"
|
||||
{...image}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
66
src/components/widgets/FAQs.astro
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
interface Item {
|
||||
question: string;
|
||||
answer: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
highlight?: string;
|
||||
items: Array<Array<Item>>;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
highlight,
|
||||
items = [],
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<div class="px-4 py-16 mx-auto max-w-6xl lg:py-20">
|
||||
<div class="max-w-xl sm:mx-auto lg:max-w-2xl">
|
||||
{
|
||||
(title || subtitle || highlight) && (
|
||||
<div class="max-w-xl mb-10 md:mx-auto md:text-center lg:max-w-2xl md:mb-12">
|
||||
{highlight && (
|
||||
<p
|
||||
class="text-base text-primary-800 dark:text-primary-200 font-semibold tracking-wide uppercase"
|
||||
set:html={highlight}
|
||||
/>
|
||||
)}
|
||||
{title && (
|
||||
<h2
|
||||
class="max-w-lg mb-4 text-3xl font-bold leading-none md:tracking-tight sm:text-4xl md:mx-auto font-heading"
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
{subtitle && <p class="max-w-3xl mx-auto text-xl text-gray-600 dark:text-slate-400" set:html={subtitle} />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div class="max-w-screen-xl sm:mx-auto">
|
||||
<div class="grid grid-cols-1 gap-x-8 gap-y-8 lg:gap-x-16 md:grid-cols-2">
|
||||
{
|
||||
items &&
|
||||
items.map((subitems) => (
|
||||
<div class="space-y-8">
|
||||
{subitems.map(({ question, answer }) => (
|
||||
<div>
|
||||
<h3 class="mb-4 text-xl font-bold">
|
||||
<Icon name="tabler:arrow-down-right" class="w-7 h-7 text-primary-800 inline-block" />
|
||||
{question}
|
||||
</h3>
|
||||
{answer && <div class="text-gray-700 dark:text-gray-400" set:html={answer} />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
71
src/components/widgets/Features.astro
Normal file
@ -0,0 +1,71 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
interface Item {
|
||||
title: string;
|
||||
description: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
highlight?: string;
|
||||
items: Array<Array<Item>>;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
highlight,
|
||||
items = [],
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class="scroll-mt-16" id="features">
|
||||
<div class="px-4 py-16 mx-auto max-w-6xl lg:px-8 lg:py-20">
|
||||
{
|
||||
(title || subtitle || highlight) && (
|
||||
<div class="mb-10 md:mx-auto text-center md:mb-12 max-w-3xl">
|
||||
{highlight && (
|
||||
<p
|
||||
class="text-base text-primary-800 dark:text-primary-200 font-semibold tracking-wide uppercase"
|
||||
set:html={highlight}
|
||||
/>
|
||||
)}
|
||||
{title && (
|
||||
<h2
|
||||
class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading"
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
|
||||
{subtitle && (
|
||||
<p class="max-w-3xl mx-auto sm:text-center text-xl text-gray-600 dark:text-slate-400" set:html={subtitle} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div class="grid mx-auto space-y-6 md:grid-cols-2 md:space-y-0">
|
||||
{
|
||||
items.map((subitems) => (
|
||||
<div class="space-y-8 sm:px-8">
|
||||
{subitems.map(({ title, description, icon }) => (
|
||||
<div class="flex flex-row max-w-md">
|
||||
<div class="mb-4 mr-4">
|
||||
<div class="flex items-center justify-center w-12 h-12 rounded-full bg-primary-800 dark:bg-primary-700">
|
||||
{icon && <Icon name={icon} class="w-6 h-6 text-white icon-light" />}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="mb-3 text-xl font-bold">{title}</h3>
|
||||
<p class="text-gray-600 dark:text-slate-400" set:html={description} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
69
src/components/widgets/Features2.astro
Normal file
@ -0,0 +1,69 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
interface Item {
|
||||
title?: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
highlight?: string;
|
||||
items: Array<Item>;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
highlight,
|
||||
items = [],
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class="relative">
|
||||
<div class="absolute inset-0 bg-primary-50 dark:bg-slate-800 pointer-events-none mb-32" aria-hidden="true"></div>
|
||||
<div class="relative max-w-6xl mx-auto px-4 sm:px-6 -mb-12">
|
||||
<div class="py-4 pt-8 sm:py-6 lg:py-8 lg:pt-12">
|
||||
{
|
||||
(title || subtitle || highlight) && (
|
||||
<div class="mb-8 md:mx-auto text-center max-w-3xl">
|
||||
{highlight && (
|
||||
<p
|
||||
class="text-base text-primary-800 dark:text-primary-200 font-semibold tracking-wide uppercase"
|
||||
set:html={highlight}
|
||||
/>
|
||||
)}
|
||||
{title && (
|
||||
<h2
|
||||
class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading"
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
|
||||
{subtitle && (
|
||||
<p
|
||||
class="max-w-3xl mx-auto sm:text-center text-xl text-gray-600 dark:text-slate-400"
|
||||
set:html={subtitle}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div class={`grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 my-12 dark:text-white items-stretch`}>
|
||||
{
|
||||
items.map(({ title, description, icon }) => (
|
||||
<div class="relative flex flex-col p-6 bg-white dark:bg-slate-900 rounded shadow-lg hover:shadow-md transition border border-transparent dark:border-slate-800">
|
||||
<div class="flex items-center">
|
||||
<Icon name={icon} class="w-10 h-10" />
|
||||
<div class="ml-4 text-xl font-bold">{title}</div>
|
||||
</div>
|
||||
{description && <p class="text-gray-500 dark:text-gray-400 text-md mt-4" set:html={description} />}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
70
src/components/widgets/Footer.astro
Normal file
@ -0,0 +1,70 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
import { getHomePermalink, getPermalink } from '~/utils/permalinks';
|
||||
|
||||
const links = [
|
||||
{
|
||||
title: 'PikaOS',
|
||||
items: [
|
||||
{ title: 'Features', href: '#features' },
|
||||
{ title: 'Download', href: '#download' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const social = [
|
||||
{ label: 'Discord', icon: 'tabler:brand-discord', href: 'https://discord.gg/aGR4YFCB' },
|
||||
{ label: 'Github', icon: 'tabler:brand-github', href: 'https://github.com/PikaOS-Linux' },
|
||||
];
|
||||
---
|
||||
|
||||
<footer class="border-t border-gray-200 dark:border-slate-800">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="grid grid-cols-12 gap-4 gap-y-8 sm:gap-8 py-8 md:py-12">
|
||||
{
|
||||
links.map(({ title, items }) => (
|
||||
<div class="col-span-6 md:col-span-3 lg:col-span-2">
|
||||
<div class="text-gray-800 dark:text-gray-300 font-medium mb-2">{title}</div>
|
||||
{items && Array.isArray(items) && items.length > 0 && (
|
||||
<ul class="text-sm">
|
||||
{items.map(({ title, href }) => (
|
||||
<li class="mb-2">
|
||||
<a
|
||||
class="text-gray-600 hover:text-gray-700 hover:underline dark:text-gray-400 transition duration-150 ease-in-out"
|
||||
href={href}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div class="md:flex md:items-center md:justify-between py-6 md:py-8">
|
||||
<ul class="flex mb-4 md:order-1 -ml-2 md:ml-4 md:mb-0">
|
||||
{
|
||||
social.map(({ label, href, icon }) => (
|
||||
<li>
|
||||
<a
|
||||
class="text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 inline-flex items-center"
|
||||
aria-label={label}
|
||||
href={href}
|
||||
>
|
||||
<Icon name={icon} class="w-5 h-5" />
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
|
||||
<div class="text-sm text-gray-700 mr-4 dark:text-slate-400">
|
||||
|
||||
Made by <a class="text-blue-600 hover:underline dark:text-gray-200" href="https://github.com/PikaOS-Linux"> the PikaOS team</a> ·
|
||||
All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
91
src/components/widgets/Header.astro
Normal file
@ -0,0 +1,91 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
import Logo from '~/components/common/Logo.astro';
|
||||
import ToggleTheme from '~/components/common/ToggleTheme.astro';
|
||||
import ToggleMenu from '~/components/common/ToggleMenu.astro';
|
||||
|
||||
import { getHomePermalink} from '~/utils/permalinks';
|
||||
|
||||
const links = [
|
||||
{
|
||||
text: 'Features',
|
||||
href: '#features',
|
||||
},
|
||||
{
|
||||
text: 'Download',
|
||||
href: '#download',
|
||||
},
|
||||
// {
|
||||
// text: 'About us',
|
||||
// href: '#about',
|
||||
// },
|
||||
];
|
||||
---
|
||||
|
||||
<header class="sticky top-0 z-40 flex-none mx-auto w-full transition-all ease-in duration-100" id="header">
|
||||
<div class="py-3 px-3 md:py-3.5 md:px-4 mx-auto w-full md:flex md:justify-between max-w-6xl">
|
||||
<div class="flex justify-between">
|
||||
<a class="flex items-center" href={getHomePermalink()}>
|
||||
<Logo />
|
||||
</a>
|
||||
<div class="flex items-center md:hidden">
|
||||
<ToggleTheme />
|
||||
<ToggleMenu />
|
||||
</div>
|
||||
</div>
|
||||
<nav
|
||||
class="items-center w-full md:w-auto hidden md:flex text-gray-600 dark:text-slate-200 h-[calc(100vh-72px)] md:h-auto overflow-y-auto md:overflow-visible"
|
||||
aria-label="Main navigation"
|
||||
>
|
||||
<ul class="flex flex-col pt-8 md:pt-0 md:flex-row md:self-center w-full md:w-auto text-xl md:text-base">
|
||||
{
|
||||
links.map(({ text, href, links }) => (
|
||||
<li class={links?.length ? 'dropdown' : ''}>
|
||||
{links?.length ? (
|
||||
<>
|
||||
<button class="font-medium hover:text-gray-900 dark:hover:text-white px-4 py-3 flex items-center transition duration-150 ease-in-out">
|
||||
{text} <Icon name="tabler:chevron-down" class="w-3.5 h-3.5 ml-0.5 hidden md:inline" />
|
||||
</button>
|
||||
<ul class="dropdown-menu rounded md:absolute pl-4 md:pl-0 md:hidden font-medium md:bg-white md:min-w-[200px] dark:md:bg-slate-800 drop-shadow-xl">
|
||||
{links.map(({ text: text2, href: href2 }) => (
|
||||
<li>
|
||||
<a
|
||||
class="first:rounded-t last:rounded-b md:hover:bg-gray-100 dark:hover:bg-gray-700 py-2 px-5 block whitespace-no-wrap"
|
||||
href={href2}
|
||||
>
|
||||
{text2}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
) : (
|
||||
<a
|
||||
class="font-medium hover:text-gray-900 dark:hover:text-white px-4 py-3 flex items-center transition duration-150 ease-in-out"
|
||||
href={href}
|
||||
>
|
||||
{text}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
))
|
||||
}
|
||||
<li class="md:hidden">
|
||||
<a
|
||||
class="font-bold hover:text-gray-900 dark:hover:text-white px-4 py-3 flex items-center transition duration-150 ease-in-out"
|
||||
href="https://github.com/onwidget/astrowind">Github</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="md:self-center flex items-center md:mb-0 ml-4">
|
||||
<div class="hidden items-center md:flex">
|
||||
<ToggleTheme iconClass="w-5 h-5" />
|
||||
<a
|
||||
class="btn w-full ml-3 py-2.5 px-5 font-semibold text-gray-600 shadow-none text-sm"
|
||||
href="#download">Download</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
106
src/components/widgets/Hero.astro
Normal file
@ -0,0 +1,106 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
import { Picture } from '@astrojs/image/components';
|
||||
|
||||
interface CallToAction {
|
||||
text: string;
|
||||
href: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
callToAction?: string | CallToAction;
|
||||
callToAction2?: string | CallToAction;
|
||||
image?: string | any; // TODO: find HTMLElementProps
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
callToAction = await Astro.slots.render('callToAction'),
|
||||
callToAction2 = await Astro.slots.render('callToAction2'),
|
||||
image = await Astro.slots.render('image'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section>
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6">
|
||||
<div class="py-12 md:py-20">
|
||||
<div class="text-center pb-10 md:pb-16 max-w-5xl mx-auto">
|
||||
{
|
||||
title && (
|
||||
<h1
|
||||
class="text-5xl md:text-[3.50rem] font-bold leading-tighter tracking-tighter mb-4 font-heading"
|
||||
set:html={title}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div class="max-w-3xl mx-auto">
|
||||
{subtitle && <p class="text-xl font-medium text-gray-500 mb-8 dark:text-slate-400" set:html={subtitle} />}
|
||||
<div class="max-w-xs sm:max-w-md m-auto flex flex-nowrap flex-col sm:flex-row sm:justify-center gap-4">
|
||||
{
|
||||
callToAction && (
|
||||
<div class="flex w-full sm:w-auto">
|
||||
{typeof callToAction === 'string' ? (
|
||||
<Fragment set:html={callToAction} />
|
||||
) : (
|
||||
<a class="btn btn-primary sm:mb-0 w-full" href={callToAction?.href} target="_blank" rel="noopener">
|
||||
{callToAction?.icon && (
|
||||
<>
|
||||
<Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />{' '}
|
||||
</>
|
||||
)}
|
||||
{callToAction?.text}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
callToAction2 && (
|
||||
<div class="flex w-full sm:w-auto">
|
||||
{typeof callToAction2 === 'string' ? (
|
||||
<Fragment set:html={callToAction2} />
|
||||
) : (
|
||||
<a class="btn w-full" href={callToAction2?.href}>
|
||||
{callToAction2?.icon && (
|
||||
<>
|
||||
<Icon name={callToAction2.icon} class="w-5 h-5 mr-1 -ml-1.5" />{' '}
|
||||
</>
|
||||
)}
|
||||
{callToAction2.text}
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{
|
||||
image && (
|
||||
<div class="relative m-auto max-w-4xl">
|
||||
{typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Picture
|
||||
class="mx-auto rounded-md w-full"
|
||||
widths={[400, 768, 1600]}
|
||||
sizes="(max-width: 767px) 400px, (max-width: 1599px) 768px, 1600px"
|
||||
aspectRatio={1600 / 833}
|
||||
loading="eager"
|
||||
width={1600}
|
||||
height={833}
|
||||
{...image}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
11
src/components/widgets/Note.astro
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
---
|
||||
|
||||
<section class="bg-primary-50 dark:bg-slate-800">
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-4 text-md text-center font-medium">
|
||||
<span class="font-bold">
|
||||
<Icon name="tabler:info-square" class="w-5 h-5 inline-block align-text-bottom" /> PikaOS:</span
|
||||
> Performance, Simplicity and Birb
|
||||
</div>
|
||||
</section>
|
274
src/components/widgets/Pricing.astro
Normal file
@ -0,0 +1,274 @@
|
||||
---
|
||||
---
|
||||
|
||||
<!-- Pricing -->
|
||||
<div class="max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 mx-auto">
|
||||
<!-- Title -->
|
||||
<div class="max-w-2xl mx-auto text-center mb-10 lg:mb-14">
|
||||
<h2 class="text-2xl font-bold md:text-4xl md:leading-tight dark:text-white">Pricing</h2>
|
||||
<p class="mt-1 text-gray-600 dark:text-gray-400">
|
||||
Whatever your status, our offers evolve according to your needs.
|
||||
</p>
|
||||
</div>
|
||||
<!-- End Title -->
|
||||
|
||||
<!-- Grid -->
|
||||
<div class="mt-12 grid sm:grid-cols-2 lg:grid-cols-4 gap-6 lg:items-center">
|
||||
<!-- Card -->
|
||||
<div class="flex flex-col border border-gray-200 text-center rounded-xl p-8 dark:border-gray-700">
|
||||
<h4 class="font-medium text-lg text-gray-800 dark:text-gray-200">Free</h4>
|
||||
<span class="mt-7 font-bold text-5xl text-gray-800 dark:text-gray-200">Free</span>
|
||||
<p class="mt-2 text-sm text-gray-500">Forever free</p>
|
||||
|
||||
<ul class="mt-7 space-y-2.5 text-sm">
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> 1 user</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> Plan features</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> Product support</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="mt-5 btn">Sign up</button>
|
||||
</div>
|
||||
<!-- End Card -->
|
||||
|
||||
<!-- Card -->
|
||||
<div class="flex flex-col border-2 border-blue-600 text-center shadow-xl rounded-xl p-8 dark:border-blue-500">
|
||||
<p class="mb-3">
|
||||
<span
|
||||
class="inline-flex items-center gap-1.5 py-1.5 px-3 rounded-md text-xs uppercase font-semibold bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-white"
|
||||
>Most popular</span
|
||||
>
|
||||
</p>
|
||||
<h4 class="font-medium text-lg text-gray-800 dark:text-gray-200">Startup</h4>
|
||||
<span class="mt-5 font-bold text-5xl text-gray-800 dark:text-gray-200">
|
||||
<span class="font-bold text-2xl -mr-2">$</span>
|
||||
39
|
||||
</span>
|
||||
<p class="mt-2 text-sm text-gray-500">All the basics for starting a new business</p>
|
||||
|
||||
<ul class="mt-7 space-y-2.5 text-sm">
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> 2 users</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> Plan features</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> Product support</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="mt-5 btn">Sign up</button>
|
||||
</div>
|
||||
<!-- End Card -->
|
||||
|
||||
<!-- Card -->
|
||||
<div class="flex flex-col border border-gray-200 text-center rounded-xl p-8 dark:border-gray-700">
|
||||
<h4 class="font-medium text-lg text-gray-800 dark:text-gray-200">Team</h4>
|
||||
<span class="mt-5 font-bold text-5xl text-gray-800 dark:text-gray-200">
|
||||
<span class="font-bold text-2xl -mr-2">$</span>
|
||||
89
|
||||
</span>
|
||||
<p class="mt-2 text-sm text-gray-500">Everything you need for a growing business</p>
|
||||
|
||||
<ul class="mt-7 space-y-2.5 text-sm">
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> 5 users</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> Plan features</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> Product support</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="mt-5 btn">Sign up</button>
|
||||
</div>
|
||||
<!-- End Card -->
|
||||
|
||||
<!-- Card -->
|
||||
<div class="flex flex-col border border-gray-200 text-center rounded-xl p-8 dark:border-gray-700">
|
||||
<h4 class="font-medium text-lg text-gray-800 dark:text-gray-200">Enterprise</h4>
|
||||
<span class="mt-5 font-bold text-5xl text-gray-800 dark:text-gray-200">
|
||||
<span class="font-bold text-2xl -mr-2">$</span>
|
||||
149
|
||||
</span>
|
||||
<p class="mt-2 text-sm text-gray-500">Advanced features for scaling your business</p>
|
||||
|
||||
<ul class="mt-7 space-y-2.5 text-sm">
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> 10 users</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> Plan features</span>
|
||||
</li>
|
||||
|
||||
<li class="flex space-x-2">
|
||||
<svg
|
||||
class="flex-shrink-0 h-5 w-5 text-blue-600"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.5219 4.0949C11.7604 3.81436 12.181 3.78025 12.4617 4.01871C12.7422 4.25717 12.7763 4.6779 12.5378 4.95844L6.87116 11.6251C6.62896 11.91 6.1998 11.94 5.9203 11.6916L2.9203 9.02494C2.64511 8.78033 2.62032 8.35894 2.86493 8.08375C3.10955 7.80856 3.53092 7.78378 3.80611 8.02839L6.29667 10.2423L11.5219 4.0949Z"
|
||||
fill="currentColor"></path>
|
||||
</svg>
|
||||
<span class="text-gray-800 dark:text-gray-400"> Product support</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="mt-5 btn">Sign up</button>
|
||||
</div>
|
||||
<!-- End Card -->
|
||||
</div>
|
||||
<!-- End Grid -->
|
||||
</div>
|
||||
<!-- End Pricing -->
|
57
src/components/widgets/Stats.astro
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
interface Item {
|
||||
name: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
highlight?: string;
|
||||
items?: Array<Item>;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
highlight,
|
||||
items = [],
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<div class="px-4 py-4 md:py-16 sm:px-6 mx-auto md:px-24 lg:px-8 lg:py-20 max-w-6xl">
|
||||
{
|
||||
(title || subtitle || highlight) && (
|
||||
<div class="max-w-xl mb-10 md:mx-auto sm:text-center lg:max-w-2xl md:mb-12">
|
||||
{highlight && (
|
||||
<p class="text-base text-primary-800 dark:text-primary-200 font-semibold tracking-wide uppercase">
|
||||
{highlight}
|
||||
</p>
|
||||
)}
|
||||
{title && (
|
||||
<h2
|
||||
class="max-w-lg mb-4 text-3xl font-bold leading-none tracking-tight sm:text-4xl md:mx-auto font-heading"
|
||||
set:html={title}
|
||||
/>
|
||||
)}
|
||||
{subtitle && (
|
||||
<p class="max-w-3xl mx-auto text-center text-xl text-gray-600 dark:text-slate-400" set:html={subtitle} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div class="grid grid-cols-2 row-gap-8 md:grid-cols-4">
|
||||
{
|
||||
items.map(({ name, value }) => (
|
||||
<div class="text-center md:border-r md:last:border-none dark:md:border-slate-500 mb-12 md:mb-0">
|
||||
<div class="text-[2.6rem] font-bold lg:text-5xl xl:text-6xl text-primary-800 dark:text-primary-600 font-heading">
|
||||
{value}
|
||||
</div>
|
||||
<p class="text-sm font-medium tracking-widest text-gray-800 dark:text-slate-400 uppercase lg:text-base">
|
||||
{name}
|
||||
</p>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
72
src/components/widgets/Steps.astro
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
import { Picture } from '@astrojs/image/components';
|
||||
|
||||
interface Item {
|
||||
title: string;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
items: Array<Item>;
|
||||
image?: string | any; // TODO: find HTMLElementProps
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
items = [],
|
||||
image = await Astro.slots.render('image'),
|
||||
} = Astro.props;
|
||||
---
|
||||
|
||||
<section class="px-4 py-16 sm:px-6 mx-auto lg:px-8 lg:py-20 max-w-6xl">
|
||||
<div class="grid gap-6 row-gap-10 md:grid-cols-2">
|
||||
<div class="md:py-4 md:pr-16 mb-4 md:mb-0">
|
||||
{title && <h2 class="mb-8 text-3xl lg:text-4xl font-bold font-heading" set:html={title} />}
|
||||
{
|
||||
items &&
|
||||
items.length &&
|
||||
items.map(({ title, description, icon }, index) => (
|
||||
<div class="flex">
|
||||
<div class="flex flex-col items-center mr-4">
|
||||
<div>
|
||||
{index !== items.length - 1 ? (
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-full border-primary-800 dark:border-primary-700 border-2">
|
||||
{icon && <Icon name={icon} class="w-6 h-6 text-primary-800 dark:text-slate-200" />}
|
||||
</div>
|
||||
) : (
|
||||
<div class="flex items-center justify-center w-10 h-10 rounded-full border-primary-800 border-2 bg-primary-800 dark:bg-primary-700 dark:border-primary-700">
|
||||
<Icon name={icon} class="w-6 h-6 text-white dark:text-slate-200" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div class="w-px h-full bg-gray-300 dark:bg-slate-500" />
|
||||
</div>
|
||||
<div class={`pt-1 ${index !== items.length - 1 ? 'pb-8' : ''}`}>
|
||||
{title && <p class="mb-2 text-xl font-bold text-gray-900 dark:text-slate-300" set:html={title} />}
|
||||
{description && <p class="text-gray-600 dark:text-slate-400" set:html={description} />}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div class="relative">
|
||||
{
|
||||
image &&
|
||||
(typeof image === 'string' ? (
|
||||
<Fragment set:html={image} />
|
||||
) : (
|
||||
<Picture
|
||||
class="inset-0 object-cover object-top w-full rounded-md shadow-lg md:absolute md:h-full bg-gray-400 dark:bg-slate-700"
|
||||
widths={[400, 768]}
|
||||
sizes="(max-width: 768px) 100vw, 432px"
|
||||
aspectRatio="432:768"
|
||||
{...image}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
127
src/components/widgets/Steps2.astro
Normal file
@ -0,0 +1,127 @@
|
||||
---
|
||||
import { Icon } from 'astro-icon';
|
||||
|
||||
interface Item {
|
||||
title: string;
|
||||
description: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
interface CallToAction {
|
||||
text: string;
|
||||
href: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
highlight?: string;
|
||||
callToAction?: string | CallToAction;
|
||||
items: Array<Item>;
|
||||
}
|
||||
|
||||
const {
|
||||
title = await Astro.slots.render('title'),
|
||||
subtitle = await Astro.slots.render('subtitle'),
|
||||
highlight,
|
||||
callToAction = await Astro.slots.render('callToAction'),
|
||||
items = [],
|
||||
} = Astro.props;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
<!-- Steps2 Widget Example ***************** -->
|
||||
|
||||
<Steps2
|
||||
title="Sed ac magna sit amet risus tristique interdum, at vel velit in hac habitasse platea dictumst."
|
||||
subtitle="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla, sed porttitor est nibh at nulla. Praesent placerat enim ut ex tincidunt vehicula. Fusce sit amet dui tellus."
|
||||
callToAction={{
|
||||
text: 'Get template',
|
||||
href: 'https://github.com/onwidget/astrowind',
|
||||
icon: 'tabler:download',
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
title: 'Responsive Elements',
|
||||
description:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
|
||||
},
|
||||
{
|
||||
title: 'Flexible Team',
|
||||
description:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
|
||||
},
|
||||
{
|
||||
title: 'Ecologic Software',
|
||||
description:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi sagittis, quam nec venenatis lobortis, mi risus tempus nulla.',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
*/
|
||||
---
|
||||
|
||||
<section>
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 overflow-hidden">
|
||||
<div class="py-12 md:py-20">
|
||||
<div class="py-4 sm:py-6 lg:py-8">
|
||||
<div class="flex flex-wrap md:-mx-8">
|
||||
<div class="w-full lg:w-1/2 px-0 sm:px-8 mb-12">
|
||||
<div>
|
||||
{
|
||||
highlight && (
|
||||
<p
|
||||
class="text-base text-primary-800 dark:text-primary-200 font-semibold tracking-wide uppercase"
|
||||
set:html={highlight}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{title && <h2 class="mb-4 text-3xl lg:text-4xl font-bold font-heading" set:html={title} />}
|
||||
{subtitle && <p class="mb-8 text-xl text-gray-600 dark:text-slate-400" set:html={subtitle} />}
|
||||
|
||||
<div class="w-full">
|
||||
{
|
||||
typeof callToAction === 'string' ? (
|
||||
<Fragment set:html={callToAction} />
|
||||
) : (
|
||||
callToAction &&
|
||||
callToAction.text &&
|
||||
callToAction.href && (
|
||||
<a class="btn btn-primary mb-4 sm:mb-0" href={callToAction.href} target="_blank" rel="noopener">
|
||||
{callToAction.icon && <Icon name={callToAction.icon} class="w-5 h-5 mr-1 -ml-1.5" />}
|
||||
{callToAction.text}
|
||||
</a>
|
||||
)
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/2 px-0 sm:px-8">
|
||||
<ul class="space-y-10">
|
||||
{
|
||||
items && items.length
|
||||
? items.map(({ title: title2, description, icon }, index) => (
|
||||
<li class="flex md:-mx-4">
|
||||
<div class="pr-4 sm:pl-4">
|
||||
<span class="flex w-16 h-16 mx-auto items-center justify-center text-2xl font-bold rounded-full bg-primary-100 text-primary-800">
|
||||
{icon ? <Icon name={icon} class="w-6 h-6 icon-bold" /> : index + 1}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pl-4">
|
||||
<h3 class="mb-4 text-xl font-semibold font-heading" set:html={title2} />
|
||||
<p class="text-gray-500 dark:text-gray-400" set:html={description} />
|
||||
</div>
|
||||
</li>
|
||||
))
|
||||
: ''
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
32
src/config.mjs
Normal file
@ -0,0 +1,32 @@
|
||||
import defaultImage from './assets/images/default.png';
|
||||
|
||||
const CONFIG = {
|
||||
name: 'PikaOS',
|
||||
|
||||
origin: 'https://pikaos.ferreo.dev',
|
||||
basePathname: '/',
|
||||
trailingSlash: false,
|
||||
|
||||
title: 'PikaOS — The cool birb OS',
|
||||
description: 'PikaOS is a gaming focused linux distribution focussing on ease of use and high compatability. Using the know how from Nobara combined with a Ubuntu base, PikaOS is almost unrivaled in software comaptability.',
|
||||
defaultImage: defaultImage,
|
||||
|
||||
defaultTheme: 'system', // Values: "system" | "light" | "dark" | "light:only" | "dark:only"
|
||||
|
||||
language: 'en',
|
||||
textDirection: 'ltr',
|
||||
|
||||
dateFormatter: new Intl.DateTimeFormat('en', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
}),
|
||||
|
||||
googleAnalyticsId: false, // or "G-XXXXXXXXXX",
|
||||
googleSiteVerificationId: false,
|
||||
|
||||
};
|
||||
|
||||
export const SITE = {...CONFIG };
|
||||
export const DATE_FORMATTER = CONFIG.dateFormatter;
|
1
src/content/config.ts
Normal file
@ -0,0 +1 @@
|
||||
|
49
src/content/types.generated.d.ts
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
declare module 'astro:content' {
|
||||
export { z } from 'astro/zod';
|
||||
export type CollectionEntry<C extends keyof typeof entryMap> =
|
||||
typeof entryMap[C][keyof typeof entryMap[C]] & Render;
|
||||
|
||||
type BaseCollectionConfig<S extends import('astro/zod').ZodRawShape> = {
|
||||
schema?: S;
|
||||
slug?: (entry: {
|
||||
id: CollectionEntry<keyof typeof entryMap>['id'];
|
||||
defaultSlug: string;
|
||||
collection: string;
|
||||
body: string;
|
||||
data: import('astro/zod').infer<import('astro/zod').ZodObject<S>>;
|
||||
}) => string | Promise<string>;
|
||||
};
|
||||
export function defineCollection<S extends import('astro/zod').ZodRawShape>(
|
||||
input: BaseCollectionConfig<S>
|
||||
): BaseCollectionConfig<S>;
|
||||
|
||||
export function getEntry<C extends keyof typeof entryMap, E extends keyof typeof entryMap[C]>(
|
||||
collection: C,
|
||||
entryKey: E
|
||||
): Promise<typeof entryMap[C][E] & Render>;
|
||||
export function getCollection<
|
||||
C extends keyof typeof entryMap,
|
||||
E extends keyof typeof entryMap[C]
|
||||
>(
|
||||
collection: C,
|
||||
filter?: (data: typeof entryMap[C][E]) => boolean
|
||||
): Promise<(typeof entryMap[C][E] & Render)[]>;
|
||||
|
||||
type InferEntrySchema<C extends keyof typeof entryMap> = import('astro/zod').infer<
|
||||
import('astro/zod').ZodObject<Required<ContentConfig['collections'][C]>['schema']>
|
||||
>;
|
||||
|
||||
type Render = {
|
||||
render(): Promise<{
|
||||
Content: import('astro').MarkdownInstance<{}>['Content'];
|
||||
headings: import('astro').MarkdownHeading[];
|
||||
injectedFrontmatter: Record<string, any>;
|
||||
}>;
|
||||
};
|
||||
|
||||
const entryMap: {
|
||||
|
||||
};
|
||||
|
||||
type ContentConfig = never;
|
||||
}
|
1
src/env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="@astrojs/image/client" />
|
33
src/layouts/BaseLayout.astro
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
import '~/assets/styles/base.css';
|
||||
|
||||
import MetaTags from '~/components/common/MetaTags.astro';
|
||||
import BasicScripts from '~/components/common/BasicScripts.astro';
|
||||
|
||||
import { MetaSEO } from '~/types';
|
||||
import { SITE } from '~/config.mjs';
|
||||
|
||||
export interface Props {
|
||||
meta?: MetaSEO;
|
||||
}
|
||||
|
||||
const { meta = {} } = Astro.props;
|
||||
const { language = 'en', textDirection = 'ltr' } = SITE;
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang={language} dir={textDirection} class="2xl:text-[20px]">
|
||||
<head>
|
||||
<MetaTags {...meta} />
|
||||
</head>
|
||||
|
||||
<body class="antialiased text-gray-900 dark:text-slate-300 tracking-tight bg-white dark:bg-slate-900">
|
||||
<slot />
|
||||
<BasicScripts />
|
||||
<style is:global>
|
||||
img {
|
||||
content-visibility: auto;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
35
src/layouts/Layout.astro
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
export interface Props {
|
||||
title: string;
|
||||
}
|
||||
|
||||
const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
<style is:global>
|
||||
:root {
|
||||
--accent: 124, 58, 237;
|
||||
--accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
|
||||
}
|
||||
html {
|
||||
font-family: system-ui, sans-serif;
|
||||
background-color: #F6F6F6;
|
||||
}
|
||||
code {
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
</style>
|
32
src/layouts/MarkdownLayout.astro
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
import Layout from '~/layouts/BaseLayout.astro';
|
||||
import Header from '~/components/widgets/Header.astro';
|
||||
import Footer from '~/components/widgets/Footer.astro';
|
||||
import Announcement from '~/components/widgets/Announcement.astro';
|
||||
|
||||
import { MetaSEO } from '~/types';
|
||||
|
||||
export interface Props {}
|
||||
|
||||
const { frontmatter } = Astro.props;
|
||||
|
||||
const meta: MetaSEO = {
|
||||
title: frontmatter?.title,
|
||||
};
|
||||
---
|
||||
|
||||
<Layout {meta}>
|
||||
<Announcement />
|
||||
<Header />
|
||||
<main>
|
||||
<section class="px-4 py-16 sm:px-6 mx-auto lg:px-8 lg:py-20 max-w-4xl">
|
||||
<h1 class="font-bold font-heading text-3xl md:text-4xl leading-tighter tracking-tighter">{frontmatter.title}</h1>
|
||||
<div
|
||||
class="mx-auto prose prose-lg max-w-4xl dark:prose-invert dark:prose-headings:text-slate-300 prose-md prose-headings:font-heading prose-headings:leading-tighter prose-headings:tracking-tighter prose-headings:font-bold prose-a:text-primary-600 dark:prose-a:text-primary-400 prose-img:rounded-md prose-img:shadow-lg mt-8"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
22
src/layouts/PageLayout.astro
Normal file
@ -0,0 +1,22 @@
|
||||
---
|
||||
import Layout from '~/layouts/BaseLayout.astro';
|
||||
import Header from '~/components/widgets/Header.astro';
|
||||
import Footer from '~/components/widgets/Footer.astro';
|
||||
//import Announcement from '~/components/widgets/Announcement.astro';
|
||||
|
||||
import { MetaSEO } from '~/types';
|
||||
|
||||
export interface Props {
|
||||
meta?: MetaSEO;
|
||||
}
|
||||
|
||||
const { meta } = Astro.props;
|
||||
---
|
||||
|
||||
<Layout {meta}>
|
||||
<Header />
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
24
src/pages/404.astro
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
import Layout from '~/layouts/BaseLayout.astro';
|
||||
import { getHomePermalink } from '~/utils/permalinks';
|
||||
|
||||
const title = `Error 404`;
|
||||
---
|
||||
|
||||
<Layout meta={{ title }}>
|
||||
<section class="flex items-center h-full p-16">
|
||||
<div class="container flex flex-col items-center justify-center px-5 mx-auto my-8">
|
||||
<div class="max-w-md text-center">
|
||||
<h2 class="mb-8 font-bold text-9xl">
|
||||
<span class="sr-only">Error</span>
|
||||
<span class="bg-clip-text text-transparent bg-gradient-to-r from-primary-500 to-secondary-500">404</span>
|
||||
</h2>
|
||||
<p class="text-3xl font-semibold md:text-3xl">Sorry, we couldn't find this page.</p>
|
||||
<p class="mt-4 mb-8 text-lg text-gray-600 dark:text-slate-400">
|
||||
But dont worry, you can find plenty of other things on our homepage.
|
||||
</p>
|
||||
<a rel="noopener noreferrer" href={getHomePermalink()} class="btn ml-4">Back to homepage</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
271
src/pages/index.astro
Normal file
@ -0,0 +1,271 @@
|
||||
---
|
||||
import { SITE } from "~/config.mjs";
|
||||
import Layout from "~/layouts/PageLayout.astro";
|
||||
|
||||
import Hero from "~/components/widgets/Hero.astro";
|
||||
import Note from "~/components/widgets/Note.astro";
|
||||
import Features from "~/components/widgets/Features.astro";
|
||||
import Features2 from "~/components/widgets/Features2.astro";
|
||||
import Steps from "~/components/widgets/Steps.astro";
|
||||
import Content from "~/components/widgets/Content.astro";
|
||||
import FAQs from "~/components/widgets/FAQs.astro";
|
||||
import Stats from "~/components/widgets/Stats.astro";
|
||||
import CallToAction from "~/components/widgets/CallToAction.astro";
|
||||
|
||||
const meta = {
|
||||
title: SITE.title,
|
||||
description: SITE.description,
|
||||
dontUseTitleTemplate: true,
|
||||
};
|
||||
---
|
||||
|
||||
<Layout {meta}>
|
||||
<!-- Hero Widget ******************* -->
|
||||
|
||||
<Hero
|
||||
callToAction={{
|
||||
text: "Get PikaOS",
|
||||
href: "https://pikaos.ferreo.dev/PikaOS-Gnome-2210-amd64_22.12.27.iso",
|
||||
icon: "tabler:download",
|
||||
}}
|
||||
callToAction2={{ text: "Learn more", href: "#features" }}
|
||||
image={{
|
||||
src: import("~/assets/images/hero.png"),
|
||||
alt: "AstroWind Hero Image",
|
||||
}}
|
||||
>
|
||||
<Fragment slot="title">
|
||||
Your new linux distro, <span>PikaOS</span>!
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
<span class="hidden sm:inline">
|
||||
PikaOS is a gaming focused linux distribution focussing on ease of use and high
|
||||
compatability. Using the know how from <a class="underline" href="https://nobaraproject.org/">Nobara</a> combined with a <a class="underline" href="https://ubuntu.com/">Ubuntu</a> base, PikaOS is almost unrivaled in software comaptability.
|
||||
</span>
|
||||
</Fragment>
|
||||
</Hero>
|
||||
|
||||
<!-- Note Widget ******************* -->
|
||||
|
||||
<Note />
|
||||
|
||||
<!-- Features Widget *************** -->
|
||||
|
||||
<Features
|
||||
highlight="Features"
|
||||
title=""
|
||||
subtitle="What sets PikaOS apart?"
|
||||
items={[
|
||||
[
|
||||
{
|
||||
title: "Cute Birb",
|
||||
description:
|
||||
"PikaOS has a cute birb as the logo/mascot. Who doesn't love the litte guy?",
|
||||
icon: "tabler:viewfinder",
|
||||
},
|
||||
{
|
||||
title: "Gaming Out Of The box",
|
||||
description:
|
||||
"PikeOS is setup to enable as pain free as possible linux gaming out of the box.",
|
||||
icon: "tabler:components",
|
||||
},
|
||||
{
|
||||
title: "Drivers Included",
|
||||
description:
|
||||
"We include the best drivers for your hardware either baked into the OS or installable through our welcome app.",
|
||||
icon: "tabler:list-check",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
title: "Excellent Performance",
|
||||
description:
|
||||
"The combination of upto date drivers and a custom tweaked kernel mean PikaOS is fast.",
|
||||
icon: "tabler:rocket",
|
||||
},
|
||||
{
|
||||
title: "Excellent Compatability",
|
||||
description:
|
||||
"Due to the Ubuntu base and custom patches PikaOS has high levels of software and hardware compatability.",
|
||||
icon: "tabler:app-window",
|
||||
},
|
||||
{
|
||||
title: "Open Source",
|
||||
description:
|
||||
"All of our code can be found on our github and are ppas are available on launchpad. Contributions are also very welcome!",
|
||||
icon: "tabler:bulb",
|
||||
},
|
||||
],
|
||||
]}
|
||||
/>
|
||||
|
||||
<!-- Content Widget **************** -->
|
||||
|
||||
<!-- <Content
|
||||
highlight="Inside template"
|
||||
title="And what's inside? ..."
|
||||
content="Ne dicta praesent ocurreret has, diam theophrastus at pro. Eos etiam regione ut, persius eripuit quo id. Sit te euismod tacimates."
|
||||
items={[
|
||||
{
|
||||
title: "Per ei quaeque sensibus",
|
||||
description:
|
||||
"Ex usu illum iudico molestie. Pro ne agam facete mediocritatem, ridens labore facete mea ei. Pro id apeirian dignissim.",
|
||||
},
|
||||
{
|
||||
title: "Cu imperdiet posidonium sed",
|
||||
description:
|
||||
"Amet utinam aliquando ut mea, malis admodum ocurreret nec et, elit tibique cu nec. Nec ex maluisset inciderint, ex quis.",
|
||||
},
|
||||
{
|
||||
title: "Nulla omittam sadipscing mel ne",
|
||||
description:
|
||||
"At sed possim oporteat probatus, justo graece ne nec, minim commodo legimus ut vix. Ut eos iudico quando soleat, nam modus.",
|
||||
},
|
||||
]}
|
||||
image={{
|
||||
src: import("~/assets/images/caos.jpg"),
|
||||
alt: "Colorful Image",
|
||||
}}
|
||||
/> -->
|
||||
|
||||
<!-- Features2 Widget ************** -->
|
||||
<!--
|
||||
<Features2
|
||||
title="Most used widgets"
|
||||
subtitle="Provides frequently used components for building websites using Tailwind CSS"
|
||||
highlight="Components"
|
||||
items={[
|
||||
{
|
||||
title: "Headers",
|
||||
description:
|
||||
"In general, Headers contain information that makes it easier for visitors to interact with the website.",
|
||||
icon: "flat-color-icons:template",
|
||||
},
|
||||
{
|
||||
title: "Heros",
|
||||
description:
|
||||
"If you want your website to get more than its fair share of visitors, the Hero section needs to be stellar.",
|
||||
icon: "flat-color-icons:gallery",
|
||||
},
|
||||
{
|
||||
title: "Features",
|
||||
description:
|
||||
"Display your product in action and how the Features actually create a solution for your target customer.",
|
||||
icon: "flat-color-icons:todo-list",
|
||||
},
|
||||
{
|
||||
title: "Content",
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.",
|
||||
icon: "flat-color-icons:document",
|
||||
},
|
||||
{
|
||||
title: "Call-to-Action",
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.",
|
||||
icon: "flat-color-icons:advertising",
|
||||
},
|
||||
{
|
||||
title: "Pricing",
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.",
|
||||
icon: "flat-color-icons:currency-exchange",
|
||||
},
|
||||
{
|
||||
title: "Testimonial",
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.",
|
||||
icon: "flat-color-icons:voice-presentation",
|
||||
},
|
||||
{
|
||||
title: "Contact",
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.",
|
||||
icon: "flat-color-icons:business-contact",
|
||||
},
|
||||
{
|
||||
title: "Footers",
|
||||
description:
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.",
|
||||
icon: "icon-park:page-template",
|
||||
},
|
||||
]}
|
||||
/> -->
|
||||
|
||||
<!-- FAQs Widget ******************* -->
|
||||
|
||||
<!-- <FAQs
|
||||
title="Frequently Asked Questions"
|
||||
subtitle="Duis turpis dui, fringilla mattis sem nec, fringilla euismod neque. Morbi tincidunt lacus nec tortor scelerisque pulvinar."
|
||||
highlight="FAQs"
|
||||
items={[
|
||||
[
|
||||
{
|
||||
question: "What do I need to start?",
|
||||
answer:
|
||||
"Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds. Many say exploration is part of our destiny, but it’s actually our duty to future generations.",
|
||||
},
|
||||
{
|
||||
question: "How to install the Astro + Tailwind CSS template?",
|
||||
answer:
|
||||
"Well, the way they make shows is, they make one show. That show's called a pilot. Then they show that show to the people who make shows, and on the strength of that one show they decide if they're going to make more shows. Some pilots get picked and become television programs. Some don't, become nothing. She starred in one of the ones that became nothing.",
|
||||
},
|
||||
{
|
||||
question: "What's something that you don't understand?",
|
||||
answer:
|
||||
"A flower in my garden, a mystery in my panties. Heart attack never stopped old Big Bear. I didn't even know we were calling him Big Bear.",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
question: "What's an example of when you changed your mind?",
|
||||
answer:
|
||||
"Michael Knight a young loner on a crusade to champion the cause of the innocent. The helpless. The powerless in a world of criminals who operate above the law. Here he comes Here comes Speed Racer. He's a demon on wheels.",
|
||||
},
|
||||
{
|
||||
question: "What is something that you would like to try again?",
|
||||
answer:
|
||||
"A business big enough that it could be listed on the NASDAQ goes belly up. Disappears! It ceases to exist without me. No, you clearly don't know who you're talking to, so let me clue you in.",
|
||||
},
|
||||
{
|
||||
question:
|
||||
"If you could only ask one question to each person you meet, what would that question be?",
|
||||
answer:
|
||||
"This is not about revenge. This is about justice. A lot of things can change in twelve years, Admiral. Well, that's certainly good to know. About four years. I got tired of hearing how young I looked.",
|
||||
},
|
||||
],
|
||||
]}
|
||||
/> -->
|
||||
|
||||
<!-- Stats Widget ****************** -->
|
||||
|
||||
<!-- <Stats
|
||||
items={[
|
||||
{ name: "Downloads", value: "132K" },
|
||||
{ name: "Stars", value: "24.8K" },
|
||||
{ name: "Forks", value: "10.3K" },
|
||||
{ name: "Users", value: "48.4K" },
|
||||
]}
|
||||
/> -->
|
||||
|
||||
<!-- CallToAction Widget *********** -->
|
||||
<div id="download">
|
||||
<CallToAction
|
||||
callToAction={{
|
||||
text: "Download ISO",
|
||||
href: "https://pikaos.ferreo.dev/PikaOS-Gnome-2210-amd64_22.12.27.iso",
|
||||
icon: "tabler:download",
|
||||
}}
|
||||
>
|
||||
<Fragment slot="title">
|
||||
PikaOS Download
|
||||
</Fragment>
|
||||
|
||||
<Fragment slot="subtitle">
|
||||
Grab our latest PikaOS ISO now!<br/><br/>
|
||||
<a class="underline pb-5" href="https://pikaos.ferreo.dev/PikaOS-Gnome-2210-amd64_22.12.27.md5">MD5 hash here.</a>
|
||||
</Fragment>
|
||||
</CallToAction>
|
||||
</div>
|
||||
</Layout>
|
185
src/pages/privacy.md
Normal file
@ -0,0 +1,185 @@
|
||||
---
|
||||
title: 'Privacy Policy'
|
||||
layout: '~/layouts/MarkdownLayout.astro'
|
||||
---
|
||||
|
||||
_Last updated_: January 06, 2023
|
||||
|
||||
This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.
|
||||
|
||||
We use Your Personal data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy. This Privacy Policy is just a Demo.
|
||||
|
||||
## Interpretation and Definitions
|
||||
|
||||
### Interpretation
|
||||
|
||||
The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.
|
||||
|
||||
### Definitions
|
||||
|
||||
For the purposes of this Privacy Policy:
|
||||
|
||||
- **Account** means a unique account created for You to access our Service or parts of our Service.
|
||||
- **Company** (referred to as either "the Company", "We", "Us" or "Our" in this Agreement) refers to AstroWind LLC, 1 Cupertino, CA 95014.
|
||||
- **Cookies** are small files that are placed on Your computer, mobile device or any other device by a website, containing the details of Your browsing history on that website among its many uses.
|
||||
- **Country** refers to: California, United States
|
||||
- **Device** means any device that can access the Service such as a computer, a cellphone or a digital tablet.
|
||||
- **Personal Data** is any information that relates to an identified or identifiable individual.
|
||||
- **Service** refers to the Website.
|
||||
- **Service Provider** means any natural or legal person who processes the data on behalf of the Company. It refers to third-party companies or individuals employed by the Company to facilitate the Service, to provide the Service on behalf of the Company, to perform services related to the Service or to assist the Company in analyzing how the Service is used.
|
||||
- **Usage Data** refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit).
|
||||
- **Website** refers to AstroWind, accessible from [https://astrowind.vercel.app](https://astrowind.vercel.app)
|
||||
- **You** means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.
|
||||
|
||||
## Collecting and Using Your Personal Data
|
||||
|
||||
### Types of Data Collected
|
||||
|
||||
#### Personal Data
|
||||
|
||||
While using Our Service, We may ask You to provide Us with certain personally identifiable information that can be used to contact or identify You. Personally identifiable information may include, but is not limited to:
|
||||
|
||||
- Usage Data
|
||||
|
||||
#### Usage Data
|
||||
|
||||
Usage Data is collected automatically when using the Service.
|
||||
|
||||
Usage Data may include information such as Your Device's Internet Protocol address (e.g. IP address), browser type, browser version, the pages of our Service that You visit, the time and date of Your visit, the time spent on those pages, unique device identifiers and other diagnostic data.
|
||||
|
||||
When You access the Service by or through a mobile device, We may collect certain information automatically, including, but not limited to, the type of mobile device You use, Your mobile device unique ID, the IP address of Your mobile device, Your mobile operating system, the type of mobile Internet browser You use, unique device identifiers and other diagnostic data.
|
||||
|
||||
We may also collect information that Your browser sends whenever You visit our Service or when You access the Service by or through a mobile device.
|
||||
|
||||
#### Tracking Technologies and Cookies
|
||||
|
||||
We use Cookies and similar tracking technologies to track the activity on Our Service and store certain information. Tracking technologies used are beacons, tags, and scripts to collect and track information and to improve and analyze Our Service. The technologies We use may include:
|
||||
|
||||
- **Cookies or Browser Cookies.** A cookie is a small file placed on Your Device. You can instruct Your browser to refuse all Cookies or to indicate when a Cookie is being sent. However, if You do not accept Cookies, You may not be able to use some parts of our Service. Unless you have adjusted Your browser setting so that it will refuse Cookies, our Service may use Cookies.
|
||||
- **Web Beacons.** Certain sections of our Service and our emails may contain small electronic files known as web beacons (also referred to as clear gifs, pixel tags, and single-pixel gifs) that permit the Company, for example, to count users who have visited those pages or opened an email and for other related website statistics (for example, recording the popularity of a certain section and verifying system and server integrity).
|
||||
|
||||
Cookies can be "Persistent" or "Session" Cookies. Persistent Cookies remain on Your personal computer or mobile device when You go offline, while Session Cookies are deleted as soon as You close Your web browser.
|
||||
|
||||
We use both Session and Persistent Cookies for the purposes set out below:
|
||||
|
||||
- **Necessary / Essential Cookies**
|
||||
|
||||
Type: Session Cookies
|
||||
|
||||
Administered by: Us
|
||||
|
||||
Purpose: These Cookies are essential to provide You with services available through the Website and to enable You to use some of its features. They help to authenticate users and prevent fraudulent use of user accounts. Without these Cookies, the services that You have asked for cannot be provided, and We only use these Cookies to provide You with those services.
|
||||
|
||||
- **Cookies Policy / Notice Acceptance Cookies**
|
||||
|
||||
Type: Persistent Cookies
|
||||
|
||||
Administered by: Us
|
||||
|
||||
Purpose: These Cookies identify if users have accepted the use of cookies on the Website.
|
||||
|
||||
- **Functionality Cookies**
|
||||
|
||||
Type: Persistent Cookies
|
||||
|
||||
Administered by: Us
|
||||
|
||||
Purpose: These Cookies allow us to remember choices You make when You use the Website, such as remembering your login details or language preference. The purpose of these Cookies is to provide You with a more personal experience and to avoid You having to re-enter your preferences every time You use the Website.
|
||||
|
||||
For more information about the cookies we use and your choices regarding cookies, please visit our Cookies Policy or the Cookies section of our Privacy Policy.
|
||||
|
||||
## Use of Your Personal Data
|
||||
|
||||
The Company may use Personal Data for the following purposes:
|
||||
|
||||
- **To provide and maintain our Service**, including to monitor the usage of our Service.
|
||||
- **To manage Your Account:** to manage Your registration as a user of the Service. The Personal Data You provide can give You access to different functionalities of the Service that are available to You as a registered user.
|
||||
- **For the performance of a contract:** the development, compliance and undertaking of the purchase contract for the products, items or services You have purchased or of any other contract with Us through the Service.
|
||||
- **To contact You:** To contact You by email, telephone calls, SMS, or other equivalent forms of electronic communication, such as a mobile application's push notifications regarding updates or informative communications related to the functionalities, products or contracted services, including the security updates, when necessary or reasonable for their implementation.
|
||||
- **To provide You** with news, special offers and general information about other goods, services and events which we offer that are similar to those that you have already purchased or enquired about unless You have opted not to receive such information.
|
||||
- **To manage Your requests:** To attend and manage Your requests to Us.
|
||||
- **For business transfers:** We may use Your information to evaluate or conduct a merger, divestiture, restructuring, reorganization, dissolution, or other sale or transfer of some or all of Our assets, whether as a going concern or as part of bankruptcy, liquidation, or similar proceeding, in which Personal Data held by Us about our Service users is among the assets transferred.
|
||||
- **For other purposes**: We may use Your information for other purposes, such as data analysis, identifying usage trends, determining the effectiveness of our promotional campaigns and to evaluate and improve our Service, products, services, marketing and your experience.
|
||||
|
||||
We may share Your personal information in the following situations:
|
||||
|
||||
- **With Service Providers:** We may share Your personal information with Service Providers to monitor and analyze the use of our Service, to contact You.
|
||||
- **For business transfers:** We may share or transfer Your personal information in connection with, or during negotiations of, any merger, sale of Company assets, financing, or acquisition of all or a portion of Our business to another company.
|
||||
- **With Affiliates:** We may share Your information with Our affiliates, in which case we will require those affiliates to honor this Privacy Policy. Affiliates include Our parent company and any other subsidiaries, joint venture partners or other companies that We control or that are under common control with Us.
|
||||
- **With business partners:** We may share Your information with Our business partners to offer You certain products, services or promotions.
|
||||
- **With other users:** when You share personal information or otherwise interact in the public areas with other users, such information may be viewed by all users and may be publicly distributed outside.
|
||||
- **With Your consent**: We may disclose Your personal information for any other purpose with Your consent.
|
||||
|
||||
## Retention of Your Personal Data
|
||||
|
||||
The Company will retain Your Personal Data only for as long as is necessary for the purposes set out in this Privacy Policy. We will retain and use Your Personal Data to the extent necessary to comply with our legal obligations (for example, if we are required to retain your data to comply with applicable laws), resolve disputes, and enforce our legal agreements and policies.
|
||||
|
||||
The Company will also retain Usage Data for internal analysis purposes. Usage Data is generally retained for a shorter period of time, except when this data is used to strengthen the security or to improve the functionality of Our Service, or We are legally obligated to retain this data for longer time periods.
|
||||
|
||||
## Transfer of Your Personal Data
|
||||
|
||||
Your information, including Personal Data, is processed at the Company's operating offices and in any other places where the parties involved in the processing are located. It means that this information may be transferred to — and maintained on — computers located outside of Your state, province, country or other governmental jurisdiction where the data protection laws may differ than those from Your jurisdiction.
|
||||
|
||||
Your consent to this Privacy Policy followed by Your submission of such information represents Your agreement to that transfer.
|
||||
|
||||
The Company will take all steps reasonably necessary to ensure that Your data is treated securely and in accordance with this Privacy Policy and no transfer of Your Personal Data will take place to an organization or a country unless there are adequate controls in place including the security of Your data and other personal information.
|
||||
|
||||
## Delete Your Personal Data
|
||||
|
||||
You have the right to delete or request that We assist in deleting the Personal Data that We have collected about You.
|
||||
|
||||
Our Service may give You the ability to delete certain information about You from within the Service.
|
||||
|
||||
You may update, amend, or delete Your information at any time by signing in to Your Account, if you have one, and visiting the account settings section that allows you to manage Your personal information. You may also contact Us to request access to, correct, or delete any personal information that You have provided to Us.
|
||||
|
||||
Please note, however, that We may need to retain certain information when we have a legal obligation or lawful basis to do so.
|
||||
|
||||
## Disclosure of Your Personal Data
|
||||
|
||||
### Business Transactions
|
||||
|
||||
If the Company is involved in a merger, acquisition or asset sale, Your Personal Data may be transferred. We will provide notice before Your Personal Data is transferred and becomes subject to a different Privacy Policy.
|
||||
|
||||
#### Law enforcement
|
||||
|
||||
Under certain circumstances, the Company may be required to disclose Your Personal Data if required to do so by law or in response to valid requests by public authorities (e.g. a court or a government agency).
|
||||
|
||||
#### Other legal requirements
|
||||
|
||||
The Company may disclose Your Personal Data in the good faith belief that such action is necessary to:
|
||||
|
||||
- Comply with a legal obligation
|
||||
- Protect and defend the rights or property of the Company
|
||||
- Prevent or investigate possible wrongdoing in connection with the Service
|
||||
- Protect the personal safety of Users of the Service or the public
|
||||
- Protect against legal liability
|
||||
|
||||
## Security of Your Personal Data
|
||||
|
||||
The security of Your Personal Data is important to Us, but remember that no method of transmission over the Internet, or method of electronic storage is 100% secure. While We strive to use commercially acceptable means to protect Your Personal Data, We cannot guarantee its absolute security.
|
||||
|
||||
## Children's Privacy
|
||||
|
||||
Our Service does not address anyone under the age of 13. We do not knowingly collect personally identifiable information from anyone under the age of 13. If You are a parent or guardian and You are aware that Your child has provided Us with Personal Data, please contact Us. If We become aware that We have collected Personal Data from anyone under the age of 13 without verification of parental consent, We take steps to remove that information from Our servers.
|
||||
|
||||
If We need to rely on consent as a legal basis for processing Your information and Your country requires consent from a parent, We may require Your parent's consent before We collect and use that information.
|
||||
|
||||
## Links to Other Websites
|
||||
|
||||
Our Service may contain links to other websites that are not operated by Us. If You click on a third party link, You will be directed to that third party's site. We strongly advise You to review the Privacy Policy of every site You visit.
|
||||
|
||||
We have no control over and assume no responsibility for the content, privacy policies or practices of any third party sites or services.
|
||||
|
||||
## Changes to this Privacy Policy
|
||||
|
||||
We may update Our Privacy Policy from time to time. We will notify You of any changes by posting the new Privacy Policy on this page.
|
||||
|
||||
We will let You know via email and/or a prominent notice on Our Service, prior to the change becoming effective and update the "Last updated" date at the top of this Privacy Policy.
|
||||
|
||||
You are advised to review this Privacy Policy periodically for any changes. Changes to this Privacy Policy are effective when they are posted on this page.
|
||||
|
||||
## Contact Us
|
||||
|
||||
If you have any questions about this Privacy Policy, You can contact us:
|
||||
|
||||
- By email: somecoolemail@domain.com
|
120
src/pages/terms.md
Normal file
@ -0,0 +1,120 @@
|
||||
---
|
||||
title: 'Terms and Conditions'
|
||||
layout: '~/layouts/MarkdownLayout.astro'
|
||||
---
|
||||
|
||||
_Last updated_: January 06, 2023
|
||||
|
||||
Please read these terms and conditions carefully before using Our Service.
|
||||
|
||||
## Interpretation and Definitions
|
||||
|
||||
### Interpretation
|
||||
|
||||
The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.
|
||||
|
||||
### Definitions
|
||||
|
||||
For the purposes of these Terms and Conditions:
|
||||
|
||||
- **Affiliate** means an entity that controls, is controlled by or is under common control with a party, where "control" means ownership of 50% or more of the shares, equity interest or other securities entitled to vote for election of directors or other managing authority.
|
||||
|
||||
- **Country** refers to: California, United States
|
||||
|
||||
- **Company** (referred to as either "the Company", "We", "Us" or "Our" in this Agreement) refers to AstroWind LLC, 1 Cupertino, CA 95014.
|
||||
|
||||
- **Device** means any device that can access the Service such as a computer, a cellphone or a digital tablet.
|
||||
|
||||
- **Service** refers to the Website.
|
||||
|
||||
- **Terms and Conditions** (also referred as "Terms") mean these Terms and Conditions that form the entire agreement between You and the Company regarding the use of the Service. This Terms and Conditions agreement is a Demo.
|
||||
|
||||
- **Third-party Social Media Service** means any services or content (including data, information, products or services) provided by a third-party that may be displayed, included or made available by the Service.
|
||||
|
||||
- **Website** refers to AstroWind, accessible from [https://astrowind.vercel.app](https://astrowind.vercel.app)
|
||||
|
||||
- **You** means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.
|
||||
|
||||
## Acknowledgment
|
||||
|
||||
These are the Terms and Conditions governing the use of this Service and the agreement that operates between You and the Company. These Terms and Conditions set out the rights and obligations of all users regarding the use of the Service.
|
||||
|
||||
Your access to and use of the Service is conditioned on Your acceptance of and compliance with these Terms and Conditions. These Terms and Conditions apply to all visitors, users and others who access or use the Service.
|
||||
|
||||
By accessing or using the Service You agree to be bound by these Terms and Conditions. If You disagree with any part of these Terms and Conditions then You may not access the Service.
|
||||
|
||||
You represent that you are over the age of 18\. The Company does not permit those under 18 to use the Service.
|
||||
|
||||
Your access to and use of the Service is also conditioned on Your acceptance of and compliance with the Privacy Policy of the Company. Our Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your personal information when You use the Application or the Website and tells You about Your privacy rights and how the law protects You. Please read Our Privacy Policy carefully before using Our Service.
|
||||
|
||||
## Links to Other Websites
|
||||
|
||||
Our Service may contain links to third-party web sites or services that are not owned or controlled by the Company.
|
||||
|
||||
The Company has no control over, and assumes no responsibility for, the content, privacy policies, or practices of any third party web sites or services. You further acknowledge and agree that the Company shall not be responsible or liable, directly or indirectly, for any damage or loss caused or alleged to be caused by or in connection with the use of or reliance on any such content, goods or services available on or through any such web sites or services.
|
||||
|
||||
We strongly advise You to read the terms and conditions and privacy policies of any third-party web sites or services that You visit.
|
||||
|
||||
## Termination
|
||||
|
||||
We may terminate or suspend Your access immediately, without prior notice or liability, for any reason whatsoever, including without limitation if You breach these Terms and Conditions.
|
||||
|
||||
Upon termination, Your right to use the Service will cease immediately.
|
||||
|
||||
## Limitation of Liability
|
||||
|
||||
Notwithstanding any damages that You might incur, the entire liability of the Company and any of its suppliers under any provision of this Terms and Your exclusive remedy for all of the foregoing shall be limited to the amount actually paid by You through the Service or 100 USD if You haven't purchased anything through the Service.
|
||||
|
||||
To the maximum extent permitted by applicable law, in no event shall the Company or its suppliers be liable for any special, incidental, indirect, or consequential damages whatsoever (including, but not limited to, damages for loss of profits, loss of data or other information, for business interruption, for personal injury, loss of privacy arising out of or in any way related to the use of or inability to use the Service, third-party software and/or third-party hardware used with the Service, or otherwise in connection with any provision of this Terms), even if the Company or any supplier has been advised of the possibility of such damages and even if the remedy fails of its essential purpose.
|
||||
|
||||
Some states do not allow the exclusion of implied warranties or limitation of liability for incidental or consequential damages, which means that some of the above limitations may not apply. In these states, each party's liability will be limited to the greatest extent permitted by law.
|
||||
|
||||
## "AS IS" and "AS AVAILABLE" Disclaimer
|
||||
|
||||
The Service is provided to You "AS IS" and "AS AVAILABLE" and with all faults and defects without warranty of any kind. To the maximum extent permitted under applicable law, the Company, on its own behalf and on behalf of its Affiliates and its and their respective licensors and service providers, expressly disclaims all warranties, whether express, implied, statutory or otherwise, with respect to the Service, including all implied warranties of merchantability, fitness for a particular purpose, title and non-infringement, and warranties that may arise out of course of dealing, course of performance, usage or trade practice. Without limitation to the foregoing, the Company provides no warranty or undertaking, and makes no representation of any kind that the Service will meet Your requirements, achieve any intended results, be compatible or work with any other software, applications, systems or services, operate without interruption, meet any performance or reliability standards or be error free or that any errors or defects can or will be corrected.
|
||||
|
||||
Without limiting the foregoing, neither the Company nor any of the company's provider makes any representation or warranty of any kind, express or implied: (i) as to the operation or availability of the Service, or the information, content, and materials or products included thereon; (ii) that the Service will be uninterrupted or error-free; (iii) as to the accuracy, reliability, or currency of any information or content provided through the Service; or (iv) that the Service, its servers, the content, or e-mails sent from or on behalf of the Company are free of viruses, scripts, trojan horses, worms, malware, timebombs or other harmful components.
|
||||
|
||||
Some jurisdictions do not allow the exclusion of certain types of warranties or limitations on applicable statutory rights of a consumer, so some or all of the above exclusions and limitations may not apply to You. But in such a case the exclusions and limitations set forth in this section shall be applied to the greatest extent enforceable under applicable law.
|
||||
|
||||
## Governing Law
|
||||
|
||||
The laws of the Country, excluding its conflicts of law rules, shall govern this Terms and Your use of the Service. Your use of the Application may also be subject to other local, state, national, or international laws.
|
||||
|
||||
## Disputes Resolution
|
||||
|
||||
If You have any concern or dispute about the Service, You agree to first try to resolve the dispute informally by contacting the Company.
|
||||
|
||||
## For European Union (EU) Users
|
||||
|
||||
If You are a European Union consumer, you will benefit from any mandatory provisions of the law of the country in which you are resident in.
|
||||
|
||||
## United States Legal Compliance
|
||||
|
||||
You represent and warrant that (i) You are not located in a country that is subject to the United States government embargo, or that has been designated by the United States government as a "terrorist supporting" country, and (ii) You are not listed on any United States government list of prohibited or restricted parties.
|
||||
|
||||
## Severability and Waiver
|
||||
|
||||
### Severability
|
||||
|
||||
If any provision of these Terms is held to be unenforceable or invalid, such provision will be changed and interpreted to accomplish the objectives of such provision to the greatest extent possible under applicable law and the remaining provisions will continue in full force and effect.
|
||||
|
||||
### Waiver
|
||||
|
||||
Except as provided herein, the failure to exercise a right or to require performance of an obligation under these Terms shall not effect a party's ability to exercise such right or require such performance at any time thereafter nor shall the waiver of a breach constitute a waiver of any subsequent breach.
|
||||
|
||||
## Translation Interpretation
|
||||
|
||||
These Terms and Conditions may have been translated if We have made them available to You on our Service. You agree that the original English text shall prevail in the case of a dispute.
|
||||
|
||||
## Changes to These Terms and Conditions
|
||||
|
||||
We reserve the right, at Our sole discretion, to modify or replace these Terms at any time. If a revision is material We will make reasonable efforts to provide at least 30 days' notice prior to any new terms taking effect. What constitutes a material change will be determined at Our sole discretion.
|
||||
|
||||
By continuing to access or use Our Service after those revisions become effective, You agree to be bound by the revised terms. If You do not agree to the new terms, in whole or in part, please stop using the website and the Service.
|
||||
|
||||
## Contact Us
|
||||
|
||||
If you have any questions about these Terms and Conditions, You can contact us:
|
||||
|
||||
- By email: somecoolemail@domain.com
|
37
src/types.ts
Normal file
@ -0,0 +1,37 @@
|
||||
export interface Post {
|
||||
id: string;
|
||||
slug: string;
|
||||
|
||||
publishDate: Date;
|
||||
title: string;
|
||||
description?: string;
|
||||
|
||||
image?: string;
|
||||
|
||||
canonical?: string | URL;
|
||||
permalink?: string;
|
||||
|
||||
draft?: boolean;
|
||||
|
||||
excerpt?: string;
|
||||
category?: string;
|
||||
tags?: Array<string>;
|
||||
author?: string;
|
||||
|
||||
Content: unknown;
|
||||
content?: string;
|
||||
readingTime: number;
|
||||
}
|
||||
|
||||
export interface MetaSEO {
|
||||
title?: string;
|
||||
description?: string;
|
||||
image?: string;
|
||||
|
||||
canonical?: string | URL;
|
||||
noindex?: boolean;
|
||||
nofollow?: boolean;
|
||||
|
||||
ogTitle?: string;
|
||||
ogType?: string;
|
||||
}
|
18
src/utils/directories.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
/** */
|
||||
export const getProjectRootDir = (): string => {
|
||||
const mode = import.meta.env.MODE;
|
||||
|
||||
return mode === 'production' ? path.join(__dirname, '../') : path.join(__dirname, '../../');
|
||||
};
|
||||
|
||||
const __srcFolder = path.join(getProjectRootDir(), '/src');
|
||||
|
||||
/** */
|
||||
export const getRelativeUrlByFilePath = (filepath: string): string => {
|
||||
return filepath.replace(__srcFolder, '');
|
||||
};
|
11
src/utils/frontmatter.mjs
Normal file
@ -0,0 +1,11 @@
|
||||
import getReadingTime from 'reading-time';
|
||||
import { toString } from 'mdast-util-to-string';
|
||||
|
||||
export function remarkReadingTime() {
|
||||
return function (tree, { data }) {
|
||||
const text = toString(tree);
|
||||
const readingTime = Math.ceil(getReadingTime(text).minutes);
|
||||
|
||||
data.astro.frontmatter.readingTime = readingTime;
|
||||
};
|
||||
}
|
37
src/utils/images.ts
Normal file
@ -0,0 +1,37 @@
|
||||
const load = async function () {
|
||||
let images: Record<string, () => Promise<unknown>> | undefined = undefined;
|
||||
try {
|
||||
images = import.meta.glob('~/assets/images/**');
|
||||
} catch (e) {
|
||||
// continue regardless of error
|
||||
}
|
||||
return images;
|
||||
};
|
||||
|
||||
let _images;
|
||||
|
||||
/** */
|
||||
export const fetchLocalImages = async () => {
|
||||
_images = _images || load();
|
||||
return await _images;
|
||||
};
|
||||
|
||||
/** */
|
||||
export const findImage = async (imagePath?: string) => {
|
||||
if (typeof imagePath !== 'string') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
if (!imagePath.startsWith('~/assets')) {
|
||||
return null;
|
||||
} // For now only consume images using ~/assets alias (or absolute)
|
||||
|
||||
const images = await fetchLocalImages();
|
||||
const key = imagePath.replace('~/', '/src/');
|
||||
|
||||
return typeof images[key] === 'function' ? (await images[key]())['default'] : null;
|
||||
};
|
44
src/utils/permalinks.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import slugify from 'limax';
|
||||
|
||||
import { SITE } from '~/config.mjs';
|
||||
import { trim } from '~/utils/utils';
|
||||
|
||||
const trimSlash = (s: string) => trim(trim(s, '/'));
|
||||
const createPath = (...params: string[]) => {
|
||||
const paths = params
|
||||
.map((el) => trimSlash(el))
|
||||
.filter((el) => !!el)
|
||||
.join('/');
|
||||
return '/' + paths + (SITE.trailingSlash && paths ? '/' : '');
|
||||
};
|
||||
|
||||
const BASE_PATHNAME = SITE.basePathname;
|
||||
|
||||
export const cleanSlug = (text = '') =>
|
||||
trimSlash(text)
|
||||
.split('/')
|
||||
.map((slug) => slugify(slug))
|
||||
.join('/');
|
||||
|
||||
/** */
|
||||
export const getCanonical = (path = ''): string | URL => new URL(path, SITE.origin);
|
||||
|
||||
/** */
|
||||
export const getPermalink = (slug = '', type = 'page'): string => {
|
||||
let permalink: string;
|
||||
|
||||
switch (type) {
|
||||
case 'page':
|
||||
default:
|
||||
permalink = createPath(slug);
|
||||
break;
|
||||
}
|
||||
|
||||
return definitivePermalink(permalink);
|
||||
};
|
||||
|
||||
/** */
|
||||
export const getHomePermalink = (): string => getPermalink('/');
|
||||
|
||||
/** */
|
||||
const definitivePermalink = (permalink: string): string => createPath(BASE_PATHNAME, permalink);
|
21
src/utils/utils.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { DATE_FORMATTER } from '~/config.mjs';
|
||||
|
||||
const formatter =
|
||||
DATE_FORMATTER ||
|
||||
new Intl.DateTimeFormat('en', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
});
|
||||
|
||||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
export const getFormattedDate = (date: Date) => (date ? formatter.format(date) : '');
|
||||
|
||||
export const trim = (str = '', ch?: string) => {
|
||||
let start = 0,
|
||||
end = str.length || 0;
|
||||
while (start < end && str[start] === ch) ++start;
|
||||
while (end > start && str[end - 1] === ch) --end;
|
||||
return start > 0 || end < str.length ? str.substring(start, end) : str;
|
||||
};
|
19
tailwind.config.cjs
Normal file
@ -0,0 +1,19 @@
|
||||
const defaultTheme = require('tailwindcss/defaultTheme');
|
||||
const colors = require('tailwindcss/colors');
|
||||
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}'],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: colors.slate,
|
||||
secondary: colors.yellow,
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ["'InterVariable'", ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography')],
|
||||
darkMode: 'class',
|
||||
};
|
11
tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"strictNullChecks": true,
|
||||
"allowJs": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["src/*"]
|
||||
}
|
||||
}
|
||||
}
|