Compare commits
17 Commits
4964727717
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b4e43b0c6c | |||
| 6bf7e738f8 | |||
| 4936692b62 | |||
| e76ac0f42c | |||
| 86c1c85cb2 | |||
| 912aa15c90 | |||
| c795ba45fb | |||
| 44733d4360 | |||
| 5b8f44461b | |||
| dea9bebdbb | |||
| b49b03b46f | |||
| 35fba1e43f | |||
| 6994808b77 | |||
| 54b5a0b3b0 | |||
| 5431967dce | |||
| fe707a600e | |||
| 2f1edd9851 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -130,8 +130,8 @@ dist
|
|||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
# Astro
|
# Direnv cache
|
||||||
.astro/
|
|
||||||
|
|
||||||
# Direnv
|
|
||||||
.direnv/
|
.direnv/
|
||||||
|
|
||||||
|
# Astro data
|
||||||
|
.astro/
|
||||||
|
|||||||
13
.prettierrc.mjs
Normal file
13
.prettierrc.mjs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// .prettierrc.mjs
|
||||||
|
/** @type {import("prettier").Config} */
|
||||||
|
export default {
|
||||||
|
plugins: ["prettier-plugin-astro"],
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: "*.astro",
|
||||||
|
options: {
|
||||||
|
parser: "astro",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from "astro/config";
|
||||||
import mdx from '@astrojs/mdx';
|
import mdx from "@astrojs/mdx";
|
||||||
import sitemap from '@astrojs/sitemap';
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://example.com',
|
site: "https://www.terakoda.com",
|
||||||
integrations: [mdx(), sitemap()],
|
integrations: [mdx(), sitemap()],
|
||||||
|
vite: {
|
||||||
|
plugins: [tailwindcss()],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
22
benchmark.yaml
Normal file
22
benchmark.yaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
concurrency: 10
|
||||||
|
base: "https://www2.terakoda.com"
|
||||||
|
iterations: 100
|
||||||
|
rampup: 2
|
||||||
|
|
||||||
|
plan:
|
||||||
|
- name: Fetch root
|
||||||
|
request:
|
||||||
|
url: "/"
|
||||||
|
- name: Fetch css
|
||||||
|
request:
|
||||||
|
url: "/_astro/about.B804zLT3.css"
|
||||||
|
- name: Fetch logo
|
||||||
|
request:
|
||||||
|
url: "/_astro/HeaderLogo.BGOAlOcd.png"
|
||||||
|
- name: Fetch blog
|
||||||
|
request:
|
||||||
|
url: "/blog"
|
||||||
|
- name: Fetch Article
|
||||||
|
request:
|
||||||
|
url: "/blog/a-fresh-start"
|
||||||
12
flake.nix
12
flake.nix
@@ -38,12 +38,14 @@
|
|||||||
{ pkgs }:
|
{ pkgs }:
|
||||||
{
|
{
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
# Pinned packages available in the environment
|
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
nodejs_22
|
git # Version control
|
||||||
eslint
|
nodejs_22 # Base language utils
|
||||||
git
|
eslint # Linting
|
||||||
nixpkgs-fmt
|
astro-language-server # LSP
|
||||||
|
nodePackages.prettier # Formatting
|
||||||
|
nixpkgs-fmt # Formatting
|
||||||
|
drill # Benchmarking
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
1013
package-lock.json
generated
1013
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "astro",
|
"name": "terakoda.com",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -12,6 +12,14 @@
|
|||||||
"@astrojs/mdx": "^4.3.0",
|
"@astrojs/mdx": "^4.3.0",
|
||||||
"@astrojs/rss": "^4.0.11",
|
"@astrojs/rss": "^4.0.11",
|
||||||
"@astrojs/sitemap": "^3.4.0",
|
"@astrojs/sitemap": "^3.4.0",
|
||||||
"astro": "^5.8.1"
|
"@tailwindcss/vite": "^4.1.8",
|
||||||
|
"astro": "^5.8.1",
|
||||||
|
"less": "^4.3.0",
|
||||||
|
"tailwindcss": "^4.1.8"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@astrojs/ts-plugin": "^1.10.4",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"prettier-plugin-astro": "^0.14.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
19
src/components/ButtonLink.astro
Normal file
19
src/components/ButtonLink.astro
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
href: string;
|
||||||
|
event?: string;
|
||||||
|
eventTitle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { href, event, eventTitle } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<a
|
||||||
|
href={href}
|
||||||
|
data-goatcounter-click={event}
|
||||||
|
data-goatcounter-title={eventTitle}
|
||||||
|
data-goatcounter-referrer="www.terakoda.com"
|
||||||
|
class="text-dark bg-primary rounded-md px-8 py-4 font-bold hover:bg-primary-dark"
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</a>
|
||||||
@@ -2,71 +2,12 @@
|
|||||||
const today = new Date();
|
const today = new Date();
|
||||||
---
|
---
|
||||||
|
|
||||||
<footer>
|
<footer
|
||||||
|
class="py-4 text-sm text-center bg-dark text-light absolute bottom-0 w-full"
|
||||||
|
>
|
||||||
© {today.getFullYear()} Terakoda, LLC. All rights reserved.
|
© {today.getFullYear()} Terakoda, LLC. All rights reserved.
|
||||||
<!--
|
<script
|
||||||
<div class="social-links">
|
data-goatcounter="https://goatcounter.terakoda.com/count"
|
||||||
footer {
|
async
|
||||||
background-color: #334155; /* Slightly darker dark background */
|
src="https://www.terakoda.com/count.js"></script>
|
||||||
color: #fdfafc; /* Light Text */
|
|
||||||
text-align: center;
|
|
||||||
padding: 20px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
<a href="https://m.webtoo.ls/@astro" target="_blank">
|
|
||||||
<span class="sr-only">Follow Astro on Mastodon</span>
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
aria-hidden="true"
|
|
||||||
width="32"
|
|
||||||
height="32"
|
|
||||||
astro-icon="social/mastodon"
|
|
||||||
><path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z"
|
|
||||||
></path></svg
|
|
||||||
>
|
|
||||||
</a>
|
|
||||||
<a href="https://twitter.com/astrodotbuild" target="_blank">
|
|
||||||
<span class="sr-only">Follow Astro on Twitter</span>
|
|
||||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" astro-icon="social/twitter"
|
|
||||||
><path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"
|
|
||||||
></path></svg
|
|
||||||
>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/withastro/astro" target="_blank">
|
|
||||||
<span class="sr-only">Go to Astro's GitHub repo</span>
|
|
||||||
<svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" astro-icon="social/github"
|
|
||||||
><path
|
|
||||||
fill="currentColor"
|
|
||||||
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"
|
|
||||||
></path></svg
|
|
||||||
>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</footer>
|
</footer>
|
||||||
<style>
|
|
||||||
footer {
|
|
||||||
padding: 2em 1em 6em 1em;
|
|
||||||
background-color: #334155; /* Slightly darker dark background */
|
|
||||||
color: #fdfafc; /* Light Text */
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
.social-links {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 1em;
|
|
||||||
margin-top: 1em;
|
|
||||||
}
|
|
||||||
.social-links a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: rgb(var(--gray));
|
|
||||||
}
|
|
||||||
.social-links a:hover {
|
|
||||||
color: rgb(var(--gray-dark));
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
---
|
---
|
||||||
interface Props {
|
interface Props {
|
||||||
date: Date;
|
date: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { date } = Astro.props;
|
const { date } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<time datetime={date.toISOString()}>
|
<time datetime={date.toISOString()} class="text-medium">
|
||||||
{
|
{
|
||||||
date.toLocaleDateString('en-us', {
|
date.toLocaleDateString("en-us", {
|
||||||
year: 'numeric',
|
year: "numeric",
|
||||||
month: 'short',
|
month: "short",
|
||||||
day: 'numeric',
|
day: "numeric",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</time>
|
</time>
|
||||||
|
|||||||
@@ -1,94 +1,49 @@
|
|||||||
---
|
---
|
||||||
import HeaderLink from './HeaderLink.astro';
|
import HeaderLink from "./HeaderLink.astro";
|
||||||
import { SITE_TITLE } from '../consts';
|
import HeaderLogo from "../assets/HeaderLogo.png";
|
||||||
---
|
---
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<nav>
|
<nav class="flex-col md:flex-row">
|
||||||
<h2><a href="/">
|
<div class="hidden md:block">
|
||||||
<img src="/HeaderLogo.png" alt="Terakoda Logo" class="logo">
|
<a href="/">
|
||||||
</a></h2>
|
<img
|
||||||
<div class="internal-links">
|
src={HeaderLogo.src}
|
||||||
<HeaderLink href="/">Home</HeaderLink>
|
alt="Terakoda"
|
||||||
<HeaderLink href="/blog">Blog</HeaderLink>
|
height={HeaderLogo.height / 4}
|
||||||
<HeaderLink href="/about">About</HeaderLink>
|
width={HeaderLogo.width / 4}
|
||||||
</div>
|
/>
|
||||||
<!-- <div class="social-links"> -->
|
</a>
|
||||||
<!-- <a href="https://m.webtoo.ls/@astro" target="_blank"> -->
|
</div>
|
||||||
<!-- <span class="sr-only">Follow Astro on Mastodon</span> -->
|
<div class="internal-links">
|
||||||
<!-- <svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" -->
|
<HeaderLink href="/">Home</HeaderLink>
|
||||||
<!-- ><path -->
|
<HeaderLink href="/blog">Blog</HeaderLink>
|
||||||
<!-- fill="currentColor" -->
|
<HeaderLink href="/about">About</HeaderLink>
|
||||||
<!-- d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z" -->
|
</div>
|
||||||
<!-- ></path></svg -->
|
</nav>
|
||||||
<!-- > -->
|
|
||||||
<!-- </a> -->
|
|
||||||
<!-- <a href="https://twitter.com/astrodotbuild" target="_blank"> -->
|
|
||||||
<!-- <span class="sr-only">Follow Astro on Twitter</span> -->
|
|
||||||
<!-- <svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" -->
|
|
||||||
<!-- ><path -->
|
|
||||||
<!-- fill="currentColor" -->
|
|
||||||
<!-- d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z" -->
|
|
||||||
<!-- ></path></svg -->
|
|
||||||
<!-- > -->
|
|
||||||
<!-- </a> -->
|
|
||||||
<!-- <a href="https://github.com/withastro/astro" target="_blank"> -->
|
|
||||||
<!-- <span class="sr-only">Go to Astro's GitHub repo</span> -->
|
|
||||||
<!-- <svg viewBox="0 0 16 16" aria-hidden="true" width="32" height="32" -->
|
|
||||||
<!-- ><path -->
|
|
||||||
<!-- fill="currentColor" -->
|
|
||||||
<!-- d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z" -->
|
|
||||||
<!-- ></path></svg -->
|
|
||||||
<!-- > -->
|
|
||||||
<!-- </a> -->
|
|
||||||
<!-- </div> -->
|
|
||||||
</nav>
|
|
||||||
</header>
|
</header>
|
||||||
<style>
|
<style>
|
||||||
header {
|
header {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
box-shadow: 0 2px 8px rgba(var(--black), 5%);
|
box-shadow: 0 2px 8px rgba(var(--black), 5%);
|
||||||
background-color: #1f2932; /* Dark Background */
|
background-color: #1f2932; /* Dark Background */
|
||||||
color: #fdfafc; /* Light Text */
|
color: #fdfafc; /* Light Text */
|
||||||
}
|
|
||||||
|
|
||||||
header .logo {
|
|
||||||
height: 2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
nav {
|
||||||
margin: 0;
|
display: flex;
|
||||||
font-size: 1em;
|
align-items: center;
|
||||||
}
|
justify-content: space-between;
|
||||||
|
}
|
||||||
h2 a,
|
nav a {
|
||||||
h2 a.active {
|
padding: 1em 0.5em;
|
||||||
text-decoration: none;
|
color: var(--black);
|
||||||
}
|
border-bottom: 4px solid transparent;
|
||||||
nav {
|
text-decoration: none;
|
||||||
display: flex;
|
}
|
||||||
align-items: center;
|
nav a.active {
|
||||||
justify-content: space-between;
|
text-decoration: none;
|
||||||
}
|
border-bottom-color: var(--color-primary);
|
||||||
nav a {
|
}
|
||||||
padding: 1em 0.5em;
|
|
||||||
color: var(--black);
|
|
||||||
border-bottom: 4px solid transparent;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
nav a.active {
|
|
||||||
text-decoration: none;
|
|
||||||
border-bottom-color: var(--accent);
|
|
||||||
}
|
|
||||||
.social-links,
|
|
||||||
.social-links a {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
@media (max-width: 720px) {
|
|
||||||
.social-links {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import { glob } from 'astro/loaders';
|
import { glob } from "astro/loaders";
|
||||||
import { defineCollection, z } from 'astro:content';
|
import { defineCollection, z } from "astro:content";
|
||||||
|
|
||||||
const blog = defineCollection({
|
const blog = defineCollection({
|
||||||
// Load Markdown and MDX files in the `src/content/blog/` directory.
|
// Load Markdown and MDX files in the `src/content/blog/` directory.
|
||||||
loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
|
loader: glob({ base: "./src/content/blog", pattern: "**/*.{md,mdx}" }),
|
||||||
// Type-check frontmatter using a schema
|
// Type-check frontmatter using a schema
|
||||||
schema: z.object({
|
schema: z.object({
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
// Transform string to Date object
|
// Transform string to Date object
|
||||||
pubDate: z.coerce.date(),
|
pubDate: z.coerce.date(),
|
||||||
updatedDate: z.coerce.date().optional(),
|
updatedDate: z.coerce.date().optional(),
|
||||||
heroImage: z.string().optional(),
|
heroImage: z.string().optional(),
|
||||||
}),
|
author: z.string().optional(),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const collections = { blog };
|
export const collections = { blog };
|
||||||
|
|||||||
@@ -1,17 +1,54 @@
|
|||||||
---
|
---
|
||||||
title: "A Fresh Start"
|
title: "A Fresh Start: Why we are starting a Terakoda"
|
||||||
description: "Starting a new venture"
|
description: "Why we decided to quit our tech jobs and start Terakoda as a cooperative."
|
||||||
pubDate: "2025-06-03"
|
pubDate: "2025-06-17"
|
||||||
heroImage: "https://placecats.com/500/400"
|
author: Drew Haven
|
||||||
---
|
---
|
||||||
|
|
||||||
This year we decided that we wanted to try something different. As funding
|
I quit my job in March. It wasn’t an easy decision. It qualified as a Good Job on basically every metric: good pay, growing business, interesting technical problems, smart co-workers. There was very little to criticize about the company. However, I realized it wasn’t the right fit for me. It’s the culmination of some ideas and feelings that have been growing and solidifying over the last six years and four companies. I realized I needed to try something different.
|
||||||
tightens and the AI bubble grows we realized that start-ups are not where we
|
|
||||||
want to be.
|
|
||||||
|
|
||||||
We decided to found a tech co-operative to:
|
I talked it over with my wife and we decided that we would try this together. On top of that, we decided that what we really wanted was to be both independent and part of a cooperative. That might seem like a contradiction, but let me explain.
|
||||||
|
|
||||||
- Support democratic decision making.
|
## First, a little history
|
||||||
- Center the company around the workers and not just profit.
|
|
||||||
- Capture the value of our work for the workers and their communities.
|
I’ve been working in and around Silicon Valley for two decades now. My first job was plugging in cables at the San Jose Convention Center in 2005. Between then and now I’ve done many things. I worked at big companies like Google. I worked at start-ups, so many start-ups, small start-ups, dying start-ups, zombie start-ups, even founding a short-lived start-up. I was lucky enough to be around for one IPO. I’ve also done freelance work a few times, though never for long.
|
||||||
- Align our work with our values.
|
|
||||||
|
Lindsay worked in the video game industry for a decade. She did design and production. She was a producer when THQ shut her studio down. She did production at a few small studios trying to get in on the mobile-game gold-rush. Eventually she landed at Cryptic and did design and production on a major Dungeons & Dragons game launch: Neverwinter. Ultimately, she left that industry to go back to school looking for a change.
|
||||||
|
|
||||||
|
In short, we’ve had enviable careers, but find ourselves disillusioned with the industry. Our careers have gone well, but something just hasn’t felt right.
|
||||||
|
|
||||||
|
## Congruence
|
||||||
|
|
||||||
|
A key issue that we’ve faced is that the values we hold and those of the companies we work for don’t align. We believed in a set of values, but found our professional lives following a different set. Living with this cognitive dissonance was putting a stress on the both of us. We’ve decided that it’s time to pursue a career that better matches those values.
|
||||||
|
|
||||||
|
What are some of those values? Here are just a few that we’ve identified.
|
||||||
|
|
||||||
|
We believe in self-determination. Many workplaces don’t give their employees much agency. Your ability to succeed, grow or advance may be largely out of your control. Instead it’s determined in the majority by the projects or managers you are assigned to. Many people don’t have the ability to choose what to work on. Even if those choices would ultimately come to the same conclusion they aren’t allowed to make that decision themselves. And don’t even worry about realizing there’s something more important to do if it’s not part of your managers roadmap.
|
||||||
|
|
||||||
|
Similarly, we believe in autonomy. This is the right of everyone to work in the way that works best for them. This is a fundamental support for equity.
|
||||||
|
|
||||||
|
A system of cooperation will always produce better outcomes than a system of competition. Competition is fundamentally a structure of destruction. It’s about beating your opponent, about taking away their resources, about taking them down. It leads to a system where there is less. Cooperation is about building each other up, about sharing resources and information. Cooperation creates and adds to the system.
|
||||||
|
|
||||||
|
But, wait, what about all the benefits of competitions? Are you saying sports are negative? No. I frame those as cooperative endeavors. They are inherently governed by a set of rules that the participants put in place so that they can create a space where they can motivate each person to do their best. Compare that to business where the goal of most businesses is to take customers and resources from competitors. It’s framed in terms of selfishly taking and hording as much as possible. How many times have you seen the founder of a failed business go out and congratulate their competitor? How often do they publicly say that they appreciate the financial loss because of the lessons it taught them?
|
||||||
|
|
||||||
|
We also believe that every person in an organization is valuable and worthy of respect. Many tech companies recently have begun laying off workers and doing their best to replace them with AI. We believe that every worker deserves a voice in their workplace.
|
||||||
|
|
||||||
|
This also extends to the customers! Customers are people too. The worst phase we ever heard in our professional lives was when someone said, unironically, “it’s our money in their pockets”. Companies should exist to serve the customers and the employees just as much as the shareholders.
|
||||||
|
|
||||||
|
## So what now?
|
||||||
|
|
||||||
|
We could try to opt out of the system. We could go join a commune in the mountains, grow our own carrots and make our own goat cheese. That’s one alternative, but it’s not an attractive one. For one, it doesn’t leverage any of the skills that we have developed during our careers. But more than that, it cuts us off from the very industry that we would like to see change. So that is why we are going to a new start-up, but we aren’t going to do things the way we’ve seen them done.
|
||||||
|
|
||||||
|
We are founding a new tech cooperative. We will be building a company that follows the principles of cooperatives. We will be democratic, we will be sustainable, we will work for the benefit for our workers, our customers, our communities and the planet.
|
||||||
|
|
||||||
|
We don’t know exactly what our business model is going to be. This is a start-up, and a start-up is all about the search for a new business model. We are going to embark on the customer development journey. Most of the text on the topic is framed in terms of high-scale, VC-backed companies, but many of the points work just as well for a small company.
|
||||||
|
|
||||||
|
We will be small for some time. We don’t know how large we will grow, but we will grow when it makes sense to do so based on our business model. We will need to balance the goals of creating a profitable, sustainable company with the goal of creating a cooperative alternative to Silicon Valley competition.
|
||||||
|
|
||||||
|
We’ve named ourselves Terakoda. Tera- could reference the Greek-origin SI prefix for one trillion, evoking the potential scale of technology. It could also reference Latin word “terra” meaning land or earth. Koda might be reminiscent of “code”, again referencing technology. But also coda, which is a passage that brings a piece to conclusion, much as we hope this represents the conclusion of the search for a fulfilling career.
|
||||||
|
|
||||||
|
## A journey begins
|
||||||
|
|
||||||
|
And so the next phase of our careers begins. We don’t know quite where it will take us, but we know what direction we are moving in. We are excited to embrace a community of respect, cooperation, resilience and sustainability. We will do our best to live our values every day.
|
||||||
|
|
||||||
|
This won’t be a journey we undertake alone. We will rely on the help of many others who have walked these paths before us. We invite you to participate. Please follow along. Don’t hesitate to reach out if you have any thoughts, advice or opportunities for us. We welcome the cooperation.
|
||||||
|
|||||||
@@ -1,85 +1,97 @@
|
|||||||
---
|
---
|
||||||
import type { CollectionEntry } from 'astro:content';
|
import type { CollectionEntry } from "astro:content";
|
||||||
import BaseHead from '../components/BaseHead.astro';
|
import BaseHead from "../components/BaseHead.astro";
|
||||||
import Header from '../components/Header.astro';
|
import Header from "../components/Header.astro";
|
||||||
import Footer from '../components/Footer.astro';
|
import Footer from "../components/Footer.astro";
|
||||||
import FormattedDate from '../components/FormattedDate.astro';
|
import FormattedDate from "../components/FormattedDate.astro";
|
||||||
|
|
||||||
type Props = CollectionEntry<'blog'>['data'];
|
type Props = CollectionEntry<"blog">["data"];
|
||||||
|
|
||||||
const { title, description, pubDate, updatedDate, heroImage } = Astro.props;
|
const { title, description, pubDate, updatedDate, heroImage, author } =
|
||||||
|
Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<BaseHead title={title} description={description} />
|
<BaseHead title={title} description={description} />
|
||||||
<style>
|
<style>
|
||||||
main {
|
main {
|
||||||
width: calc(100% - 2em);
|
width: calc(100% - 2em);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
.hero-image {
|
.hero-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.hero-image img {
|
.hero-image img {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
box-shadow: var(--box-shadow);
|
box-shadow: var(--box-shadow);
|
||||||
}
|
}
|
||||||
.prose {
|
.prose {
|
||||||
width: 720px;
|
width: 720px;
|
||||||
max-width: calc(100% - 2em);
|
max-width: calc(100% - 2em);
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
color: rgb(var(--gray-dark));
|
color: rgb(var(--gray-dark));
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
padding: 1em 0;
|
padding: 1em 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
.title h1 {
|
.title h1 {
|
||||||
margin: 0 0 0.5em 0;
|
margin: 0 0 0.5em 0;
|
||||||
}
|
}
|
||||||
.date {
|
.last-updated-on {
|
||||||
margin-bottom: 0.5em;
|
font-style: italic;
|
||||||
color: rgb(var(--gray));
|
}
|
||||||
}
|
</style>
|
||||||
.last-updated-on {
|
<!-- Set style as global so that it can apply to the rendered elements -->
|
||||||
font-style: italic;
|
<style is:global>
|
||||||
}
|
@reference "tailwindcss";
|
||||||
</style>
|
.content h2 {
|
||||||
</head>
|
font-size: 1.5em;
|
||||||
|
}
|
||||||
|
.content p {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
<body>
|
<body class="relative min-h-dvh">
|
||||||
<Header />
|
<Header />
|
||||||
<main>
|
<main>
|
||||||
<article>
|
<article class="max-w-200 m-auto flex flex-col items-center py-16">
|
||||||
<div class="hero-image">
|
{
|
||||||
{heroImage && <img width={1020} height={510} src={heroImage} alt="" />}
|
heroImage && heroImage.trim().length > 0 && (
|
||||||
</div>
|
<div class="">
|
||||||
<div class="prose">
|
<img src={heroImage} alt="" class="max-h-100" />
|
||||||
<div class="title">
|
</div>
|
||||||
<div class="date">
|
)
|
||||||
<FormattedDate date={pubDate} />
|
}
|
||||||
{
|
<div class="flex flex-col gap-2 items-center m-4">
|
||||||
updatedDate && (
|
<h1 class="text-3xl">{title}</h1>
|
||||||
<div class="last-updated-on">
|
{author && <p class="author text-sm text-medium">{author}</p>}
|
||||||
Last updated on <FormattedDate date={updatedDate} />
|
<div class="published-on text-sm text-medium">
|
||||||
</div>
|
<FormattedDate date={pubDate} />
|
||||||
)
|
</div>
|
||||||
}
|
{
|
||||||
</div>
|
updatedDate && (
|
||||||
<h1>{title}</h1>
|
<div class="last-updated-on text-sm text-medium">
|
||||||
<hr />
|
Last updated on <FormattedDate date={updatedDate} />
|
||||||
</div>
|
</div>
|
||||||
<slot />
|
)
|
||||||
</div>
|
}
|
||||||
</article>
|
</div>
|
||||||
</main>
|
<div class="content">
|
||||||
<Footer />
|
<slot />
|
||||||
</body>
|
</div>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,62 +1,54 @@
|
|||||||
---
|
---
|
||||||
import Layout from '../layouts/BlogPost.astro';
|
import Layout from "../layouts/BlogPost.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout
|
<Layout
|
||||||
title="About Me"
|
title="About Terakoda"
|
||||||
description="Lorem ipsum dolor sit amet"
|
description="About Terakoda"
|
||||||
pubDate={new Date('August 08 2021')}
|
pubDate={new Date("June 9, 2025")}
|
||||||
heroImage="/blog-placeholder-about.jpg"
|
heroImage=""
|
||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
|
Terakoda is a software cooperative. We commit to the <a
|
||||||
labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo
|
href="https://ica.coop/en/cooperatives/cooperative-identity/"
|
||||||
viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam
|
>Seven Cooperative Principles</a
|
||||||
adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus
|
>.
|
||||||
et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus
|
</p>
|
||||||
vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque
|
|
||||||
sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
<div class="members">
|
||||||
Morbi tristique senectus et netus. Id semper risus in hendrerit gravida rutrum quisque non
|
<h2>Members</h2>
|
||||||
tellus. Habitasse platea dictumst quisque sagittis purus sit amet. Tellus molestie nunc non
|
<div class="member">
|
||||||
blandit massa. Cursus vitae congue mauris rhoncus. Accumsan tortor posuere ac ut. Fringilla urna
|
<h3 class="member-name">Drew Haven</h3>
|
||||||
porttitor rhoncus dolor. Elit ullamcorper dignissim cras tincidunt lobortis. In cursus turpis
|
<p>
|
||||||
massa tincidunt dui ut ornare lectus. Integer feugiat scelerisque varius morbi enim nunc.
|
Drew has two decades of experience working in technology, from large
|
||||||
Bibendum neque egestas congue quisque egestas diam. Cras ornare arcu dui vivamus arcu felis
|
multi-nationals to small start-ups and freelance work. He specializes in
|
||||||
bibendum. Dignissim suspendisse in est ante in nibh mauris. Sed tempus urna et pharetra pharetra
|
web systems, software architecture and sustainable development.
|
||||||
massa massa ultricies mi.
|
</p>
|
||||||
</p>
|
</div>
|
||||||
|
<div class="member">
|
||||||
|
<h3 class="member-name">Lindsay Haven</h3>
|
||||||
|
<p>
|
||||||
|
Lindsay spent a decade in the video game industry bringing joy to users
|
||||||
|
through projects from large-budget MMOs and small mobile apps. She
|
||||||
|
specializes in organization and communication within teams.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style lang="less">
|
||||||
|
@reference "tailwindcss";
|
||||||
|
h2 {
|
||||||
|
@apply text-3xl text-center;
|
||||||
|
}
|
||||||
|
|
||||||
<p>
|
.members {
|
||||||
Mollis nunc sed id semper risus in. Convallis a cras semper auctor neque. Diam sit amet nisl
|
@apply my-4;
|
||||||
suscipit. Lacus viverra vitae congue eu consequat ac felis donec. Egestas integer eget aliquet
|
.member {
|
||||||
nibh praesent tristique magna sit amet. Eget magna fermentum iaculis eu non diam. In vitae
|
@apply pb-4;
|
||||||
turpis massa sed elementum. Tristique et egestas quis ipsum suspendisse ultrices. Eget lorem
|
&-name {
|
||||||
dolor sed viverra ipsum. Vel turpis nunc eget lorem dolor sed viverra. Posuere ac ut consequat
|
@apply text-xl;
|
||||||
semper viverra nam. Laoreet suspendisse interdum consectetur libero id faucibus. Diam phasellus
|
color: var(--color-accent);
|
||||||
vestibulum lorem sed risus ultricies tristique. Rhoncus dolor purus non enim praesent elementum
|
}
|
||||||
facilisis. Ultrices tincidunt arcu non sodales neque. Tempus egestas sed sed risus pretium quam
|
}
|
||||||
vulputate. Viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Fringilla
|
}
|
||||||
urna porttitor rhoncus dolor purus non. Amet dictum sit amet justo donec enim.
|
</style>
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Mattis ullamcorper velit sed ullamcorper morbi tincidunt. Tortor posuere ac ut consequat semper
|
|
||||||
viverra. Tellus mauris a diam maecenas sed enim ut sem viverra. Venenatis urna cursus eget nunc
|
|
||||||
scelerisque viverra mauris in. Arcu ac tortor dignissim convallis aenean et tortor at. Curabitur
|
|
||||||
gravida arcu ac tortor dignissim convallis aenean et tortor. Egestas tellus rutrum tellus
|
|
||||||
pellentesque eu. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Ut enim
|
|
||||||
blandit volutpat maecenas volutpat blandit aliquam etiam. Id donec ultrices tincidunt arcu. Id
|
|
||||||
cursus metus aliquam eleifend mi.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Tempus quam pellentesque nec nam aliquam sem. Risus at ultrices mi tempus imperdiet. Id porta
|
|
||||||
nibh venenatis cras sed felis eget velit. Ipsum a arcu cursus vitae. Facilisis magna etiam
|
|
||||||
tempor orci eu lobortis elementum. Tincidunt dui ut ornare lectus sit. Quisque non tellus orci
|
|
||||||
ac. Blandit libero volutpat sed cras. Nec tincidunt praesent semper feugiat nibh sed pulvinar
|
|
||||||
proin gravida. Egestas integer eget aliquet nibh praesent tristique magna.
|
|
||||||
</p>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -1,135 +1,57 @@
|
|||||||
---
|
---
|
||||||
import BaseHead from '../../components/BaseHead.astro';
|
import BaseHead from "../../components/BaseHead.astro";
|
||||||
import Header from '../../components/Header.astro';
|
import Header from "../../components/Header.astro";
|
||||||
import Footer from '../../components/Footer.astro';
|
import Footer from "../../components/Footer.astro";
|
||||||
import { SITE_TITLE, SITE_DESCRIPTION } from '../../consts';
|
import { SITE_TITLE, SITE_DESCRIPTION } from "../../consts";
|
||||||
import { getCollection } from 'astro:content';
|
import { getCollection } from "astro:content";
|
||||||
import FormattedDate from '../../components/FormattedDate.astro';
|
import FormattedDate from "../../components/FormattedDate.astro";
|
||||||
|
|
||||||
const posts = (await getCollection('blog')).sort(
|
const posts = (await getCollection("blog")).sort(
|
||||||
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
|
||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
||||||
<style>
|
</head>
|
||||||
main {
|
<body class="relative min-h-dvh">
|
||||||
width: 960px;
|
<Header />
|
||||||
}
|
<main>
|
||||||
ul {
|
<section class="max-w-200 m-auto my-16">
|
||||||
display: flex;
|
<ul>
|
||||||
flex-wrap: wrap;
|
{
|
||||||
gap: 2rem;
|
posts.map((post) => (
|
||||||
list-style-type: none;
|
<li class="border-t-2 border-medium p-4 first:border-0">
|
||||||
margin: 0;
|
<a href={`/blog/${post.id}/`}>
|
||||||
padding: 0;
|
<div class="flex flex-row gap-4">
|
||||||
}
|
{post.data.heroImage &&
|
||||||
ul li {
|
post.data.heroImage.trim().length > 0 && (
|
||||||
width: calc(50% - 1rem);
|
<div class="flex-none">
|
||||||
}
|
<img
|
||||||
ul li * {
|
width={200}
|
||||||
text-decoration: none;
|
height={200}
|
||||||
transition: 0.2s ease;
|
src={post.data.heroImage}
|
||||||
}
|
alt=""
|
||||||
ul li:first-child {
|
/>
|
||||||
width: 100%;
|
</div>
|
||||||
margin-bottom: 1rem;
|
)}
|
||||||
text-align: center;
|
<div>
|
||||||
}
|
<p class="text-medium">
|
||||||
ul li:first-child img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
ul li:first-child .title {
|
|
||||||
font-size: 2.369rem;
|
|
||||||
}
|
|
||||||
ul li img {
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
ul li a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.title {
|
|
||||||
margin: 0;
|
|
||||||
color: rgb(var(--black));
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
.date {
|
|
||||||
margin: 0;
|
|
||||||
color: rgb(var(--gray));
|
|
||||||
}
|
|
||||||
ul li a:hover h4,
|
|
||||||
ul li a:hover .date {
|
|
||||||
color: rgb(var(--accent));
|
|
||||||
}
|
|
||||||
ul a:hover img {
|
|
||||||
box-shadow: var(--box-shadow);
|
|
||||||
}
|
|
||||||
@media (max-width: 720px) {
|
|
||||||
ul {
|
|
||||||
gap: 0.5em;
|
|
||||||
}
|
|
||||||
ul li {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
ul li:first-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
ul li:first-child .title {
|
|
||||||
font-size: 1.563em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: top;
|
|
||||||
gap: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-text {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-image img {
|
|
||||||
max-height: 200px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<Header />
|
|
||||||
<main>
|
|
||||||
<section>
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
posts.map((post) => (
|
|
||||||
<li>
|
|
||||||
<a href={`/blog/${post.id}/`}>
|
|
||||||
<div class="post">
|
|
||||||
<div class="post-image">
|
|
||||||
<img width={200} height={200} src={post.data.heroImage} alt="" />
|
|
||||||
</div>
|
|
||||||
<div class="post-text">
|
|
||||||
<p class="date">
|
|
||||||
<FormattedDate date={post.data.pubDate} />
|
<FormattedDate date={post.data.pubDate} />
|
||||||
</p>
|
</p>
|
||||||
<h4 class="title">{post.data.title}</h4>
|
<h4 class="text-xl font-bold">{post.data.title}</h4>
|
||||||
|
<p>{post.data.description}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,211 +1,133 @@
|
|||||||
---
|
---
|
||||||
import BaseHead from '../components/BaseHead.astro';
|
import BaseHead from "../components/BaseHead.astro";
|
||||||
import Header from '../components/Header.astro';
|
import Header from "../components/Header.astro";
|
||||||
import Footer from '../components/Footer.astro';
|
import Footer from "../components/Footer.astro";
|
||||||
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
|
import ButtonLink from "../components/ButtonLink.astro";
|
||||||
|
import { SITE_TITLE, SITE_DESCRIPTION } from "../consts";
|
||||||
|
import HeaderLogo from "../assets/HeaderLogo.png";
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="relative min-h-dvh">
|
||||||
<Header />
|
<Header />
|
||||||
<main>
|
<div class="bg-dark text-light py-16">
|
||||||
<div class="hero">
|
<div class="flex flex-col items-center gap-8 max-w-200 m-auto px-4">
|
||||||
<div class="hero-content">
|
<img src={HeaderLogo.src} alt="Terakoda" class="max-w-full" />
|
||||||
<img src="HeaderLogo.png" alt="Terakoda Software Systems Logo" class="logo">
|
<p>
|
||||||
<p>Solving Your Unique Software Challenges with Precision and Quality</p>
|
Solving Your Unique Software Challenges with Precision and Quality
|
||||||
<a href="#contact" class="cta-button">Get a Consultation</a>
|
</p>
|
||||||
</div>
|
<ButtonLink href="#contact">Get a Consultation</ButtonLink>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="services">
|
<main>
|
||||||
<h2>Our Expertise</h2>
|
<div class="bg-light-accent py-16 text-dark">
|
||||||
<div class="services-content">
|
<div class="max-w-200 m-auto px-4">
|
||||||
|
<h2 class="text-4xl mb-8 text-center">Our Expertise</h2>
|
||||||
|
<div class="grid md:grid-cols-2 grid-cols-1 gap-8">
|
||||||
<div class="service-item">
|
<div class="service-item">
|
||||||
<h3>Custom Software Solutions</h3>
|
<h3>Custom Software Solutions</h3>
|
||||||
<p>We specialize in providing bespoke software solutions for those critical, isolated challenges that can impact your operations and growth.</p>
|
<p>
|
||||||
|
We specialize in providing bespoke software solutions for those
|
||||||
|
critical, isolated challenges that can impact your operations
|
||||||
|
and growth.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="service-item">
|
<div class="service-item">
|
||||||
<h3>Engineering Challenges</h3>
|
<h3>Engineering Challenges</h3>
|
||||||
<p>Our experienced developers can tackle complex engineering hurdles that fall outside your team's immediate expertise.</p>
|
<p>
|
||||||
|
Our experienced developers can tackle complex engineering
|
||||||
|
hurdles that fall outside your team's immediate expertise.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="service-item">
|
<div class="service-item">
|
||||||
<h3>Organizational Challenges</h3>
|
<h3>Organizational Challenges</h3>
|
||||||
<p>We offer solutions to organizational challenges that can be addressed through tailored software applications.</p>
|
<p>
|
||||||
|
We offer solutions to organizational challenges that can be
|
||||||
|
addressed through tailored software applications.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="quality">
|
<div class="py-16 max-w-200 m-auto px-4">
|
||||||
<h2>Our Commitment to Quality</h2>
|
<h2 class="text-4xl mb-8 text-center">Our Commitment to Quality</h2>
|
||||||
<div class="quality-content">
|
<div class="quality-content">
|
||||||
<p>At Terakoda Software Systems, we are dedicated to delivering solutions that meet the highest standards of quality and reliability.</p>
|
<p>
|
||||||
<ul class="quality-list">
|
At Terakoda Software Systems, we are dedicated to delivering
|
||||||
<li>Expert Analysis to understand your specific needs.</li>
|
solutions that meet the highest standards of quality and
|
||||||
<li>Custom-Crafted Solutions designed for optimal performance.</li>
|
reliability.
|
||||||
<li>Uncompromising Quality through rigorous development standards.</li>
|
</p>
|
||||||
<li>Clear Communication throughout the entire process.</li>
|
<ul class="quality-list">
|
||||||
<li>Long-Term Value ensuring lasting solutions.</li>
|
<li>Expert Analysis to understand your specific needs.</li>
|
||||||
</ul>
|
<li>Custom-Crafted Solutions designed for optimal performance.</li>
|
||||||
|
<li>
|
||||||
|
Uncompromising Quality through rigorous development standards.
|
||||||
|
</li>
|
||||||
|
<li>Clear Communication throughout the entire process.</li>
|
||||||
|
<li>Long-Term Value ensuring lasting solutions.</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="contact" id="contact">
|
<div class="py-16 bg-dark text-light" id="contact">
|
||||||
<h2>Ready to Solve Your Software Puzzle?</h2>
|
<div
|
||||||
<p>Contact us today for a consultation and let Terakoda Software Systems help you overcome your unique software challenges with precision and excellence.</p>
|
class="max-w-200 m-auto px-4 flex flex-col items-center gap-8 text-center"
|
||||||
<a href="mailto:your-email@example.com" class="contact-button">Contact Us</a>
|
>
|
||||||
</div>
|
<h2 class="text-4xl text-center">
|
||||||
</main>
|
Ready to Solve Your Software Puzzle?
|
||||||
<Footer />
|
</h2>
|
||||||
</body>
|
<p class="text-lg">
|
||||||
<style>
|
Contact us today for a consultation and let Terakoda Software
|
||||||
.logo {
|
Systems help you overcome your unique software challenges with
|
||||||
max-width: 400px; /* Adjust as needed */
|
precision and excellence.
|
||||||
height: auto;
|
</p>
|
||||||
}
|
<ButtonLink
|
||||||
|
href="mailto:contact@terakoda.com"
|
||||||
|
event="contact-us"
|
||||||
|
eventTitle="Contact Us"
|
||||||
|
>
|
||||||
|
Contact Us
|
||||||
|
</ButtonLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</body><style>
|
||||||
|
.service-item {
|
||||||
|
background-color: white;
|
||||||
|
padding: 25px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.hero {
|
.service-item h3 {
|
||||||
background-color: #1f2932; /* Dark Background */
|
color: #2eb670; /* Accent Color */
|
||||||
color: #fdfafc; /* Light Text */
|
margin-top: 0;
|
||||||
padding: 80px 20px;
|
margin-bottom: 10px;
|
||||||
text-align: center;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.hero-content {
|
.quality-list {
|
||||||
max-width: 800px;
|
list-style: none;
|
||||||
margin: 0 auto;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero h1 {
|
.quality-list li {
|
||||||
font-size: 2.5em;
|
margin-bottom: 15px;
|
||||||
margin-bottom: 20px;
|
padding-left: 25px;
|
||||||
}
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.hero p {
|
.quality-list li::before {
|
||||||
font-size: 1.2em;
|
content: "\2713"; /* Checkmark */
|
||||||
margin-bottom: 30px;
|
color: #2eb670; /* Accent Color */
|
||||||
}
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
.cta-button {
|
}
|
||||||
display: inline-block;
|
|
||||||
background-color: #2EB670; /* Accent Color */
|
|
||||||
color: #1f2932; /* Dark Text */
|
|
||||||
padding: 15px 30px;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-button:hover {
|
|
||||||
background-color: #269e61;
|
|
||||||
}
|
|
||||||
|
|
||||||
.services {
|
|
||||||
padding: 60px 20px;
|
|
||||||
text-align: center;
|
|
||||||
background-color: #f9f7f7; /* Slightly darker light background for contrast */
|
|
||||||
}
|
|
||||||
|
|
||||||
.services h2 {
|
|
||||||
font-size: 2em;
|
|
||||||
color: #1f2932; /* Dark Text */
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.services-content {
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
||||||
gap: 30px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-item {
|
|
||||||
background-color: white;
|
|
||||||
padding: 25px;
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-item h3 {
|
|
||||||
color: #2EB670; /* Accent Color */
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality {
|
|
||||||
background-color: #fdfafc; /* Light Background */
|
|
||||||
padding: 60px 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality h2 {
|
|
||||||
font-size: 2em;
|
|
||||||
color: #1f2932; /* Dark Text */
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality-content {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality-list {
|
|
||||||
list-style: none;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality-list li {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
padding-left: 25px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.quality-list li::before {
|
|
||||||
content: "\2713"; /* Checkmark */
|
|
||||||
color: #2EB670; /* Accent Color */
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact {
|
|
||||||
background-color: #1f2932; /* Dark Background */
|
|
||||||
color: #fdfafc; /* Light Text */
|
|
||||||
padding: 60px 20px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact h2 {
|
|
||||||
font-size: 2em;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact p {
|
|
||||||
font-size: 1.1em;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-button {
|
|
||||||
display: inline-block;
|
|
||||||
background-color: #2EB670; /* Accent Color */
|
|
||||||
color: #1f2932; /* Dark Text */
|
|
||||||
padding: 15px 30px;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
transition: background-color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.contact-button:hover {
|
|
||||||
background-color: #269e61;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
/*
|
@import "tailwindcss";
|
||||||
The CSS in this style tag is based off of Bear Blog's default CSS.
|
|
||||||
https://github.com/HermanMartinus/bearblog/blob/297026a877bc2ab2b3bdfbd6b9f7961c350917dd/templates/styles/blog/default.css
|
|
||||||
License MIT: https://github.com/HermanMartinus/bearblog/blob/master/LICENSE.md
|
|
||||||
*/
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--accent: #2337ff;
|
--accent: #2eb670; /* Accent Color */
|
||||||
--accent-dark: #000d8a;
|
--accent-dark: rgb(23, 91, 56);
|
||||||
--black: 15, 18, 25;
|
--black: 15, 18, 25;
|
||||||
--gray: 96, 115, 159;
|
--gray: 96, 115, 159;
|
||||||
--gray-light: 229, 233, 240;
|
--gray-light: 229, 233, 240;
|
||||||
@@ -16,140 +12,14 @@
|
|||||||
0 2px 6px rgba(var(--gray), 25%), 0 8px 24px rgba(var(--gray), 33%),
|
0 2px 6px rgba(var(--gray), 25%), 0 8px 24px rgba(var(--gray), 33%),
|
||||||
0 16px 32px rgba(var(--gray), 33%);
|
0 16px 32px rgba(var(--gray), 33%);
|
||||||
}
|
}
|
||||||
@font-face {
|
|
||||||
font-family: "Atkinson";
|
|
||||||
src: url("/fonts/atkinson-regular.woff") format("woff");
|
|
||||||
font-weight: 400;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
@font-face {
|
|
||||||
font-family: "Atkinson";
|
|
||||||
src: url("/fonts/atkinson-bold.woff") format("woff");
|
|
||||||
font-weight: 700;
|
|
||||||
font-style: normal;
|
|
||||||
font-display: swap;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: "Atkinson", sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
text-align: left;
|
|
||||||
background: linear-gradient(var(--gray-gradient)) no-repeat;
|
|
||||||
background-size: 100% 600px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
color: rgb(var(--gray-dark));
|
|
||||||
font-size: 20px;
|
|
||||||
line-height: 1.7;
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
width: 720px;
|
|
||||||
max-width: calc(100% - 2em);
|
|
||||||
margin: auto;
|
|
||||||
padding: 3em 1em;
|
|
||||||
}
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
margin: 0 0 0.5rem 0;
|
|
||||||
color: rgb(var(--black));
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 3.052em;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
font-size: 2.441em;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
font-size: 1.953em;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
font-size: 1.563em;
|
|
||||||
}
|
|
||||||
h5 {
|
|
||||||
font-size: 1.25em;
|
|
||||||
}
|
|
||||||
strong,
|
|
||||||
b {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
.prose p {
|
|
||||||
margin-bottom: 2em;
|
|
||||||
}
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
padding: 2px 5px;
|
|
||||||
background-color: rgb(var(--gray-light));
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
padding: 1.5em;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
pre > code {
|
|
||||||
all: unset;
|
|
||||||
}
|
|
||||||
blockquote {
|
|
||||||
border-left: 4px solid var(--accent);
|
|
||||||
padding: 0 0 0 20px;
|
|
||||||
margin: 0px;
|
|
||||||
font-size: 1.333em;
|
|
||||||
}
|
|
||||||
hr {
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid rgb(var(--gray-light));
|
|
||||||
}
|
|
||||||
@media (max-width: 720px) {
|
|
||||||
body {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 1em;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sr-only {
|
@theme {
|
||||||
border: 0;
|
--color-primary: rgb(46, 182, 112);
|
||||||
padding: 0;
|
--color-primary-dark: #269e61;
|
||||||
margin: 0;
|
--color-light: #fdfafc;
|
||||||
position: absolute !important;
|
/* Mix light and dark using the perceptually uniform oklab color space */
|
||||||
height: 1px;
|
--color-medium: color-mix(in oklab, var(--color-light), var(--color-dark));
|
||||||
width: 1px;
|
--color-dark: #1f2932;
|
||||||
overflow: hidden;
|
--color-light-accent: #f9f7f7; /* Slightly darker light background for contrast */
|
||||||
/* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
|
--color-accent: #2eb670; /* Accent Color */
|
||||||
clip: rect(1px 1px 1px 1px);
|
|
||||||
/* maybe deprecated but we need to support legacy browsers */
|
|
||||||
clip: rect(1px, 1px, 1px, 1px);
|
|
||||||
/* modern browsers, clip-path works inwards from each corner */
|
|
||||||
clip-path: inset(50%);
|
|
||||||
/* added line to stop words getting smushed together (as they go onto separate lines and some screen readers do not understand line feeds as a space */
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user