feat: initial landing page (#89)
* create basic landing page * change name in package.json
2
landing-page/.npmrc
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Expose Astro dependencies for `pnpm` users
|
||||
shamefully-hoist=true
|
2
landing-page/.prettierignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
dist/
|
||||
.output/
|
1
landing-page/.prettierrc.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
4
landing-page/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"recommendations": ["astro-build.astro-vscode", "bradlc.vscode-tailwindcss"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
11
landing-page/.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
9
landing-page/LICENSE
Normal file
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022-present Markus Hsi-Yang Fritz
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
49
landing-page/README.md
Normal file
|
@ -0,0 +1,49 @@
|
|||
# Welcome to [Astro](https://astro.build)
|
||||
|
||||
[](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||

|
||||
|
||||
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro project, you'll see the following folders and files:
|
||||
|
||||
```
|
||||
/
|
||||
├── public/
|
||||
│ └── favicon.svg
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ │ └── Card.astro
|
||||
│ ├── layouts/
|
||||
│ │ └── Layout.astro
|
||||
│ └── pages/
|
||||
│ └── index.astro
|
||||
└── package.json
|
||||
```
|
||||
|
||||
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||
|
||||
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||
|
||||
Any static assets, like images, can be placed in the `public/` directory.
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :--------------------- | :------------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:3000` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
|
||||
| `npm run astro --help` | Get help using the Astro CLI |
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
12
landing-page/astro.config.mjs
Normal file
|
@ -0,0 +1,12 @@
|
|||
import tailwind from "@astrojs/tailwind";
|
||||
import { defineConfig } from "astro/config";
|
||||
|
||||
export default defineConfig({
|
||||
site: "https://astro-moon-landing.netlify.app/",
|
||||
integrations: [tailwind()],
|
||||
vite: {
|
||||
ssr: {
|
||||
external: ["@11ty/eleventy-img", "svgo"],
|
||||
},
|
||||
},
|
||||
});
|
14593
landing-page/package-lock.json
generated
Normal file
29
landing-page/package.json
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "astro-reactive-library-landing-page",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"format": "prettier --write .",
|
||||
"clean": "rimraf dist node_modules"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/tailwind": "^2.0.2",
|
||||
"@types/micromodal": "^0.3.3",
|
||||
"astro": "^1.4.7",
|
||||
"astro-eleventy-img": "^0.5.0",
|
||||
"astro-icon": "^0.7.3",
|
||||
"prettier": "2.7.1",
|
||||
"prettier-plugin-astro": "^0.5.5",
|
||||
"rimraf": "^3.0.2",
|
||||
"tailwindcss-fluid-type": "^1.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"micromodal": "^0.4.10",
|
||||
"tailwindcss": "^3.1.8"
|
||||
}
|
||||
}
|
BIN
landing-page/public/assets/images/astronaut/yLnzHhqALK-450.avif
Normal file
BIN
landing-page/public/assets/images/astronaut/yLnzHhqALK-450.png
Normal file
After Width: | Height: | Size: 117 KiB |
BIN
landing-page/public/assets/images/astronaut/yLnzHhqALK-450.webp
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
landing-page/public/assets/images/astronaut/yLnzHhqALK-800.avif
Normal file
BIN
landing-page/public/assets/images/astronaut/yLnzHhqALK-800.png
Normal file
After Width: | Height: | Size: 323 KiB |
BIN
landing-page/public/assets/images/astronaut/yLnzHhqALK-800.webp
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
landing-page/public/favicon.ico
Normal file
After Width: | Height: | Size: 4.2 KiB |
24
landing-page/public/favicon.svg
Normal file
|
@ -0,0 +1,24 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 627 894" fill="none">
|
||||
<style>
|
||||
#letter-a {
|
||||
fill: black;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#letter-a {
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<path
|
||||
id="letter-a"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M445.433 22.9832C452.722 32.0324 456.439 44.2432 463.873 68.6647L626.281 602.176C566.234 571.026 500.957 548.56 432.115 536.439L326.371 179.099C324.641 173.252 319.27 169.241 313.173 169.241C307.06 169.241 301.68 173.273 299.963 179.14L195.5 536.259C126.338 548.325 60.7632 570.832 0.459473 602.095L163.664 68.5412C171.121 44.1617 174.85 31.9718 182.14 22.9393C188.575 14.9651 196.946 8.77213 206.454 4.95048C217.224 0.621582 229.971 0.621582 255.466 0.621582H372.034C397.562 0.621582 410.326 0.621582 421.106 4.95951C430.622 8.78908 438.998 14.9946 445.433 22.9832Z"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M464.867 627.566C438.094 650.46 384.655 666.073 323.101 666.073C247.551 666.073 184.229 642.553 167.426 610.921C161.419 629.05 160.072 649.798 160.072 663.052C160.072 663.052 156.114 728.134 201.38 773.401C201.38 749.896 220.435 730.842 243.939 730.842C284.226 730.842 284.181 765.99 284.144 794.506C284.143 795.36 284.142 796.209 284.142 797.051C284.142 840.333 310.595 877.436 348.215 893.075C342.596 881.518 339.444 868.54 339.444 854.825C339.444 813.545 363.679 798.175 391.845 780.311C414.255 766.098 439.155 750.307 456.315 718.629C465.268 702.101 470.352 683.17 470.352 663.052C470.352 650.68 468.43 638.757 464.867 627.566Z"
|
||||
fill="#FF5D01"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
BIN
landing-page/src/assets/astronaut.png
Normal file
After Width: | Height: | Size: 147 KiB |
131
landing-page/src/components/header.astro
Normal file
|
@ -0,0 +1,131 @@
|
|||
---
|
||||
import { Icon } from "astro-icon";
|
||||
import ThemeSwitcher from "~/components/theme-switcher.astro";
|
||||
|
||||
const socials = [
|
||||
{
|
||||
name: "GitHub",
|
||||
url: "#",
|
||||
icon: "fa-brands:github-alt",
|
||||
},
|
||||
{
|
||||
name: "Discord",
|
||||
url: "#",
|
||||
icon: "fa-brands:discord",
|
||||
},
|
||||
{
|
||||
name: "Twitter",
|
||||
url: "#",
|
||||
icon: "fa-brands:twitter",
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
<header
|
||||
id="page-header"
|
||||
class="absolute bottom-0 z-10 flex items-center justify-between w-full px-8 py-4 text-white border-b border-transparent"
|
||||
>
|
||||
<a class="flex items-center gap-3 hover:!text-default" href="#">
|
||||
<h1 class="sr-only">Astro</h1>
|
||||
<Icon name="logomark" class="h-10" />
|
||||
<Icon name="wordmark" class="hidden h-4 sm:block" />
|
||||
</a>
|
||||
<div>
|
||||
<div class="flex items-center gap-6">
|
||||
<ul class="flex gap-4">
|
||||
{
|
||||
socials.map((social) => (
|
||||
<li class="list-none">
|
||||
<a
|
||||
class="flex items-center justify-center w-12 h-12 p-3 border-2 border-current rounded-full"
|
||||
href={social.url}
|
||||
>
|
||||
<Icon name={social.icon} class="h-full" />
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
<button
|
||||
id="open-nav-button"
|
||||
type="button"
|
||||
class="btn sm:hidden"
|
||||
aria-label="Navigation"
|
||||
>
|
||||
<Icon pack="mdi" name="menu" class="h-8" />
|
||||
</button>
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
<div id="menu-modal" class="hidden modal" aria-hidden="true">
|
||||
<div class="fixed inset-0 px-8 py-4 bg-default text-default">
|
||||
<div class="space-y-4" role="dialog" aria-modal="true">
|
||||
<header class="text-right">
|
||||
<button
|
||||
id="close-nav-button"
|
||||
type="button"
|
||||
class="btn"
|
||||
aria-label="Close navigation"
|
||||
>
|
||||
<Icon pack="mdi" name="close" class="h-8" />
|
||||
</button>
|
||||
</header>
|
||||
<div class="flex justify-center">
|
||||
<Icon name="logomark" class="h-16" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<script>
|
||||
import MicroModal from "micromodal";
|
||||
|
||||
const menuModalId = "menu-modal";
|
||||
|
||||
const header: HTMLElement = document.querySelector("#page-header");
|
||||
const page = document.documentElement;
|
||||
const menu = document.querySelector(`#${menuModalId} ul`);
|
||||
const openNavButton = document.querySelector("#open-nav-button");
|
||||
const closeNavButton = document.querySelector("#close-nav-button");
|
||||
|
||||
const openMenu = () => {
|
||||
MicroModal.show(menuModalId, { disableScroll: true });
|
||||
};
|
||||
|
||||
const closeMenu = () => {
|
||||
MicroModal.close(menuModalId);
|
||||
};
|
||||
|
||||
openNavButton.addEventListener("click", openMenu);
|
||||
closeNavButton.addEventListener("click", closeMenu);
|
||||
|
||||
document.addEventListener("scroll", () => {
|
||||
const d = page.clientHeight - page.scrollTop - header.offsetHeight;
|
||||
header.classList.toggle("fixed-header", d < 0);
|
||||
});
|
||||
|
||||
menu.addEventListener("click", (event) => {
|
||||
if ((event.target as HTMLElement).tagName === "A") {
|
||||
closeMenu();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<style>
|
||||
#open-nav-button {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
|
||||
<style>
|
||||
.fixed-header {
|
||||
@apply fixed top-0 bottom-auto;
|
||||
@apply text-default bg-default border-default;
|
||||
}
|
||||
.modal.is-open {
|
||||
@apply block;
|
||||
}
|
||||
</style>
|
89
landing-page/src/components/splash.astro
Normal file
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
import { generateImage } from "astro-eleventy-img";
|
||||
import { Icon } from "astro-icon";
|
||||
import Starfield from "~/components/starfield.astro";
|
||||
|
||||
const widths = [450, 800];
|
||||
const sizes = "(min-width: 640px) 42vw, 67vw";
|
||||
|
||||
const { webp, avif, png } = generateImage("src/assets/astronaut.png", {
|
||||
widths,
|
||||
formats: ["webp", "avif", "png"],
|
||||
outputDir: "public/assets/images/astronaut",
|
||||
urlPath: "/assets/images/astronaut",
|
||||
});
|
||||
|
||||
const avifSrcset = avif.map(({ srcset }) => srcset).join(",");
|
||||
const webpSrcset = webp.map(({ srcset }) => srcset).join(",");
|
||||
const pngSrcset = png.map(({ srcset }) => srcset).join(",");
|
||||
---
|
||||
|
||||
<section class="relative h-full bg-black">
|
||||
<Starfield />
|
||||
<div class="relative grid h-full sm:grid-cols-2 place-items-center">
|
||||
<h2
|
||||
class="flex flex-col self-end gap-2 sm:gap-4 sm:self-auto sm:justify-self-end"
|
||||
>
|
||||
<Icon name="logomark" class="h-24 text-white md:h-32" />
|
||||
<div
|
||||
class="font-extrabold tracking-tighter text-center text-8xl gradient-text"
|
||||
>
|
||||
Build fast
|
||||
<br /> websites,
|
||||
<br /> faster.
|
||||
</div>
|
||||
</h2>
|
||||
<picture
|
||||
class="self-start w-2/3 max-w-3xl sm:w-10/12 sm:self-auto sm:justify-self-start"
|
||||
>
|
||||
<source type="image/avif" srcset={avifSrcset} sizes={sizes} />
|
||||
<source type="image/webp" srcset={webpSrcset} sizes={sizes} />
|
||||
<source type="image/png" srcset={pngSrcset} sizes={sizes} />
|
||||
<img
|
||||
class="object-cover w-full h-full"
|
||||
src={png[0].url}
|
||||
width={png[0].width}
|
||||
height={png[0].height}
|
||||
alt="A floating astronaut in a space suit"
|
||||
/>
|
||||
</picture>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<noscript>
|
||||
<style>
|
||||
#splash-bg-fallback {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
|
||||
<style>
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate3d(0, 30px, 0);
|
||||
}
|
||||
}
|
||||
|
||||
picture {
|
||||
animation: float linear 2.5s infinite alternate;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
picture {
|
||||
@apply animate-none;
|
||||
}
|
||||
|
||||
:global(#starfield) {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
#splash-bg-fallback {
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
</style>
|
97
landing-page/src/components/starfield.astro
Normal file
|
@ -0,0 +1,97 @@
|
|||
<div id="starfield" class="absolute inset-0">
|
||||
<canvas id="starfield-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const COUNT = 800;
|
||||
const SPEED = 0.1;
|
||||
|
||||
class Star {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
xPrev: number;
|
||||
yPrev: number;
|
||||
|
||||
constructor(x = 0, y = 0, z = 0) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.xPrev = x;
|
||||
this.yPrev = y;
|
||||
}
|
||||
|
||||
update(width: number, height: number, speed: number) {
|
||||
this.xPrev = this.x;
|
||||
this.yPrev = this.y;
|
||||
this.z += speed * 0.0675;
|
||||
this.x += this.x * (speed * 0.0225) * this.z;
|
||||
this.y += this.y * (speed * 0.0225) * this.z;
|
||||
if (
|
||||
this.x > width / 2 ||
|
||||
this.x < -width / 2 ||
|
||||
this.y > height / 2 ||
|
||||
this.y < -height / 2
|
||||
) {
|
||||
this.x = Math.random() * width - width / 2;
|
||||
this.y = Math.random() * height - height / 2;
|
||||
this.xPrev = this.x;
|
||||
this.yPrev = this.y;
|
||||
this.z = 0;
|
||||
}
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D) {
|
||||
ctx.lineWidth = this.z;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.x, this.y);
|
||||
ctx.lineTo(this.xPrev, this.yPrev);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
const stars = Array.from({ length: COUNT }, () => new Star(0, 0, 0));
|
||||
let rafId = 0;
|
||||
|
||||
const canvas: HTMLCanvasElement = document.querySelector("#starfield-canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const container = document.querySelector("#starfield");
|
||||
const resizeObserver = new ResizeObserver(setup);
|
||||
resizeObserver.observe(container);
|
||||
|
||||
function setup() {
|
||||
rafId > 0 && cancelAnimationFrame(rafId);
|
||||
const { clientWidth: width, clientHeight: height } = container;
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
canvas.width = width * dpr;
|
||||
canvas.height = height * dpr;
|
||||
canvas.style.width = `${width}px`;
|
||||
canvas.style.height = `${height}px`;
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
for (const star of stars) {
|
||||
star.x = Math.random() * width - width / 2;
|
||||
star.y = Math.random() * height - height / 2;
|
||||
star.z = 0;
|
||||
}
|
||||
|
||||
ctx.translate(width / 2, height / 2);
|
||||
ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
|
||||
ctx.strokeStyle = "white";
|
||||
rafId = requestAnimationFrame(frame);
|
||||
}
|
||||
|
||||
function frame() {
|
||||
const { clientWidth: width, clientHeight: height } = container;
|
||||
|
||||
for (const star of stars) {
|
||||
star.update(width, height, SPEED);
|
||||
star.draw(ctx);
|
||||
}
|
||||
|
||||
ctx.fillRect(-width / 2, -height / 2, width, height);
|
||||
rafId = requestAnimationFrame(frame);
|
||||
}
|
||||
</script>
|
60
landing-page/src/components/theme-switcher.astro
Normal file
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
import { Icon } from "astro-icon";
|
||||
---
|
||||
|
||||
<!--
|
||||
negative margin is sum of button width (8) and gap size of flex parent (6)
|
||||
TODO don't hardcode these values
|
||||
-->
|
||||
<button
|
||||
id="theme-switcher"
|
||||
type="button"
|
||||
class="scale-0 transition-all origin-[right_center] duration-500 -ml-14"
|
||||
>
|
||||
<div id="icon-theme-light">
|
||||
<Icon name="theme/light" class="h-8" />
|
||||
<span class="sr-only">Use light theme</span>
|
||||
</div>
|
||||
<div id="icon-theme-dark" class="hidden">
|
||||
<Icon name="theme/dark" class="h-8" />
|
||||
<span class="sr-only">Use dark theme</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
const themes = ["light", "dark"];
|
||||
const button = document.querySelector("#theme-switcher");
|
||||
|
||||
const getThemeCurrent = () => document.documentElement.dataset.theme;
|
||||
const getThemeNext = () => {
|
||||
const themeCurrent = getThemeCurrent();
|
||||
const indexThemeCurrent = themes.indexOf(themeCurrent);
|
||||
return themes[(indexThemeCurrent + 1) % themes.length];
|
||||
};
|
||||
|
||||
const updateIcon = () => {
|
||||
const themeCurrent = getThemeCurrent();
|
||||
document
|
||||
.querySelector(`#icon-theme-${themeCurrent}`)
|
||||
.classList.add("hidden");
|
||||
const themeNext = getThemeNext();
|
||||
document
|
||||
.querySelector(`#icon-theme-${themeNext}`)
|
||||
.classList.remove("hidden");
|
||||
};
|
||||
|
||||
button.addEventListener("click", () => {
|
||||
const themeNext = getThemeNext();
|
||||
document.documentElement.dataset.theme = themeNext;
|
||||
localStorage.setItem("theme", themeNext);
|
||||
updateIcon();
|
||||
});
|
||||
|
||||
updateIcon();
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:global(.fixed-header) #theme-switcher {
|
||||
@apply scale-100 ml-0;
|
||||
}
|
||||
</style>
|
1
landing-page/src/env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/// <reference types="astro/client" />
|
14
landing-page/src/icons/logomark.svg
Normal file
|
@ -0,0 +1,14 @@
|
|||
<svg viewBox="0 0 627 894" fill="none">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M445.433 22.9832C452.722 32.0324 456.439 44.2432 463.873 68.6647L626.281 602.176C566.234 571.026 500.957 548.56 432.115 536.439L326.371 179.099C324.641 173.252 319.27 169.241 313.173 169.241C307.06 169.241 301.68 173.273 299.963 179.14L195.5 536.259C126.338 548.325 60.7632 570.832 0.459473 602.095L163.664 68.5412C171.121 44.1617 174.85 31.9718 182.14 22.9393C188.575 14.9651 196.946 8.77213 206.454 4.95048C217.224 0.621582 229.971 0.621582 255.466 0.621582H372.034C397.562 0.621582 410.326 0.621582 421.106 4.95951C430.622 8.78908 438.998 14.9946 445.433 22.9832Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M464.867 627.566C438.094 650.46 384.655 666.073 323.101 666.073C247.551 666.073 184.229 642.553 167.426 610.921C161.419 629.05 160.072 649.798 160.072 663.052C160.072 663.052 156.114 728.134 201.38 773.401C201.38 749.896 220.435 730.842 243.939 730.842C284.226 730.842 284.181 765.99 284.144 794.506C284.143 795.36 284.142 796.209 284.142 797.051C284.142 840.333 310.595 877.436 348.215 893.075C342.596 881.518 339.444 868.54 339.444 854.825C339.444 813.545 363.679 798.175 391.845 780.311C414.255 766.098 439.155 750.307 456.315 718.629C465.268 702.101 470.352 683.17 470.352 663.052C470.352 650.68 468.43 638.757 464.867 627.566Z"
|
||||
fill="#FF5D01"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
161
landing-page/src/icons/theme/dark.svg
Normal file
|
@ -0,0 +1,161 @@
|
|||
<!-- source: https://github.com/basmilius/weather-icons -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="a"
|
||||
x1="54.33"
|
||||
y1="29.03"
|
||||
x2="187.18"
|
||||
y2="259.13"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stop-color="currentColor" />
|
||||
<stop offset="0.45" stop-color="currentColor" />
|
||||
<stop offset="1" stop-color="currentColor" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="b"
|
||||
x1="294"
|
||||
y1="112.82"
|
||||
x2="330"
|
||||
y2="175.18"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stop-color="currentColor" />
|
||||
<stop offset="0.45" stop-color="currentColor" />
|
||||
<stop offset="1" stop-color="currentColor" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="c"
|
||||
x1="295.52"
|
||||
y1="185.86"
|
||||
x2="316.48"
|
||||
y2="222.14"
|
||||
xlink:href="#b"
|
||||
/>
|
||||
<linearGradient
|
||||
id="d"
|
||||
x1="356.29"
|
||||
y1="194.78"
|
||||
x2="387.71"
|
||||
y2="249.22"
|
||||
xlink:href="#b"
|
||||
/>
|
||||
<symbol id="e" viewBox="0 0 270 270" overflow="visible">
|
||||
<!-- moon -->
|
||||
<path
|
||||
d="M252.25,168.63C178.13,168.63,118,109.35,118,36.21A130.48,130.48,0,0,1,122.47,3C55.29,10.25,3,66.37,3,134.58,3,207.71,63.09,267,137.21,267,199.69,267,252,224.82,267,167.79A135.56,135.56,0,0,1,252.25,168.63Z"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="6"
|
||||
fill="url(#a)"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
additive="sum"
|
||||
type="rotate"
|
||||
values="-15 135 135; 9 135 135; -15 135 135"
|
||||
dur="6s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</path>
|
||||
</symbol>
|
||||
</defs>
|
||||
|
||||
<!-- star-1 -->
|
||||
<path
|
||||
d="M282.83,162.84l24.93-6.42a1.78,1.78,0,0,1,1.71.46l18.37,18a1.8,1.8,0,0,0,3-1.73l-6.42-24.93a1.78,1.78,0,0,1,.46-1.71l18-18.37a1.8,1.8,0,0,0-1.73-3l-24.93,6.42a1.78,1.78,0,0,1-1.71-.46l-18.37-18a1.8,1.8,0,0,0-3,1.73l6.42,24.93a1.78,1.78,0,0,1-.46,1.71l-18,18.37A1.8,1.8,0,0,0,282.83,162.84Z"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
fill="url(#b)"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
additive="sum"
|
||||
type="rotate"
|
||||
values="-15 312 144; 15 312 144; -15 312 144"
|
||||
dur="6s"
|
||||
calcMode="spline"
|
||||
keySplines=".42, 0, .58, 1; .42, 0, .58, 1"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1; .75; 1; .75; 1; .75; 1"
|
||||
dur="6s"
|
||||
/>
|
||||
</path>
|
||||
|
||||
<!-- star-2 -->
|
||||
<path
|
||||
d="M285.4,193.44l12,12.25a1.19,1.19,0,0,1,.3,1.14l-4.28,16.62a1.2,1.2,0,0,0,2,1.15l12.25-12a1.19,1.19,0,0,1,1.14-.3l16.62,4.28a1.2,1.2,0,0,0,1.15-2l-12-12.25a1.19,1.19,0,0,1-.3-1.14l4.28-16.62a1.2,1.2,0,0,0-2-1.15l-12.25,12a1.19,1.19,0,0,1-1.14.3l-16.62-4.28A1.2,1.2,0,0,0,285.4,193.44Z"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
fill="url(#c)"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
additive="sum"
|
||||
type="rotate"
|
||||
values="-15 306 204; 15 306 204; -15 306 204"
|
||||
begin="-.33s"
|
||||
dur="6s"
|
||||
calcMode="spline"
|
||||
keySplines=".42, 0, .58, 1; .42, 0, .58, 1"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1; .75; 1; .75; 1; .75; 1"
|
||||
begin="-.33s"
|
||||
dur="6s"
|
||||
/>
|
||||
</path>
|
||||
|
||||
<!-- star-3 -->
|
||||
<path
|
||||
d="M337.32,223.73l24.8,6.9a1.83,1.83,0,0,1,1.25,1.25l6.9,24.8a1.79,1.79,0,0,0,3.46,0l6.9-24.8a1.83,1.83,0,0,1,1.25-1.25l24.8-6.9a1.79,1.79,0,0,0,0-3.46l-24.8-6.9a1.83,1.83,0,0,1-1.25-1.25l-6.9-24.8a1.79,1.79,0,0,0-3.46,0l-6.9,24.8a1.83,1.83,0,0,1-1.25,1.25l-24.8,6.9A1.79,1.79,0,0,0,337.32,223.73Z"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
fill="url(#d)"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
additive="sum"
|
||||
type="rotate"
|
||||
values="-15 372 222; 15 372 222; -15 372 222"
|
||||
begin="-.67s"
|
||||
dur="6s"
|
||||
calcMode="spline"
|
||||
keySplines=".42, 0, .58, 1; .42, 0, .58, 1"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1; .75; 1; .75; 1; .75; 1"
|
||||
begin="-.67s"
|
||||
dur="6s"
|
||||
/>
|
||||
</path>
|
||||
|
||||
<use
|
||||
width="270"
|
||||
height="270"
|
||||
transform="translate(121 121)"
|
||||
xlink:href="#e"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.4 KiB |
53
landing-page/src/icons/theme/light.svg
Normal file
|
@ -0,0 +1,53 @@
|
|||
<!-- source: https://github.com/basmilius/weather-icons -->
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="a"
|
||||
x1="149.99"
|
||||
y1="119.24"
|
||||
x2="234.01"
|
||||
y2="264.76"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stop-color="currentColor" />
|
||||
<stop offset="0.45" stop-color="currentColor" />
|
||||
<stop offset="1" stop-color="currentColor" />
|
||||
</linearGradient>
|
||||
<symbol id="b" viewBox="0 0 384 384">
|
||||
<!-- core -->
|
||||
<circle
|
||||
cx="192"
|
||||
cy="192"
|
||||
r="84"
|
||||
stroke="currentColor"
|
||||
stroke-miterlimit="10"
|
||||
stroke-width="6"
|
||||
fill="url(#a)"
|
||||
/>
|
||||
|
||||
<!-- rays -->
|
||||
<path
|
||||
d="M192,61.66V12m0,360V322.34M284.17,99.83l35.11-35.11M64.72,319.28l35.11-35.11m0-184.34L64.72,64.72M319.28,319.28l-35.11-35.11M61.66,192H12m360,0H322.34"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-miterlimit="10"
|
||||
stroke-width="24"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
additive="sum"
|
||||
type="rotate"
|
||||
values="0 192 192; 45 192 192"
|
||||
dur="6s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</path>
|
||||
</symbol>
|
||||
</defs>
|
||||
<use width="384" height="384" transform="translate(64 64)" xlink:href="#b" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
22
landing-page/src/icons/wordmark.svg
Normal file
|
@ -0,0 +1,22 @@
|
|||
<svg viewBox="0 0 1847 457" fill="none">
|
||||
<path
|
||||
d="M134.148 456.833C202.08 456.833 253.03 432.665 273.93 391.516C273.93 411.111 275.23 431.36 278.5 447.036H390.19C384.97 424.173 382.36 392.82 382.36 351.671V251.081C382.36 155.717 326.18 110.648 201.43 110.648C92.3441 110.648 19.188 155.717 10.697 229.527H126.963C130.882 197.521 157.66 179.885 201.43 179.885C244.53 179.885 268.7 197.521 268.7 234.1V243.896L150.48 254.347C92.997 260.227 60.338 270.023 37.477 285.7C13.31 302.028 0.898987 326.851 0.898987 357.549C0.898987 418.948 51.847 456.833 134.148 456.833ZM177.26 388.902C139.37 388.902 116.512 373.88 116.512 349.712C116.512 324.892 135.45 311.827 183.14 305.949L270.66 296.805V316.4C270.66 360.163 232.78 388.902 177.26 388.902Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M625.77 456.833C739.43 456.833 797.56 414.377 797.56 345.793C797.56 288.966 764.9 257.613 685.87 247.162L587.23 236.059C559.15 232.138 547.39 224.953 547.39 209.277C547.39 190.336 566.33 181.844 609.44 181.844C668.88 181.844 710.03 195.561 743.35 222.342L796.25 169.434C759.67 131.55 696.32 110.648 617.94 110.648C507.55 110.648 446.15 149.838 446.15 215.809C446.15 273.289 484.03 305.295 562.41 315.745L651.25 326.196C686.52 330.769 696.97 337.302 696.97 354.283C696.97 373.88 677.37 384.331 631.65 384.331C563.72 384.331 518 366.041 487.3 332.076L427.21 381.717C467.05 431.36 534.98 456.833 625.77 456.833Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M889.78 194.255V332.076C889.78 413.07 935.5 454.221 1033.48 454.221C1063.53 454.221 1087.04 450.955 1109.25 444.423V359.508C1097.49 362.122 1083.12 364.734 1064.18 364.734C1023.03 364.734 1002.78 346.446 1002.78 307.908V194.255H1109.9V120.445H1002.78V0.914001L889.78 42.717V120.445H816.62V194.255H889.78Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M1272.13 120.445H1168.93V447.036H1281.93V324.892C1281.93 289.618 1289.77 257.613 1311.33 237.364C1328.31 221.687 1352.48 213.198 1386.44 213.198C1398.85 213.198 1408.65 214.502 1419.75 215.809V113.26C1412.57 111.954 1407.34 111.954 1398.2 111.954C1333.53 111.954 1289.77 149.185 1272.13 209.932V120.445Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M1643.05 456.833C1762.59 456.833 1846.85 393.475 1846.85 283.088C1846.85 173.353 1762.59 110.648 1643.05 110.648C1522.87 110.648 1438.61 173.353 1438.61 283.088C1438.61 393.475 1522.87 456.833 1643.05 456.833ZM1643.05 381.717C1588.19 381.717 1553.57 346.446 1553.57 283.088C1553.57 219.728 1588.19 185.763 1643.05 185.763C1697.27 185.763 1731.89 219.728 1731.89 283.088C1731.89 346.446 1697.27 381.717 1643.05 381.717Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
83
landing-page/src/pages/index.astro
Normal file
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
import Header from "~/components/header.astro";
|
||||
import Splash from "~/components/splash.astro";
|
||||
import "~/styles/index.css";
|
||||
|
||||
const { site } = Astro;
|
||||
const image = new URL("social.jpg", site);
|
||||
const description =
|
||||
"Build fast websites, faster. Astro is a new kind of site builder for the modern web. Lightning-fast performance meets powerful developer experience.";
|
||||
---
|
||||
|
||||
<html lang="en" class="h-full motion-safe:scroll-smooth" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
|
||||
<title>Astro Reactive Library</title>
|
||||
<meta name="description" content={description} />
|
||||
|
||||
<!-- fonts -->
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
rel="preload"
|
||||
as="style"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;800&display=swap"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;800&display=swap"
|
||||
media="print"
|
||||
onload="this.media='all'"
|
||||
/>
|
||||
<noscript>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;800&display=swap"
|
||||
/>
|
||||
</noscript>
|
||||
|
||||
<!-- social media -->
|
||||
<meta property="og:title" content="Astro" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:image" content={image} />
|
||||
<meta property="og:url" content={site} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
|
||||
<!-- initialize theme -->
|
||||
<script is:inline>
|
||||
const themeSaved = localStorage.getItem("theme");
|
||||
|
||||
if (themeSaved) {
|
||||
document.documentElement.dataset.theme = themeSaved;
|
||||
} else {
|
||||
const prefersDark = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)"
|
||||
).matches;
|
||||
document.documentElement.dataset.theme = prefersDark ? "dark" : "light";
|
||||
}
|
||||
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", (event) => {
|
||||
if (!localStorage.getItem("theme")) {
|
||||
document.documentElement.dataset.theme = event.matches
|
||||
? "dark"
|
||||
: "light";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body
|
||||
class="h-full overflow-x-hidden text-base bg-default text-default selection:bg-secondary selection:text-white"
|
||||
>
|
||||
<Header />
|
||||
<Splash />
|
||||
<div class="h-screen">
|
||||
<h1>This section is added just to test the sticky header</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
12
landing-page/src/styles/global.css
Normal file
|
@ -0,0 +1,12 @@
|
|||
/* TODO: consider creating <Button> and <GradientText> components */
|
||||
a,
|
||||
.btn {
|
||||
@apply transition-colors duration-200;
|
||||
@apply hover:text-secondary;
|
||||
@apply focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-transparent focus:ring-secondary;
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
@apply text-transparent bg-clip-text;
|
||||
@apply bg-gradient-to-br from-indigo-500 via-fuchsia-500 to-pink-500;
|
||||
}
|
2
landing-page/src/styles/index.css
Normal file
|
@ -0,0 +1,2 @@
|
|||
@import "global.css";
|
||||
@import "theme.css";
|
19
landing-page/src/styles/theme.css
Normal file
|
@ -0,0 +1,19 @@
|
|||
[data-theme="light"] {
|
||||
--color-primary: theme("colors.pink.500");
|
||||
--color-secondary: theme("colors.indigo.500");
|
||||
--color-text: theme("colors.gray.900");
|
||||
--color-text-offset: theme("colors.gray.600");
|
||||
--color-background: theme("colors.gray.50");
|
||||
--color-background-offset: theme("colors.gray.100");
|
||||
--color-border: theme("colors.gray.900" / 10%);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--color-primary: theme("colors.pink.400");
|
||||
--color-secondary: theme("colors.indigo.400");
|
||||
--color-text: theme("colors.gray.50");
|
||||
--color-text-offset: theme("colors.gray.400");
|
||||
--color-background: theme("colors.gray.900");
|
||||
--color-background-offset: theme("colors.gray.800");
|
||||
--color-border: theme("colors.gray.50" / 10%);
|
||||
}
|
31
landing-page/tailwind.config.cjs
Normal file
|
@ -0,0 +1,31 @@
|
|||
const defaultTheme = require("tailwindcss/defaultTheme");
|
||||
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{astro,html,js,jsx,svelte,ts,tsx,vue}"],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ["Inter", ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
colors: {
|
||||
primary: "var(--color-primary)",
|
||||
secondary: "var(--color-secondary)",
|
||||
},
|
||||
textColor: {
|
||||
default: "var(--color-text)",
|
||||
offset: "var(--color-text-offset)",
|
||||
},
|
||||
backgroundColor: {
|
||||
default: "var(--color-background)",
|
||||
offset: "var(--color-background-offset)",
|
||||
},
|
||||
borderColor: {
|
||||
default: "var(--color-border)",
|
||||
},
|
||||
},
|
||||
},
|
||||
corePlugins: {
|
||||
fontSize: false,
|
||||
},
|
||||
plugins: [require("tailwindcss-fluid-type")],
|
||||
};
|
20
landing-page/tsconfig.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Enable top-level await, and other modern ESM features.
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
// Enable node-style module resolution, for things like npm package imports.
|
||||
"moduleResolution": "node",
|
||||
// Enable JSON imports.
|
||||
"resolveJsonModule": true,
|
||||
// Enable stricter transpilation for better output.
|
||||
"isolatedModules": true,
|
||||
// Astro will directly run your TypeScript code, no transpilation needed.
|
||||
"noEmit": true,
|
||||
// Alias `src/` to `~`
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["src/*"]
|
||||
}
|
||||
}
|
||||
}
|