This commit is contained in:
Enrico Bühler 2024-06-27 10:21:45 +02:00
parent 698b347ccb
commit 2cb10d475c
28 changed files with 754 additions and 0 deletions

24
web/.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
.idea/

4
web/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

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

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

54
web/README.md Normal file
View File

@ -0,0 +1,54 @@
# Astro Starter Kit: Basics
```sh
npm create astro@latest -- --template basics
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554)
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── 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:4321` |
| `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 check` |
| `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
web/astro.config.mjs Normal file
View File

@ -0,0 +1,12 @@
import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";
// https://astro.build/config
export default defineConfig({
integrations: [
tailwind({
applyBaseStyles: false,
}),
],
});

BIN
web/bun.lockb Executable file

Binary file not shown.

28
web/package.json Normal file
View File

@ -0,0 +1,28 @@
{
"name": "web",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.7.0",
"@astrojs/tailwind": "^5.1.0",
"astro": "^4.11.3",
"autoprefixer": "^10.4.19",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"marked": "^13.0.1",
"motion": "^10.18.0",
"postcss": "^8.4.38",
"postcss-custom-media": "^10.0.6",
"postcss-import": "^16.1.0",
"tailwind-merge": "^2.3.0",
"tailwindcss": "^3.4.4",
"typescript": "^5.5.2"
}
}

11
web/postcss.config.cjs Normal file
View File

@ -0,0 +1,11 @@
const postcssImport = require("postcss-import");
const nesting = require("tailwindcss/nesting");
const tailwind = require("tailwindcss");
const autoprefixer = require("autoprefixer");
const customMedia = require("postcss-custom-media");
const config = {
plugins: [postcssImport, nesting, tailwind, autoprefixer, customMedia],
};
module.exports = config;

9
web/public/favicon.svg Normal file
View File

@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

After

Width:  |  Height:  |  Size: 749 B

View File

@ -0,0 +1,46 @@
---
import type { HTMLAttributes } from "astro/types";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
export const button = cva(
cn(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
"transition-colors focus-visible:outline-none",
"ring-main focus-visible:ring-2 focus-visible:ring-ring",
"focus-visible:ring-offset-2 disabled:opacity-50",
"disabled:pointer-events-none ring-offset-background"
),
{
variants: {
variant: {
default: "bg-main text-neutral hocus:bg-main/80",
primary:
"bg-primary dark:bg-primary/50 text-neutral dark:text-main hocus:bg-primary/80 dark:hocus:bg-primary/80",
destructive:
"bg-error text-destructive-foreground hocus:bg-destructive/90",
outline:
"border border-input hover:bg-accent hover:text-accent-foreground",
secondary: "bg-main/10 text-main hocus:bg-main/20",
ghost: "hocus:bg-accent hocus:text-accent-foreground",
link: "underline-offset-4 hocus:underline text-primary",
},
size: {
default: "h-10 text-base py-2 px-4",
sm: "h-9 px-3 rounded-md",
lg: "h-11 px-8 rounded-md",
},
},
}
);
export interface Props
extends HTMLAttributes<"button">,
VariantProps<typeof button> {}
const { variant = "default", size = "default", ...props } = Astro.props;
---
<button {...props} class={button({ variant, size })}>
<slot />
</button>

View File

@ -0,0 +1,36 @@
---
import type { HTMLAttributes } from "astro/types";
import { type VariantProps, cva } from "class-variance-authority";
const card = cva(
"card rounded-card transition-colors bg-neutral-accent ring-2 ring-main/10",
{
variants: {
interactable: {
false: "transition-shadow focus-within:shadow-lg ring-2 ring-neutral",
true: "cursor-pointer shadow-sm hocus:bg-neutral-accent/50 \
hocus:shadow-lg hocus:ring-2 hocus:outline-none",
},
padding: {
true: "py-6 px-8",
false: "p-0",
},
},
}
);
export interface Props
extends HTMLAttributes<"div">,
VariantProps<typeof card> {}
const {
interactable = false,
padding = true,
class: _class,
...props
} = Astro.props;
---
<div {...props} class={card({ interactable, padding, class: _class })}>
<slot />
</div>

View File

@ -0,0 +1,52 @@
---
import { cn } from "@/lib/utils";
import type { HTMLAttributes } from "astro/types";
import { VariantProps, cva } from "class-variance-authority";
const heading = cva("max-w-3xl m-[var(--section-heading-margin)]", {
variants: {
main: {
true: "font-bold",
false: "font-semibold",
},
padding: {
true: "py-4",
false: "p-0",
},
},
});
export interface Props
extends HTMLAttributes<"h1" | "h2">,
VariantProps<typeof heading> {
subtitle?: string;
}
const { main = false, padding = true, subtitle } = Astro.props;
const titleClass = cn(
heading({ main, padding }),
subtitle ? "mb-[calc(var(--section_heading-margin-bottom)_/_2)]" : ""
);
---
<div class="z-0 relative">
{
main ? (
<h1 class={titleClass}>
<slot />
</h1>
) : (
<h2 class={titleClass}>
<slot />
</h2>
)
}
{
subtitle && (
<h3 class="font-normal m-[var(--section-heading-margin)] max-w-3xl">
{subtitle}
</h3>
)
}
</div>

View File

@ -0,0 +1,92 @@
---
import Section from "../Section.astro";
enum NavigationItemType {
Link,
Text,
}
type NavigationItem =
| {
path: string;
label: string;
type: NavigationItemType.Link;
}
| {
type: NavigationItemType.Text;
content: string;
};
type NavigationGroup = {
title: string;
class?: string;
items: Array<NavigationItem>;
};
const tree: Array<NavigationGroup> = [
{
title: "Overview",
items: [
{
path: "/",
type: NavigationItemType.Link,
label: "Landing",
},
{
path: "https://github.com/tempblade/creator",
type: NavigationItemType.Link,
label: "GitHub",
},
{
path: "https://tempblade.com",
type: NavigationItemType.Link,
label: "tempblade",
},
],
},
{
title: "Legal",
items: [
{
path: "/legal/imprint",
label: "Imprint",
type: NavigationItemType.Link,
},
],
},
{
title: "",
class: "ml-auto mr-0 self-end",
items: [
{
type: NavigationItemType.Text,
content: "Made with ❤️ in Rottweil",
},
],
},
];
---
<footer class="bg-neutral-accent">
<Section>
<div class="flex flex-row flex-wrap gap-12 w-full pb-8">
{
tree.map((group) => (
<div class={group.class}>
{group.title && <h3 class="mb-2">{group.title}</h3>}
<div class="flex flex-col">
{group.items.map((item) => {
switch (item.type) {
case NavigationItemType.Link:
return <a href={item.path}>{item.label}</a>;
case NavigationItemType.Text:
return <p>{item.content}</p>;
}
})}
</div>
</div>
))
}
</div>
</Section>
</footer>

View File

@ -0,0 +1,18 @@
---
import { button } from "../Button.astro";
import Logo from "components/Logo/Logo.astro";
---
<header class="fixed top-0 w-full h-16 z-50">
<div
class="flex flex-row justify-between h-full w-full max-w-section m-auto p-main"
>
<div class="w-16 h-8 flex">
<Logo />
</div>
<a
class={button({ variant: "primary", size: "default" })}
href="https://github.com/tempblade/creator/releases">Download</a
>
</div>
</header>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,27 @@
---
import type { HTMLAttributes } from "astro/types";
import { cva, type VariantProps } from "class-variance-authority";
const section = cva("relative w-full", {
variants: {
padding: {
true: "p-main",
false: "p-0",
},
maxWidth: {
true: "max-w-section mx-auto",
false: "",
},
},
});
export interface Props
extends HTMLAttributes<"section">,
VariantProps<typeof section> {}
const { padding = true, maxWidth = true, ...props } = Astro.props;
---
<section {...props} class={section({ padding, maxWidth })}>
<slot />
</section>

1
web/src/env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="astro/client" />

View File

@ -0,0 +1,32 @@
---
import Footer from "@/components/Layout/Footer.astro";
import Header from "@/components/Layout/Header.astro";
import "@/styles/global.css";
export interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Astro description" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<script
defer
data-domain="creator.tempblade.com"
src="https://analytics.unom.io/js/plausible.js"></script>
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<Header />
<slot />
<Footer />
</body>
</html>

27
web/src/lib/space-api.ts Normal file
View File

@ -0,0 +1,27 @@
type SpaceApi = {
api: "0.13";
space: string;
logo: string;
url: string;
location: {
address: string;
lat: number;
lon: number;
};
contact: {
phone: string;
twitter: string;
email: string;
};
issue_report_channels: Array<string>;
state: { open: boolean; lastchange: number };
sensors: { temperature: null; humidity: null };
};
export async function getSpaceData() {
const response = await fetch("https://vspace.one/spaceapi.json");
const data: SpaceApi = await response.json();
return data;
}

6
web/src/lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
import clsx, { type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

21
web/src/pages/index.astro Normal file
View File

@ -0,0 +1,21 @@
---
import LandingHeader from "@/sections/LandingHeader.astro";
import Layout from "../layouts/RootLayout.astro";
import Section from "@/components/Section.astro";
import Introduction from "@/sections/Introduction.astro";
import SpaceState from "@/sections/SpaceState.astro";
---
<Layout
title="vspace.one - Makerspace, Hackerspace, Chaostreff und offene Werkstatt für Villingen-Schwenningen und Umgebung."
>
<main>
<Section>
<LandingHeader />
</Section>
<Section>
<Introduction />
</Section>
<SpaceState />
</main>
</Layout>

View File

@ -0,0 +1,18 @@
<div>
<h3>Was wir machen</h3>
<p>
Unser Ziel ist der Wissensaustausch sowie die Bildung aller interessierten
in den Bereichen neuartiger computergestützter Technologien (wie zum
Beispiel aber nicht ausschließlich 3D-­Druck, CNC, Internet der Dinge und
Robotik), der Elektrotechnik und Elektronik sowie auch auf Gebieten der
Reparatur und Wartung.
<br />
Natürlich aber auch ganz im Sinne aller anderen Hacker- und Makerspaces das bieten
einer Plattform, um seinen Interessen in diesen Gebieten nachzugehen von Programmieren
bis Holz- und Metallwerken ist fast alles dabei.
<br />
Dazu haben wir eine immer größer werdende offene Werkstatt, die für jeden zugänglich
sein soll!
</p>
</div>

View File

@ -0,0 +1,3 @@
<div class="min-h-[30vh] flex justify-center items-center">
<h1>vspace.one</h1>
</div>

View File

@ -0,0 +1,17 @@
---
import { cn } from "@/lib/utils";
import { getSpaceData } from "@/lib/space-api";
const spaceData = await getSpaceData();
---
<div
class={cn(
"w-full h-40 flex justify-center items-center",
spaceData.state.open ? "bg-green-500" : "bg-red-400"
)}
>
<p class="text-neutral">
{spaceData.state.open ? "Offen" : "Geschlossen"}
</p>
</div>

View File

@ -0,0 +1,5 @@
@custom-media --sm-viewport screen and (min-width: 640px);
@custom-media --md-viewport screen and (min-width: 768px);
@custom-media --lg-viewport screen and (min-width: 1024px);
@custom-media --xl-viewport screen and (min-width: 1280px);
@custom-media --2xl-viewport screen and (min-width: 1536px);

104
web/src/styles/global.css Normal file
View File

@ -0,0 +1,104 @@
@import "./breakpoints.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--color-main: 0 0% 0%;
--color-main: 0 0% 10%;
--color-primary: 280 80% 50%;
--color-neutral: 0 0% 100%;
--color-neutral-accent: 0 0% 93%;
--color-highlight: 264 100% 50%;
--color-success: 132 100% 78%;
--color-error: 335 100% 62%;
--padding-main: 25px 25px;
--padding-card: 1.25rem 1.25rem;
--spacing-main: 20px;
--gap-card: 1rem;
--max-width-section: 100%;
}
@media (prefers-color-scheme: dark) {
:root {
--color-main: 0 0% 100%;
--color-secondary: 0 0% 80%;
--color-primary: 150 90% 60%;
--color-neutral: 160 20% 5%;
--color-neutral-accent: 160 70% 6%;
--color-highlight: 336 100% 60%;
--color-success: 132 100% 78%;
--color-error: 335 100% 62%;
}
}
html {
@apply bg-neutral text-main;
font-family: system-ui, sans-serif;
}
code {
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
h1 {
@apply text-main;
}
@media (--lg-viewport) {
:root {
--max-width-section: 1000px;
}
}
@media (--xl-viewport) {
:root {
--max-width-section: 1550px;
--padding-main: 45px 100px;
}
}
h1 {
@apply text-3xl font-bold;
}
h2 {
@apply text-2xl font-semibold;
}
h3 {
@apply text-xl font-medium;
}
h4 {
@apply text-lg;
}
[astro-icon] {
fill: currentColor;
}
article {
& h1, h2, h3, h4, h5 {
@apply mt-6;
}
& h1, h2, h3, h4, h5, p, ol, ul {
@apply mb-4;
}
}
p,
li {
@apply max-w-[600px] whitespace-pre-line text-base text-secondary;
line-height: 1.5;
}

65
web/tailwind.config.cjs Normal file
View File

@ -0,0 +1,65 @@
const plugin = require("tailwindcss/plugin");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
theme: {
extend: {
screens: { "2xl": "1400px", "3xl": "1850px" },
borderRadius: {
card: "0.5rem",
md: "0.5rem",
},
colors: {
main: "hsl(var(--color-main))",
primary: "hsl(var(--color-primary))",
secondary: "hsl(var(--color-secondary))",
neutral: "hsl(var(--color-neutral))",
"neutral-accent": "hsl(var(--color-neutral-accent))",
success: "hsl(var(--color-success))",
error: "hsl(var(--color-error))",
},
transitionTimingFunction: {
"in-sine": "cubic-bezier(0.47, 0, 0.745, 0.715)",
"out-sine": "cubic-bezier(0.39, 0.575, 0.565, 1)",
"in-out-sine": "cubic-bezier(0.445, 0.05, 0.55, 0.95)",
"in-quad": "cubic-bezier(0.55, 0.085, 0.68, 0.53)",
"out-quad": "cubic-bezier(0.25, 0.46, 0.45, 0.94)",
"in-out-quad": "cubic-bezier(0.455, 0.03, 0.515, 0.955)",
"in-cubic": "cubic-bezier(0.55, 0.055, 0.675, 0.19)",
"out-cubic": "cubic-bezier(0.215, 0.61, 0.355, 1)",
"in-out-cubic": "cubic-bezier(0.645, 0.045, 0.355, 1)",
"in-quart": "cubic-bezier(0.895, 0.03, 0.685, 0.22)",
"out-quart": "cubic-bezier(0.165, 0.84, 0.44, 1)",
"in-out-quart": "cubic-bezier(0.77, 0, 0.175, 1)",
"in-quint": "cubic-bezier(0.755, 0.05, 0.855, 0.06)",
"out-quint": "cubic-bezier(0.23, 1, 0.32, 1)",
"in-out-quint": "cubic-bezier(0.86, 0, 0.07, 1)",
"in-expo": "cubic-bezier(0.95, 0.05, 0.795, 0.035)",
"out-expo": "cubic-bezier(0.19, 1, 0.22, 1)",
"in-out-expo": "cubic-bezier(1, 0, 0, 1)",
"in-circ": "cubic-bezier(0.6, 0.04, 0.98, 0.335)",
"out-circ": "cubic-bezier(0.075, 0.82, 0.165, 1)",
"in-out-circ": "cubic-bezier(0.785, 0.135, 0.15, 0.86)",
},
gap: {
main: "var(--spacing-main)",
card: "var(--gap-card)",
},
padding: {
main: "var(--padding-main)",
card: "var(--padding-card)",
},
maxWidth: {
section: "var(--max-width-section)",
form: "var(--max-width-form)",
},
},
},
plugins: [
plugin(function ({ addVariant }) {
addVariant("hocus", ["&:hover", "&:focus"]);
}),
],
};

11
web/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"extends": "astro/tsconfigs/strictest",
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@/*": [
"./*"
]
}
}
}