Initial commit

This commit is contained in:
Filip Znachor 2024-04-24 16:07:10 +02:00
commit 119a5f3e0d
28 changed files with 480 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
dist/
.astro/
node_modules/

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

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

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

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

8
astro.config.mjs Normal file
View file

@ -0,0 +1,8 @@
import { defineConfig } from 'astro/config';
import icon from "astro-icon";
// https://astro.build/config
export default defineConfig({
integrations: [icon()]
});

21
package.json Normal file
View file

@ -0,0 +1,21 @@
{
"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.5.10",
"@iconify-json/mdi": "^1.1.66",
"@iconify-json/tabler": "^1.1.110",
"astro": "^4.6.3",
"astro-icon": "^1.1.0",
"localtunnel": "^2.0.2",
"typescript": "^5.4.5"
}
}

7
public/.htaccess Normal file
View file

@ -0,0 +1,7 @@
RewriteEngine on
RewriteCond %{HTTP_HOST} !^www.codespace.cz$
RewriteRule (.*)$ http://www.codespace.cz/$1 [R=301,L]
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

BIN
public/assets/logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

9
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

1
public/logo.svg Normal file
View file

@ -0,0 +1 @@
<svg viewBox="0 0 12.79 11.41" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a"><stop style="stop-color:#db2446;stop-opacity:1" offset="0"/><stop style="stop-color:#c51b6e;stop-opacity:1" offset="1"/></linearGradient><linearGradient xlink:href="#a" id="b" gradientUnits="userSpaceOnUse" x1="16.32" y1="51.01" x2="28.46" y2="60.75" gradientTransform="translate(-16.3 -49.93)"/></defs><text xml:space="preserve" style="font-size:12.347px;line-height:0;font-family:Cantarell;-inkscape-font-specification:Cantarell;opacity:1;fill:#000;fill-opacity:0;stroke-width:.264578;-inkscape-stroke:none" x="-.31" y="9.72" transform="scale(.99998 1.00002)"><tspan style="font-style:normal;font-variant:normal;font-weight:900;font-stretch:normal;line-height:0;font-family:Inconsolata;-inkscape-font-specification:'Inconsolata Heavy';fill:#000;fill-opacity:0;stroke-width:.264578" x="-.31" y="9.72">{</tspan></text><text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:700;font-stretch:normal;font-size:16.3865px;line-height:0;font-family:Inconsolata;-inkscape-font-specification:'Inconsolata Bold';writing-mode:tb-rl;fill:#000;fill-opacity:0;stroke-width:.273109;-inkscape-stroke:none" x="5.07" y="10.67" transform="scale(.99998 1.00002)"><tspan style="writing-mode:lr-tb;fill:#000;fill-opacity:0;stroke-width:.273109" x="5.07" y="10.67">$</tspan></text><path style="font-weight:900;font-size:12.3472px;line-height:0;font-family:Inconsolata;-inkscape-font-specification:'Inconsolata Heavy';fill:url(#b);stroke-width:.264583" d="M8.89 0v.97c-.5.06-.95.2-1.36.42-.42.21-.75.48-1 .82a1.9 1.9 0 0 0-.38 1.17c0 .43.1.82.31 1.16.21.34.52.64.92.9.4.25.9.49 1.46.7v2.97a3.65 3.65 0 0 1-1.25-.44 2.38 2.38 0 0 1-.5-.4.43.43 0 0 1-.14-.26c0-.1-.07-.17-.18-.21l-.85 1.4a4.21 4.21 0 0 0 1.89 1.1c.36.1.72.17 1.08.2v.9h1.15v-.91a3.46 3.46 0 0 0 1.44-.53 2.9 2.9 0 0 0 1.3-2.27c0-.47-.08-.87-.27-1.18a2.38 2.38 0 0 0-.88-.86c-.4-.25-.91-.49-1.55-.72V2.36c.12.02.25.05.37.1l.34.11c.1.05.2.1.3.17.1.06.18.14.26.23.05.03.09.1.11.2.02.1.1.15.21.16l.86-1.09c-.2-.25-.44-.46-.7-.63a3.89 3.89 0 0 0-.86-.43c-.3-.12-.62-.2-.93-.23v-.4c0-.1.01-.2.04-.27a.5.5 0 0 0 .05-.17l-.01-.06c0-.02-.02-.04-.05-.05zM3.84.85c-.52 0-.98.09-1.37.26-.39.16-.7.41-.91.74a2.2 2.2 0 0 0-.33 1.22l.03 1.16c0 .23-.03.41-.09.53a.45.45 0 0 1-.24.26 1 1 0 0 1-.42.08H0v1.34h.52a1 1 0 0 1 .38.07c.1.03.2.11.25.24.06.13.08.34.08.6L1.2 8.4c0 .6.12 1.06.37 1.39.25.34.6.57 1.04.7.44.13.95.2 1.53.2h.69V9.35h-.87c-.26 0-.48-.04-.65-.1a.73.73 0 0 1-.37-.31 1.2 1.2 0 0 1-.11-.55l.02-.86a2.33 2.33 0 0 0-.42-1.35A1.6 1.6 0 0 0 2 5.76c.18-.11.34-.25.47-.42a2.06 2.06 0 0 0 .4-1.25l-.02-1.14c0-.24.06-.42.19-.54.13-.14.33-.2.59-.2h1.21V.85Zm5 1.5v2.14a3.5 3.5 0 0 1-.6-.33c-.17-.13-.3-.27-.4-.4a.92.92 0 0 1-.13-.47c0-.17.05-.32.15-.46a1 1 0 0 1 .4-.3c.18-.1.37-.15.58-.19zm1.24 4.16c.24.08.45.19.63.3.18.13.32.26.4.42a1.15 1.15 0 0 1 .01 1.11 1.74 1.74 0 0 1-1.03.74z"/></svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

61
src/components/Card.astro Normal file
View file

@ -0,0 +1,61 @@
---
interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #23262d;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: white;
background-color: #23262d;
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;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
</style>

View file

@ -0,0 +1,5 @@
<footer>
<div class="inner">
&copy; CodeSpace { new Date().getFullYear() }
</div>
</footer>

38
src/components/Nav.astro Normal file
View file

@ -0,0 +1,38 @@
---
import { Icon } from 'astro-icon/components';
import NavLink from './NavLink.astro';
const nav = [
{title: "Úvod", link: "/"},
{title: "Služby", link: "/sluzby"},
// {title: "O nás", link: "/o-nas"},
{title: "Kontakt", link: "/kontakt"}
];
---
<input type="checkbox" id="menu-toggle">
<nav>
<div class="inner">
<div class="title">
<label for="menu-toggle">
<Icon name="tabler:menu" />
</label>
<a href="/">
<img src="/assets/logo.webp" />
</a>
</div>
<ul>
{ nav.map(item =>
<li>
<NavLink href={item.link}>{item.title}</NavLink>
</li>
) }
<li class="right">
<a href="//status.cdsp.cz" target="_blank">Stav služeb <Icon name="tabler:external-link"></a>
</li>
<li>
<a href="//wiki.cdsp.cz" target="_blank">Wiki <Icon name="tabler:external-link"></a>
</li>
</ul>
</div>
</nav>

View file

@ -0,0 +1,15 @@
---
import type { HTMLAttributes } from 'astro/types';
type Props = HTMLAttributes<'a'>;
const { href, class: className, ...props } = Astro.props;
const { pathname } = Astro.url;
const subpath = pathname.match(/[^\/]+/g);
const isActive = href === pathname || href === '/' + subpath?.[0];
---
<a href={href} class:list={[className, { active: isActive }]} {...props}>
<slot />
</a>

2
src/env.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

35
src/layouts/Layout.astro Normal file
View file

@ -0,0 +1,35 @@
---
import Nav from '../components/Nav.astro';
import Footer from '../components/Footer.astro';
interface Meta {
title?: string;
desc: string;
}
interface Props {
meta: Meta
}
const { meta } = Astro.props;
import '../styles/global.css';
import '../styles/fonts.css';
---
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>{meta.title}</title>
<meta name="description" content={meta.desc} />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/webp" href="/assets/logo.webp" />
<script defer data-domain="codespace.cz" src="https://plausible.cdsp.cz/js/script.js"></script>
</head>
<body>
<Nav />
<slot />
<Footer />
</body>
</html>

43
src/pages/index.astro Normal file
View file

@ -0,0 +1,43 @@
---
import Layout from '../layouts/Layout.astro';
import { Icon } from 'astro-icon/components';
import { services } from '../scripts/services';
const meta = {
title: "CodeSpace.cz - Spojuje nás touha po svobodě a nezávislosti v digitální době",
desc: "Provozujeme si vlastní síť a servery, abychom nemuseli spoléhat na ostatní."
};
---
<Layout meta={meta}>
<header class="main">
<div class="inner">
<div class="grid">
<div>
<h1>Spojuje nás touha po <span>svobodě</span> a <span>nezávislosti</span> v digitální době.</h1>
<p>Provozujeme si vlastní síť a servery, abychom nemuseli spoléhat na ostatní. Poskytujeme různé služby, které respektují naše soukromí.</p>
</div>
</div>
</div>
</header>
<section>
<div class="inner">
<h2>Služby</h2>
<p>Přehled veřejných služeb, které provozujeme.</p>
<div class="services">
{ services.map(service =>
<div class="service">
<div class="title">
<Icon name={ service.icon } />
<h3>{ service.name }</h3>
</div>
<p>{ service.desc }</p>
{ service.url &&
<a href={ "https://" + service.url } target="_blank"><Icon name="tabler:link" /> { service.url }</a>
}
</div>
) }
</div>
</div>
</section>
</Layout>

17
src/pages/kontakt.astro Normal file
View file

@ -0,0 +1,17 @@
---
import Layout from '../layouts/Layout.astro';
const meta = {
title: "Kontakt - CodeSpace.cz",
desc: "Způsoby, kterými nás můžete kontaktovat."
};
---
<Layout meta={meta}>
<header>
<div class="inner">
<h1>Kontakt</h1>
<p>email: <a href="mailto:hello@codespace.cz">hello@codespace.cz</a></p>
</div>
</header>
</Layout>

37
src/pages/sluzby.astro Normal file
View file

@ -0,0 +1,37 @@
---
import Layout from '../layouts/Layout.astro';
import { Icon } from 'astro-icon/components';
import { services } from '../scripts/services';
const meta = {
title: "Služby - CodeSpace.cz",
desc: "Přehled veřejných služeb, které provozujeme."
};
---
<Layout meta={meta}>
<header>
<div class="inner">
<h1>Služby</h1>
<p>Přehled veřejných služeb, které provozujeme.</p>
</div>
</header>
<section>
<div class="inner">
<div class="services">
{ services.map(service =>
<div class="service">
<div class="title">
<Icon name={ service.icon } />
<h3>{ service.name }</h3>
</div>
<p>{ service.desc }</p>
{ service.url &&
<a href={ "https://" + service.url } target="_blank"><Icon name="tabler:link" /> { service.url }</a>
}
</div>
) }
</div>
</div>
</section>
</Layout>

8
src/scripts/services.ts Normal file
View file

@ -0,0 +1,8 @@
export const services = [
{name: "Piped", icon: "tabler:brand-youtube", desc: "Alternativní frontend pro YouTube, který vás nesleduje a je bez reklam.", url: "yt.cdsp.cz"},
{name: "Piped Music", icon: "tabler:headphones", desc: "Náš open-source klient pro poslech hudby, který vaše data s nikým nesdílí.", url: "pm.cdsp.cz"},
{name: "Send", icon: "tabler:cloud-upload", desc: "Posílejte až 10 GB souborů bezpečně a soukromě s end-to-end šifrováním.", url: "send.cdsp.cz"},
{name: "Forgejo", icon: "tabler:brand-git", desc: "Jednoduchá a nenáročná nadstavba Gitu, na které vyvíjíme své projekty.", url: "git.cdsp.cz"},
{name: "Nextcloud", icon: "tabler:brand-nextcloud", desc: "Úložiště souborů s možností synchronizace a spoustou užitečných aplikací."},
{name: "Vaultwarden", icon: "tabler:square-key", desc: "Správce hesel s užitečnými funkcemi kompatibilní s Bitwardenem."}
];

29
src/styles/fonts.css Normal file
View file

@ -0,0 +1,29 @@
@font-face {
font-family: 'Cantarell';
src: local('Cantarell Bold'), local('Cantarell-Bold'),
url('../fonts/Cantarell-Bold.woff2') format('woff2'),
url('../fonts/Cantarell-Bold.woff') format('woff');
font-weight: bold;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Cantarell';
src: local('Cantarell Extra Bold'), local('Cantarell-ExtraBold'),
url('../fonts/Cantarell-ExtraBold.woff2') format('woff2'),
url('../fonts/Cantarell-ExtraBold.woff') format('woff');
font-weight: 900;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Cantarell';
src: local('Cantarell Regular'), local('Cantarell-Regular'),
url('../fonts/Cantarell-Regular.woff2') format('woff2'),
url('../fonts/Cantarell-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
font-display: swap;
}

123
src/styles/global.css Normal file
View file

@ -0,0 +1,123 @@
:root {
--bg-color: #18181b;
--dark-bg-color: #000000;
--alt-bg-color: #232327;
--border-color: #27272a;
--brand-color-1: #db2446;
--brand-color-2: #c51b6e;
--nav-link-color: #fff;
--inactive-color: #444;
--title-color: #fff;
--text-color: #fffa;
--text-hover-color: #fffd;
--text-active-color: #fff;
--link-color: #dd4379;
--link-hover-color: #c21f58;
--site-padding: 40px;
--site-width: 1200px;
}
body {font-family: Cantarell, sans-serif; background-color: var(--bg-color); color: var(--text-color); margin: 0; line-height: 1.5; font-size: 20px;}
:is(nav, header, section, footer) > .inner {max-width: var(--site-width); margin: auto; padding: 0 var(--site-padding);}
@media screen and (max-width: 600px) {
:root {
--site-padding: 20px;
}
}
h1, h2, h3, h4, h5 {color: var(--title-color); font-weight: 900;}
a {color: var(--link-color);}
a:hover {color: var(--link-hover-color);}
/* Navigation */
nav {position: sticky; top: 0; left: 0; width: 100%; border-bottom: 1px solid var(--border-color); background-color: var(--bg-color);}
nav .inner {display: flex; gap: 30px; align-items: center;}
nav .title {display: flex; gap: 15px; align-items: center;}
nav .title label {display: none; padding: 20px; margin: -20px; cursor: pointer; font-size: 24px;}
nav .title img {height: 30px; padding-bottom: 1px;}
nav ul {list-style: none; padding: 0; margin: 0; display: flex; gap: 20px; font-size: 20px; flex: 1;}
nav a {display: flex; align-items: center; gap: 8px; color: var(--text-color); text-decoration: none; font-weight: 700; padding: 22px 0;}
nav a.active {color: var(--nav-link-color); box-shadow: 0 1px 0 0 var(--nav-link-color);}
nav a:hover {color: var(--text-hover-color);}
nav ul li.right {margin-left: auto;}
#menu-toggle {display: none;}
@media screen and (max-width: 900px) {
nav {height: 75px; overflow: hidden;}
#menu-toggle:checked + nav {height: 100%;}
nav .inner {display: block; flex-direction: column-reverse;}
nav .title {border-bottom: 1px solid var(--border-color);}
nav .title label {display: flex;}
nav ul {flex-direction: column; margin: 25px 0; gap: 0px;}
nav ul li {margin: 0 !important;}
nav ul a {padding: 8px 20px; padding-left: 51px; margin: 0 -20px; font-size: 18px; position: relative; text-decoration: none;}
nav ul a.active {font-weight: 900; box-shadow: none;}
nav ul a::before {content: ""; width: 11px; height: 2px; background-color: var(--inactive-color); position: absolute; left: 23px; top: 50%; border-radius: 5px;}
nav ul a.active::before {background-color: var(--brand-color-1);}
}
/* Header */
header > .inner {padding: 70px var(--site-padding);}
header.main {background-image: radial-gradient(max(50vh, 60vw) at -10vw 100vh, var(--brand-color-2), transparent), radial-gradient(max(50vh, 60vw) at 110vw 0, var(--brand-color-1), transparent); border-bottom: 1px solid var(--border-color); background-color: var(--dark-bg-color);}
header.main .grid {display: grid; grid-template-columns: 600px 1fr;}
header.main h1 {font-weight: 900; font-size: 40px;}
header.main h1 span:nth-child(1) {color: var(--brand-color-1);}
header.main h1 span:nth-child(2) {color: var(--brand-color-2);}
@media screen and (max-width: 700px) {
header.main .grid {grid-template-columns: 1fr;}
}
/* Sections */
:is(section, footer) > .inner {padding: 0 var(--site-padding); margin: 50px auto;}
/* Services */
.services {display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 30px; margin: 50px 0;}
.service .title {display: flex; align-items: center; gap: 15px;}
.service .title svg {font-size: 24px; color: var(--title-color);}
.service :is(h3, p) {margin: 10px 0;}
.service a {display: flex; align-items: center; gap: 7px;}
@media screen and (max-width: 900px) {
.services {grid-template-columns: 1fr 1fr;}
}
@media screen and (max-width: 600px) {
.services {grid-template-columns: 1fr;}
}

3
tsconfig.json Normal file
View file

@ -0,0 +1,3 @@
{
"extends": "astro/tsconfigs/strict"
}