100 Commits

Author SHA1 Message Date
brookechr f8265574dc Merge branch 'main' into strawberry/7-26-fixes 2026-07-20 19:04:48 -07:00
jjjj 01f188f95c update hire us link
continuous-integration/drone/push Build is failing
2026-07-16 11:47:29 -07:00
jjjj 908f90ad04 Merge pull request 'add robots.txt' (#13) from strawberry/add-robots-txt into main
continuous-integration/drone/push Build is failing
Reviewed-on: #13
2026-07-16 18:36:54 +00:00
brookechr 30ce20565f public hire us link 2026-07-10 18:18:16 -07:00
brookechr 80e44e9dca add robots.txt 2026-07-08 17:43:22 -07:00
brookechr a2d4cfc764 more cleanup/styling fixes 2026-07-08 17:31:48 -07:00
brookechr 9f37d75a99 small tweaks based on feedback 2026-07-08 16:55:38 -07:00
jjjj 9604c4ba70 Merge pull request 'adding service offerings and a few styling fixes for mobile' (#11) from strawberry/service-offerings into main
continuous-integration/drone/push Build is failing
Reviewed-on: #11
2026-07-07 01:34:12 +00:00
brookechr 7df9adc69a adding service offerings and a few styling fixes for mobile 2026-07-06 16:25:14 -07:00
brookechr 696596068f Merge pull request 'Strawberry/06 26 website updates 2.0' (#10) from strawberry/06-26-website-updates into main
continuous-integration/drone/push Build is failing
Reviewed-on: #10
2026-07-01 00:30:56 +00:00
brookechr 9aed60fc01 lots of changes 2026-06-22 20:28:11 -07:00
brookechr 27647ea1f9 fixing formatting of new look navbar and projects section 2026-06-17 10:17:20 -07:00
jjjj ee4ef882dc add a /newlook page that contains all info on one page
continuous-integration/drone/push Build is failing
2026-06-15 17:59:21 -07:00
jjjj 41c21aa463 Remove zines' flipbook view, as it's hard to get the zoom working with that 2026-06-10 13:58:26 -07:00
jjjj 6cad58a332 Enable individual zine sharing by link 2026-05-07 20:23:10 -07:00
jjjj 897ef35747 add signal 201 2026-05-07 19:51:13 -07:00
jjjj a9366cb48b allow zooming when reading zines 2026-03-30 20:58:57 -07:00
brookechr aba9f78b03 Merge pull request 'fixing out of order pages' (#5) from strawberry/discotech-101-pages-out-of-order into main
Reviewed-on: #5
2026-03-17 02:21:04 +00:00
brookechr 9beac5d1b1 fixing out of order pages 2026-03-16 19:19:54 -07:00
brookechr 207ba77559 Merge pull request 'Adding Signal 101 Zine and Making Some Capitalization/Naming Fixes for Other Zines' (#4) from strawberry/signal-101-zine into main
Reviewed-on: #4
2026-02-26 04:58:24 +00:00
brookechr e87c8efd66 updates to zine data file 2026-02-25 20:56:16 -08:00
brookechr e67d93768e signal 101 printable 2026-02-25 20:55:40 -08:00
brookechr ef95670283 rename printable 2026-02-25 20:55:01 -08:00
brookechr d0c7861a1b rename De-monopoly Cheatsheet to Brief Notes from a De-Monopoly DiscoTech 2026-02-25 20:54:23 -08:00
brookechr fdaf49d6ca adding signal 101 zine pages 2026-02-25 20:52:21 -08:00
jjjj 9ed1c4c5b8 add a new zine 2026-02-02 15:56:16 -08:00
jjjj 630f53ea5d 1. fix copyleft year and color in darkmode 2. remove duplicate ourprojects title 2026-01-26 21:48:58 -08:00
jjjj b08aaf416f remove duplicate entries in the projects page 2026-01-21 08:30:13 -08:00
jjjj d63fddc3ba Change printable zine permission so they can be downloaded; rename flipbookviewer to zineviewer 2026-01-21 08:20:25 -08:00
jordansullivan 65afc10fc6 Fixing all the stuff I effed up in rebasing 2026-01-19 22:44:08 -08:00
jordansullivan 8c771fac6a pet calendar updates 2026-01-19 22:44:08 -08:00
jordansullivan e42b75951a Add all the tutorial links babyyyy 2026-01-19 22:44:08 -08:00
jordansullivan f01be89d91 Create pet calendar page 2026-01-19 22:44:08 -08:00
jjjj d5412264dc rename ../ourprojectspages/... to just .../projects/... in the page route 2026-01-19 22:44:08 -08:00
jjjj 87351bafda add zines to our projects 2026-01-19 22:44:08 -08:00
jjjj 821ef086eb update bookclub page; rename What Were Working On to Our Projects in the code 2026-01-19 22:44:08 -08:00
jordansullivan bc4d1dc4db Merge pull request 'Create pet calendar page' (#3) from js/petcal into main
Reviewed-on: #3
2026-01-20 06:42:35 +00:00
ammaratef45 5b4e5694d6 Merge branch 'main' into js/petcal 2026-01-19 22:13:06 -08:00
jordansullivan 51ba08d267 Fixing all the stuff I effed up in rebasing 2026-01-19 21:47:23 -08:00
jordansullivan bedc9997ea pet calendar updates 2026-01-19 21:37:30 -08:00
jordansullivan aa14a03761 Add all the tutorial links babyyyy 2026-01-19 21:37:13 -08:00
jordansullivan 760c3d0794 Create pet calendar page 2026-01-19 21:36:10 -08:00
jjjj 152995cc0b rename ../ourprojectspages/... to just .../projects/... in the page route 2026-01-19 21:35:51 -08:00
jjjj 8680dcfd2b add zines to our projects 2026-01-19 21:35:34 -08:00
jjjj 6cffcd0113 update bookclub page; rename What Were Working On to Our Projects in the code 2026-01-19 21:34:04 -08:00
jjjj 5c6e8a60a0 Update Our Points of Unity 2026-01-19 21:32:54 -08:00
jjjj 717b91ed70 make navbar buttons less dark in light mode 2026-01-19 21:32:54 -08:00
jjjj 10da042ad2 1. Rename What we're working on to our projects 2. make them look clickable 2026-01-19 21:32:54 -08:00
jjjj 26a7be8070 remove stale alternatives 2026-01-19 21:32:54 -08:00
jordansullivan 5f4eb9a6f6 Add all the tutorial links babyyyy 2026-01-19 21:22:02 -08:00
jjjj 37d49af3e4 rename ../ourprojectspages/... to just .../projects/... in the page route 2026-01-18 22:38:15 -08:00
jjjj 956c42811d add zines to our projects 2026-01-18 21:56:55 -08:00
jjjj 8d2b02cc1a update bookclub page; rename What Were Working On to Our Projects in the code 2026-01-18 21:02:11 -08:00
jjjj edd81e5bd6 Update Our Points of Unity 2026-01-18 20:33:54 -08:00
jjjj a6d355a826 make navbar buttons less dark in light mode 2026-01-15 21:18:37 -08:00
jordansullivan 66378e97a9 Create pet calendar page 2026-01-15 21:10:16 -08:00
jjjj be46a62157 1. Rename What we're working on to our projects 2. make them look clickable 2026-01-15 20:50:38 -08:00
jjjj d6a89e76b0 remove stale alternatives 2026-01-15 20:31:54 -08:00
brookechr 9246961f22 Merge pull request 'updating readme with deployment info' (#2) from strawberry/readme-deployment-info-update into main
Reviewed-on: #2
Reviewed-by: ammaratef45 <ammaratef45@proton.me>
2025-10-17 04:22:59 +00:00
brookechr 26582aebbc addressing feedback 2025-10-16 21:22:14 -07:00
brookechr 255f360825 adding instructions for clone of sootie-config 2025-10-14 12:18:01 -07:00
brookechr 695431dbc0 updating readme with deployment info 2025-10-14 12:12:01 -07:00
brookechr 7c52ab5b78 adding discussion guide to book club 2025-10-13 22:33:46 -07:00
brookechr 314010cbf6 adjusting book club pic width 2025-10-13 20:12:52 -07:00
brookechr 977e5c84ca add details to readme 2025-10-13 19:52:16 -07:00
jjjj 05fcd26c6e update version to 0.0.8 2025-10-13 19:17:42 -07:00
brookechr ffd26c42ca fixing horizontal sizing issue in navbar 2025-10-09 19:54:42 -07:00
brookechr 0c440ac44c updating book club page 2025-10-09 19:52:03 -07:00
jjjj 57f322a7b0 update README with deployment instruction 2025-09-01 15:02:50 -07:00
jjjj 0e236f67a8 update tags 2025-09-01 13:31:39 -07:00
jjjj 726de1a0db strike through big tech option 2025-09-01 13:26:42 -07:00
jjjj a04e99c7f0 remove this website from what we are working on + add alternatives page 2025-09-01 13:17:24 -07:00
jjjj 2a9aeb5a46 add alt page 2025-08-19 16:08:26 -07:00
ammaratef45 e07d5a80c8 update to 0.0.6 version 2025-07-31 22:10:29 -07:00
ammaratef45 7b7b2edc87 update version to 0.0.3 2025-07-31 21:56:41 -07:00
ammaratef45 14e2e18f54 add a heart symbol to points of unity 2025-07-31 21:53:41 -07:00
ammaratef45 2d224b8f7d add build badge 2025-07-31 21:45:32 -07:00
ammaratef45 54a544b3c1 add .drone.yml 2025-07-31 21:40:50 -07:00
brookechr fe3ad8eea6 update README and get rid of astro embed 2025-06-16 21:02:43 -07:00
brookechr 635d4a1b21 Merge branch 'main' of https://git.coopcloud.tech/RTM/rtm-website 2025-06-13 17:21:34 -07:00
brookechr e39a0f3b30 update computer icon and add commented out calendar page 2025-06-13 17:21:26 -07:00
jjjj a3cc09bf2f add details for book club 2025-06-09 20:23:35 -07:00
jjjj dff21eb51a make the computer/people icon numbers not hard coded 2025-05-22 21:01:57 -07:00
jjjj 5a53feb817 1. remove ../public from asset links
2. add book club page
2025-05-22 20:36:44 -07:00
Ammar Hussein 7dbbfbe479 deployment configs and instructions 2025-05-15 19:57:46 -07:00
brookechr fd77268cbe polishing! 2025-05-08 19:07:23 -07:00
brookechr 9f3e1b21ef Merge branch 'main' of https://git.coopcloud.tech/RTM/rtm-website 2025-05-01 20:51:03 -07:00
brookechr e502445a8d movin pictures around 2025-05-01 20:51:01 -07:00
jjjj b1d955edc1 format the points of unity 2025-05-01 20:49:06 -07:00
jjjj 2991171654 fix computer and people icons 2025-05-01 20:44:27 -07:00
jjjj b6a68f5435 highlight the current page on the navbar 2025-05-01 20:25:42 -07:00
jjjj 25166508e5 add the sign up button 2025-05-01 19:59:21 -07:00
brookechr 8ed99cd3b2 navbar code cleanup 2025-05-01 19:55:42 -07:00
brookechr c3f941b5d2 h1 title styling 2025-05-01 19:55:04 -07:00
brookechr d44ce6b8a2 uppercase page names 2025-05-01 19:44:25 -07:00
jjjj c8c6b6f7cd Redirect to home page when clicking RTM 2025-05-01 19:26:46 -07:00
brookechr f908c69635 adding tailwind docs to readme 2025-05-01 19:26:05 -07:00
brookechr 5b56c3e8d8 making UI work 2025-05-01 19:22:40 -07:00
brookechr 67208841de Merge branch 'main' of https://git.coopcloud.tech/RTM/rtm-website 2025-05-01 18:51:17 -07:00
jjjj e07be531a4 add instruction to readme 2025-04-24 20:32:21 -07:00
47 changed files with 992 additions and 1018 deletions
+55 -11
View File
@@ -55,7 +55,7 @@ version=<specify-version>
docker build --platform linux/amd64 -t git.coopcloud.tech/rtm/rtmwebsite:$version .
```
## Push the image to gitea registry
## Push the image to gitea registery
Check out [this documentation](https://docs.gitea.com/next/usage/packages/container) for how to login with gitea registery.
@@ -66,19 +66,63 @@ docker push git.coopcloud.tech/rtm/rtmwebsite:$version
At [our co-op cloud's packages site](https://git.coopcloud.tech/RTM/-/packages), click on "rtmwebsite" and check that the version mentioned is the version you specified!
## Deploy changes to resisttechmonopolies.online
## Update recipe
We use coop cloud tooling [private recipe](https://git.coopcloud.tech/RTM/rtm-astro-recipe) to deploy this website to our [fleet](https://git.coopcloud.tech/RTM/rtm-config) of lil cat-named machines.
We use a [private recipe](https://git.coopcloud.tech/RTM/rtm-astro-recipe) to deploy this website. This step needs Wireguard to be activated (download Wireguard and ask Sootie's owner to create a config for you and give you Docker permissions). The following examples will assume your name in Sootie's config is "blueberry"!
Read the "Fleet Setup and access" collectives page on our RTM nextcloud to get the `rtm-config` repo set up with the `rtm-astro-recipe` submodule and install the `abra` command line tool!
You will need to have wget (`brew install wget` on mac) and [abra](https://docs.coopcloud.tech/abra/) installed.
Then, in your `rtm-config` repo update the RTM website image version to the one you just built and published by running:
Create an SSH key to use with Sootie with the following command. Take note of the file where you save the key. The following examples will assume it is saved to `rtm` and that the `.ssh` directory is in your home directory (which you can find with the command `echo $HOME`).
``` bash
$ abra app config resisttechmonopolies.online # Change VERSION to the docker image you just pushed
$ abra app deploy -f resisttechmonopolies.online # Re-deploy the RTM website, now with your changes!
$ git add abra/servers/laylotta.resisttechmonopolies.online/resisttechmonopolies.online
$ git commit -m 'Updated website to x.x.x' # Publish this change to the rtm-config repo either via direct commit or a PR
```ssh-keygen -t ed25519```
Run the following commands to install the SSH key to Sootie as an authorized key:
```
ssh-copy-id -i $HOME/.ssh/rtm.pub blueberry@resisttechmonopolies.online
ssh -i $HOME/.ssh/rtm 'blueberry@resisttechmonopolies.online'
```
Done! Thank you for your contributions 🏋️⚡📖!
In the `$HOME/.ssh/config` file (which you may have to create if it does not exist), paste the following:
```
Host resisttechmonopolies.online
Hostname resisttechmonopolies.online
User blueberry
UseKeychain yes
IdentityFile ~/.ssh/rtm
```
You should now be able to SSH into Sootie with just the command `ssh resisttechmonopolies.online`
Run the following command (outside of the terminal in which you ran ssh in the previous step)
```abra server add resisttechmonopolies.online```
Clone the `sootie-config` repo into your `$HOME/.abra/servers/resisttechmonopolies.online` directory:
``` bash
git clone https://git.coopcloud.tech/RTM/sootie-config.git .
# DON'T FORGET THE . AT THE END OF THE COMMAND
```
Clone the `rtm-astro-recipe` repo into your `$HOME/.abra/recipes` directory:
```git clone https://git.coopcloud.tech/RTM/rtm-astro-recipe.git```
Update the version number to the latest in
``` bash
.abra/servers/resisttechmonopolies.online/resisttechmonopolies.online.env
```
Then
``` bash
abra app undeploy resisttechmonopolies.online
# wait 10 seconds
abra app deploy resisttechmonopolies.online
```
-29
View File
@@ -26,7 +26,6 @@
"nanostores": "^0.11.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-pageflip": "^2.0.3",
"tailwind-merge": "^2.5.5",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
@@ -5177,12 +5176,6 @@
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
},
"node_modules/page-flip": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/page-flip/-/page-flip-2.0.7.tgz",
"integrity": "sha512-96lQFUUz7r/LZzEUZJ3yBIMEKU9+m8HMFDzTvTdD6P7Ag/wXINjp9n0W7b4wanwnDbQETo4uNUoL3zMqpFxwGA==",
"license": "MIT"
},
"node_modules/parse-latin": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz",
@@ -5676,15 +5669,6 @@
"react": "^19.0.0"
}
},
"node_modules/react-pageflip": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/react-pageflip/-/react-pageflip-2.0.3.tgz",
"integrity": "sha512-k81mHhRvUM52y8jyzTCh5t4O0lepkLhp+XGSUzq2C3uD+iW99Cv0jfRlqFCjZbD5N3jKkIFr7/3giucoXKDP3Q==",
"license": "MIT",
"dependencies": {
"page-flip": "latest"
}
},
"node_modules/react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
@@ -10307,11 +10291,6 @@
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
},
"page-flip": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/page-flip/-/page-flip-2.0.7.tgz",
"integrity": "sha512-96lQFUUz7r/LZzEUZJ3yBIMEKU9+m8HMFDzTvTdD6P7Ag/wXINjp9n0W7b4wanwnDbQETo4uNUoL3zMqpFxwGA=="
},
"parse-latin": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz",
@@ -10563,14 +10542,6 @@
"scheduler": "^0.25.0"
}
},
"react-pageflip": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/react-pageflip/-/react-pageflip-2.0.3.tgz",
"integrity": "sha512-k81mHhRvUM52y8jyzTCh5t4O0lepkLhp+XGSUzq2C3uD+iW99Cv0jfRlqFCjZbD5N3jKkIFr7/3giucoXKDP3Q==",
"requires": {
"page-flip": "latest"
}
},
"react-refresh": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
-1
View File
@@ -27,7 +27,6 @@
"nanostores": "^0.11.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-pageflip": "^2.0.3",
"tailwind-merge": "^2.5.5",
"tailwindcss": "^3.4.17",
"tailwindcss-animate": "^1.0.7",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 KiB

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
User-agent: *
Disallow: /
+122
View File
@@ -0,0 +1,122 @@
---
import Card from "./Card.astro";
---
<div class="pl-4 pr-4">
<h1 class="pb-4 pt-4 text-3xl font-semibold">Big Tech Alternatives</h1>
<p class="mb-4">
Explore open-source and privacy-focused alternatives to popular big tech
software.
</p>
<div class="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
<Card
title="Browsers"
bigTech="Chrome (Google)"
alternatives={[
{
name: "Firefox",
description: "Privacy-focused browser.",
url: "https://www.mozilla.org/firefox",
},
]}
bgClass="bg-blue-50"
/>
<Card
title="Cloud Storage"
bigTech="Google Drive"
alternatives={[
{
name: "Nextcloud",
description: "Self-hosted file sync and share.",
url: "https://nextcloud.com",
},
]}
bgClass="bg-indigo-50"
/>
<Card
title="Email"
bigTech="Gmail (Google)"
alternatives={[
{
name: "ProtonMail",
description: "Encrypted email.",
url: "https://protonmail.com",
},
]}
bgClass="bg-yellow-50"
/>
<Card
title="Maps"
bigTech="Google Maps"
alternatives={[
{
name: "OpenStreetMap",
description: "Community-driven maps.",
url: "https://www.openstreetmap.org",
},
]}
bgClass="bg-teal-50"
/>
<Card
title="Messaging"
bigTech="WhatsApp (Meta), Slack"
alternatives={[
{
name: "Signal",
description: "Encrypted messaging (alternative to WhatsApp).",
url: "https://signal.org",
},
{
name: "Element",
description: "Matrix-based chat (alternative to Slack).",
url: "https://element.io",
},
]}
bgClass="bg-red-50"
/>
<Card
title="Search Engines"
bigTech="Google Search"
alternatives={[
{
name: "DuckDuckGo",
description: "Privacy-protecting search.",
url: "https://duckduckgo.com",
},
]}
bgClass="bg-green-50"
/>
<Card
title="Social Media"
bigTech="Twitter (X), Facebook (Meta)"
alternatives={[
{
name: "Mastodon",
description: "Decentralized social network (alternative to Twitter).",
url: "https://joinmastodon.org",
},
]}
bgClass="bg-purple-50"
/>
<Card
title="Video Platforms"
bigTech="YouTube (Google)"
alternatives={[
{
name: "PeerTube",
description: "Decentralized video hosting.",
url: "https://joinpeertube.org",
},
]}
bgClass="bg-pink-50"
/>
</div>
</div>
+20 -9
View File
@@ -10,15 +10,26 @@ const { title, bigTech, alternatives, bgClass } = Astro.props;
---
<div class={`border rounded-lg p-6 shadow-md ${bgClass} text-gray-900`}>
<h2 class="text-2xl font-semibold mb-4">{title}</h2>
<p class="text-base mb-4"><span class="line-through">Big Tech Option: {bigTech}</span></p>
<h2 class="mb-4 text-2xl font-semibold">{title}</h2>
<p class="mb-4 text-base">
<span class="line-through">Big Tech Option: {bigTech}</span>
</p>
<ul class="space-y-4">
{alternatives.map(alt => (
<li>
<h3 class="text-lg font-bold underline">{alt.name}</h3>
<p>{alt.description}</p>
<a href={alt.url} class="text-blue-500 hover:text-blue-700">Website</a>
</li>
))}
{
alternatives.map((alt) => (
<li>
<h3 class="text-lg font-bold underline">{alt.name}</h3>
<p>{alt.description}</p>
<a
href={alt.url}
class="text-blue-500 hover:text-blue-700"
rel="noopener noreferrer"
target="_blank"
>
Website
</a>
</li>
))
}
</ul>
</div>
+12 -2
View File
@@ -4,10 +4,20 @@ export default function Footer() {
return (
<div
className={cn(
"flex h-12 w-screen flex-col place-items-center justify-center bg-white dark:bg-black dark:text-white",
"flex h-12 gap-4 mt-8 w-screen place-items-center justify-center bg-[#dfd9ffff] text-black",
)}
>
<h2>Copyleft {new Date().getFullYear()}</h2>
<h2>Resist Tech Monopolies - Copyleft {new Date().getFullYear()}</h2>
<a href="https://social.coop/@rtm" rel="noopener noreferrer" target="_blank"
>
<img
src={"/assets/mastodon.png"}
alt={"the logo for Mastodon (a purple text bubble with the letter M)"}
width="30"
height="30"
/></a>
</div>
);
}
+76 -48
View File
@@ -1,63 +1,91 @@
import { cn } from "@/lib/utils";
import { useState } from "react";
interface NavbarProps {
activePage?: string;
}
export default function Navbar({ activePage = "" }: NavbarProps) {
const NAV_ITEMS = [
{ href: "/#aboutus", label: "About Us", activeKey: "AboutUs", text: "text-red-400", darkText: "dark:text-red-400" },
{ href: "/#whatwereupto", label: "What We're Up To", activeKey: "WhatWereUpTo", text: "text-orange-400", darkText: "dark:text-orange-400" },
{ href: "/#services", label: "Services", activeKey: "Services", text: "text-green-400", darkText: "dark:text-green-400" },
{ href: "/#bigtechalternatives", label: "Big Tech Alternatives", activeKey: "BigTechAlternatives", text: "text-purple-400", darkText: "dark:text-purple-400" },
// TODO once we have this section done!
// { href: "", label: "Calendar", activeKey: "Calendar", text: "text-blue-400", darkText: "dark:text-blue-400" },
];
export default function Navbar(navbarProps: NavbarProps) {
const [activePage, setActivePage] = useState(navbarProps.activePage);
const [isShowingHamburgerFullMenu, setIsShowingHamburgerFullMenu] = useState(false);
return (
<>
<div className={cn("flex flex-row justify-between pb-8 pl-4 pr-4 pt-8")}>
<div>
<strong className={cn("sm:text-2xl md:text-4xl")}>
<div className={cn("bg-white dark:bg-black px-4")}>
<div className={cn("flex flex-row items-center justify-between pb-4 pt-4")}>
<a href="/" onClick={() => { setActivePage("Home") }}>
<div className={cn("flex items-center gap-3")}>
{/*
TODO: uncomment once we have a logo!
<img
src={"/assets/logo.png"}
alt={"the logo for RTM (a cat in graffiti style with the RTM written in red and Resist Tech Monopolies written in black)"}
width="50"
height="50"
/> */}
<h1 className={cn(
"text-2xl md:text-3xl font-bold",
activePage === "Home" && "underline",
)}>Resist Tech Monopolies</h1>
</div>
</a>
<div className={cn("items-center gap-4 hidden md:flex")}>
{NAV_ITEMS.map(({ href, label, activeKey, text, darkText }) => (
<a
key={href}
href={href}
onClick={() => {
setActivePage(activeKey)
}}
className={cn(
activePage === "Home" && "border-b-2 border-black-500",
"transition-colors md:text-2xl font-bold",
text,
darkText,
activePage == activeKey && "underline",
)}
href="/">Resist Tech Monopolies</a>
</strong>
</div>
<strong className={cn("sm:text-1xl flex gap-10 md:text-4xl")}>
<a
href="/PointsOfUnity/"
className={cn(
"rounded-full px-6 py-2 transition-colors",
"bg-gray-200 text-green-700 hover:bg-gray-300",
"dark:bg-gray-800 dark:text-green-400 dark:hover:bg-gray-700",
activePage === "PointsOfUnity" && "ring-2 ring-green-500 ring-offset-2",
)}
>
Points of Unity
</a>
<a
href="/projects/"
className={cn(
"rounded-full px-6 py-2 transition-colors",
"bg-gray-200 text-red-700 hover:bg-gray-300",
"dark:bg-gray-800 dark:text-red-400 dark:hover:bg-gray-700",
activePage === "OurProjects" && "ring-2 ring-red-500 ring-offset-2",
)}
>
Our Projects
</a>
</strong>
</div>
<div className={cn("flex flex-row justify-between pb-8")}>
<div className="w-full grid grid-flow-col">
{Array.from({ length: 12 }).map((_, index) => (
<div key={index} className="flex justify-center">
<img
src={index % 2 === 0 ? "/assets/computer.png" : "/assets/people.png"}
alt={index % 2 === 0 ? "computer icon" : "people icon"}
width="30"
height="30"
/>
</div>
>
{label}
</a>
))}
</div>
<div className={cn("hamburger block md:hidden")} onClick={() => { setIsShowingHamburgerFullMenu(!isShowingHamburgerFullMenu) }}>
<span className={cn("bg-black dark:bg-white line")}></span>
<span className={cn("bg-black dark:bg-white line")}></span>
<span className={cn("bg-black dark:bg-white line")}></span>
</div>
</div>
</>
{isShowingHamburgerFullMenu && <div className={cn("flex md:hidden flex-col gap-4 bg-white p-2 mb-2")}>
{NAV_ITEMS.map(({ href, label, activeKey, text, darkText }) => (
<a
key={href}
href={href}
onClick={() => {
setActivePage(activeKey)
setIsShowingHamburgerFullMenu(false)
}}
className={cn(
"transition-colors text-xl md:text-2xl font-bold",
text,
darkText,
activePage == activeKey && "underline",
)}
>
{label}
</a>
))}
</div>}
</div>
);
}
+130
View File
@@ -0,0 +1,130 @@
<h1 class="text-3xl font-semibold">Points of Unity</h1>
<p class="mb-4">
Our community agreements and decision-making process describe <em>how</em> we work
together. The points of unity are the broader philosophies that we're working towards
as a group. Perhaps people aren't 100% aligned individually, but this is what the
group as a whole believes in when showing up to the work.
</p>
<h2 class="mb-2 mt-10 text-2xl font-semibold">Grounding</h2>
<p class="mb-4">
We situate ourselves in our context - as Seattle residents and as users and
creators of the technologies we aim to resist. We are seeing a long-spanning
rise of fascism within national governments here and abroad. In parallel is
the increasing power of tech companies over our lives through surveillance and
the provisioning of everyday needs, from employment to how we get our internet
utilities in the first place. There are rising income disparities across the
nation, but also more specifically across everyone who can be considered a
"tech worker": from a CEO, to a software engineer to a tech campus cafeteria
worker to a child enslaved to mine cobalt in the Congo.
</p>
<p class="mb-4">
We know that technology intersects with and amplifies systems of oppression,
from anti-Black racism to classism, ableism, cisheteropatriarchy, etc. Big
tech permeates all aspects of life in Seattle, but to name a few ways:
</p>
<ul class="mb-4 list-disc space-y-2 pl-8">
<li>
The rise of a <a
href="https://stopsurveillancecity.wordpress.com/"
class="font-bold underline hover:text-gray-600"
rel="noopener noreferrer"
target="_blank">Surveillance City</a
> in Seattle facilitates the tracking of out-of-state people seeking abortions,
the kidnapping of migrants, and the increased surveillance and criminalization
of sex workers, the visibly unhoused, and poor and racialized communities.
</li>
<li>
The <strong>environmental racism</strong> and community impacts of <strong
>data centers</strong
>, both from the AI bubble and the demand for more data processing that
comes with surveillance capitalism.
</li>
<li>
<strong>The AI genocide</strong> in Palestine abetted by Google, Amazon, Boeing
and Microsoft, all local Seattle employers.
</li>
<li>
Rising <strong>gentrification</strong> and <strong>income inequality</strong
> that comes from financialization of real estate and the gentrifying waves of
tech workers moving into the city.
</li>
</ul>
<p class="mb-4">
Some of us are employed by Big Tech companies, and we are all living on stolen
Coast Salish land covered by the broken Treaty of Point Elliot of 1855; we
understand that we are contributing - directly or indirectly - to these
oppressions, and seek to use our skills towards more liberatory ends.
</p>
<h2 class="mb-2 mt-10 text-2xl font-semibold">Theory of Change</h2>
<p class="mb-4">
We are committed to fostering our collective understanding of resisting tech
monopolies and building a constellation of alternative technologies that exist
outside of exploitative, oppressive systems. We seek out radical possibilities
by building connections across our communities with others struggling to
resist tech monopolies and reject work that expands the reach of these
monopolies.
</p>
<p class="mb-4">
We don't need to passively wait around for somebody to come implement these
solutions. Making improvements to our lives and our communities paves the way
for collective liberation. Dismantling smaller elements of a system is
practice for uprooting the whole and replacing it with a constellation of
liberatory alternatives.
</p>
<h2 class="mb-2 mt-10 text-2xl font-semibold">Role of Technology</h2>
<p class="mb-4">
"We believe our communities and organizing efforts can and should harness the
possibilities of new technology." (<a
href="https://mijente.net/our-dna/"
class="underline hover:text-gray-600"
rel="noopener noreferrer"
target="_blank">Mijente</a
>) However, we don't innovate for the sake of innovation, but aim to create
with intention, listen to the perspectives of those we hope to serve
(including ourselves), and hold each other accountable in this work.
</p>
<h2 class="mb-2 mt-10 text-2xl font-semibold">Our Position in the Movement</h2>
<p class="mb-4">
We don't see ourselves as a vanguard organization - that is, we make no claims
to being the sole authority on resisting tech monopolies and we will never
tell a front-line community or group of tech users what they actually need.
Everyone benefits from the work of resisting tech monopolies, and for that
reason we do the work for ourselves as much as we do it for anybody we are in
solidarity with.
</p>
<p class="mb-4">
However we also recognize our own privilege in being highly-skilled tech
workers: because of this we center the voices of the people most vulnerable to
various tech monopolies, and aim to share our expertise so that others can
also become agents in co-creating technologies of liberation and resistance.
</p>
<h2 class="mb-2 mt-10 text-2xl font-semibold">Friendly Vibes</h2>
<p class="mb-4">
Holding all of the above, we're also a group of friends here for the scrappy,
hacky vibes. We don't take our work too seriously and we're here to work
joyfully. This is first and foremost a community.
</p>
<h2 class="mb-2 mt-10 text-2xl font-semibold">Acknowledgments</h2>
<p class="mb-4">
These points of unity were inspired by the Prison Library Solidarity Network,
the Coalition of Anti-Racist Whites, Ruha Benjamin's work on Abolitionist
Tech, and Mijente.
</p>
+47
View File
@@ -0,0 +1,47 @@
<h1 class="text-3xl font-semibold">Services</h1>
<section class="flex flex-col">
<h2 class="text-2xl font-semibold mt-4">Our Mission</h2>
We promote free and open source options with a non-judgmental attitude. We understand
that almost everyone ends up using Big Tech software sometimes. We want to help
you meet your own goals, whether it be reducing what you pay big corporations or
increasing your privacy.
<h2 class="text-2xl font-semibold mt-4">Managed Service Hosting</h2>
We offer individuals and organizations access to a suite of open-source, privacy-friendly
tools, including: • NextCloud (alternative to Google Workspace, Microsoft 365,
etc.) • Loomio (democratic, asynchronous decision-making platform) • Zulip (alternative
to Slack, Teams, Discord) • Keycloak (Single Sign-On/SSO) • Vaultwarden (password
manager) • Headscale (VPN) • Matrix (decentralized, federated alternative to Discord)
• Immich (alternative to Google Photos) Our team has decades of collective expertise
in cloud computing and open-source software. We're patient, responsive to users'
needs, and skilled at translating from tech jargon to plain language.
<h2 class="text-2xl font-semibold mt-4">Data Migration</h2>
We know a lot of people want to move away from the Big Tech ecosystem but feel
overwhelmed and don't know where to start. We've been doing this for years, and
we know how to help you move your data away from Big Tech and select appropriate,
privacy-friendly, open-source alternatives that still meet your needs. We offer
individual, organization, and family pricing.
<h2 class="text-2xl font-semibold mt-4">Training</h2>
We offer in-person events to help individuals and organizations understand the
risks of Big Tech, improve their digital security, and learn about privacy-friendly,
open-source alternatives. Here are a few of the events we've held: • DiscoTechs
(science-fair style event where you can learn about a variety of alternatives to
Big Tech) • Digital Security workshops (hands-on sessions to improve your security
posture and understand what threats are actually relevant for you and your organization)s
• Office Hours (drop-in IT help)
<h2 class="text-2xl font-semibold mt-4">Tech Consulting</h2>
Does your organization need help choosing IT tools? Need a website built or hosted?
Maybe you want to self-host your own server infrastructure, but need a little support.
Whatever your tech needs, just drop us a line, and we'll see if we can help.
<a
href="https://nextcloud.resisttechmonopolies.online/apps/forms/s/L2aP5oxnbmJcot9CPYGmKAQy"
class="max-w-[120px] rounded-sm bg-[#4ade80] my-4 px-6 py-3 text-center font-semibold text-white transition-colors"
>
Hire us!
</a>
</section>
+48
View File
@@ -0,0 +1,48 @@
<h1 class="pb-4 text-3xl font-semibold">What We're Up To</h1>
<div class="grid max-w-[1600px] grid-cols-1 gap-8 xl:grid-cols-3">
<a href="/projects/bookclub/" class="grid justify-items-center gap-8">
<img
src="/assets/book-club.png"
alt="4 books we've read: Common Cicruits, Race After Technology, Teaching Community Technology, and Internet for the People"
loading="lazy"
class="h-[250px] rounded-[4px] object-contain xl:object-scale-down"
/>
<div
class="max-w-[240px] rounded-sm bg-[#fb923c] px-6 py-3 text-center font-semibold text-white transition-colors"
>
What We're Reading
</div>
</a>
<a href="/projects/PetCalendar/" class="grid justify-items-center gap-8">
<img
src="/assets/pet-calendar-2026-cover.jpg"
alt="The cover of our pet calendar; a collage of dogs and cats with computer stuff"
loading="lazy"
class="h-[250px] rounded-[4px] object-contain xl:object-scale-down"
/>
<div
class="max-w-[240px] rounded-sm bg-[#fb923c] px-6 py-3 text-center font-semibold text-white transition-colors"
>
Pet Calendar
</div>
</a>
<a href="/projects/zines/" class="grid justify-items-center gap-8">
<img
src="/assets/zines-thumbnail.png"
alt="4 zines we made: Creating an Internet for the People, De-Monopoly DiscoTech 101, How to Get Started with Signal, and Brief Notes from a De-Monopoly DiscoTech"
loading="lazy"
class="h-[250px] rounded-[4px] object-contain xl:object-scale-down"
/>
<div
class="max-w-[240px] rounded-sm bg-[#fb923c] px-6 py-3 text-center font-semibold text-white transition-colors"
>
Zines
</div>
</a>
</div>
+40
View File
@@ -0,0 +1,40 @@
import { useEffect, useState } from "react";
import { Check, Share2 } from "lucide-react";
import { Button } from "../ui/button";
interface ShareZineButtonProps {
zineId: string;
}
export function ShareZineButton({ zineId }: ShareZineButtonProps) {
const [copied, setCopied] = useState(false);
useEffect(() => {
if (!copied) return;
const timer = setTimeout(() => setCopied(false), 1500);
return () => clearTimeout(timer);
}, [copied]);
const handleClick = async (e: React.MouseEvent) => {
e.stopPropagation();
const url = `${window.location.origin}/projects/zines/${zineId}`;
await navigator.clipboard.writeText(url);
setCopied(true);
};
return (
<Button variant="outline" size="sm" onClick={handleClick}>
{copied ? (
<>
<Check />
Copied!
</>
) : (
<>
<Share2 />
Share
</>
)}
</Button>
);
}
+11 -17
View File
@@ -1,21 +1,19 @@
import { Download } from "lucide-react";
import type { Zine } from "../../data/zines";
import { Button } from "../ui/button";
import { ShareZineButton } from "./ShareZineButton";
interface ZineCardProps {
zine: Zine;
onClick: () => void;
}
export function ZineCard({ zine, onClick }: ZineCardProps) {
const handleDownload = (e: React.MouseEvent) => {
e.stopPropagation();
};
export function ZineCard({ zine }: ZineCardProps) {
const href = `/projects/zines/${zine.id}`;
return (
<div className="flex flex-col items-center gap-2 rounded-lg border p-4">
<button
onClick={onClick}
<a
href={href}
className="group flex flex-col items-center gap-2 transition-all focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
>
<div className="aspect-[3/4] w-48 overflow-hidden rounded bg-muted">
@@ -29,23 +27,19 @@ export function ZineCard({ zine, onClick }: ZineCardProps) {
{zine.description && (
<p className="text-sm text-muted-foreground">{zine.description}</p>
)}
</button>
</a>
<div className="flex gap-2 mt-2">
<Button variant="outline" size="sm" onClick={onClick}>
Read Online
<Button variant="outline" size="sm" asChild>
<a href={href}>Read Online</a>
</Button>
<Button
variant="outline"
size="sm"
asChild
onClick={handleDownload}
>
<Button variant="outline" size="sm" asChild>
<a href={zine.printablePdf} download>
<Download className="h-4 w-4 mr-1" />
<Download />
Printable
</a>
</Button>
<ShareZineButton zineId={zine.id} />
</div>
</div>
);
+6 -48
View File
@@ -1,54 +1,12 @@
import { useState } from "react";
import { zines, type Zine } from "../../data/zines";
import { zines } from "../../data/zines";
import { ZineCard } from "./ZineCard";
import { ZineViewer } from "./ZineViewer";
import {
Dialog,
DialogContent,
DialogTitle,
DialogDescription,
} from "../ui/dialog";
import * as VisuallyHidden from "@radix-ui/react-visually-hidden";
export function ZineGrid() {
const [selectedZine, setSelectedZine] = useState<Zine | null>(null);
const handleOpenZine = (zine: Zine) => {
setSelectedZine(zine);
};
const handleCloseZine = () => {
setSelectedZine(null);
};
return (
<>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{zines.map((zine) => (
<ZineCard
key={zine.id}
zine={zine}
onClick={() => handleOpenZine(zine)}
/>
))}
</div>
<Dialog open={selectedZine !== null} onOpenChange={handleCloseZine}>
<DialogContent className="max-w-4xl">
<VisuallyHidden.Root>
<DialogTitle>{selectedZine?.title}</DialogTitle>
<DialogDescription>
Interactive flipbook viewer for {selectedZine?.title}
</DialogDescription>
</VisuallyHidden.Root>
{selectedZine && (
<ZineViewer
pages={selectedZine.pages}
title={selectedZine.title}
/>
)}
</DialogContent>
</Dialog>
</>
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{zines.map((zine) => (
<ZineCard key={zine.id} zine={zine} />
))}
</div>
);
}
-245
View File
@@ -1,245 +0,0 @@
import React, { useRef, useState, useCallback, forwardRef, useEffect } from "react";
import HTMLFlipBook from "react-pageflip";
import { ChevronLeft, ChevronRight, ZoomIn, ZoomOut, Minimize2 } from "lucide-react";
import { Button } from "../ui/button";
interface PageProps {
src: string;
pageNumber: number;
}
const Page = forwardRef<HTMLDivElement, PageProps>(({ src, pageNumber }, ref) => {
return (
<div ref={ref} className="page bg-white">
<img
src={src}
alt={`Page ${pageNumber}`}
className="h-full w-full object-contain"
/>
</div>
);
});
Page.displayName = "Page";
interface ZineViewerProps {
pages: string[];
title: string;
}
const MIN_ZOOM = 1;
const MAX_ZOOM = 2.5;
const ZOOM_STEP = 0.5;
export function ZineViewer({ pages, title }: ZineViewerProps) {
const flipBookRef = useRef<any>(null);
const [currentPage, setCurrentPage] = useState(0);
const totalPages = pages.length;
const [zoom, setZoom] = useState(1);
const [pan, setPan] = useState({ x: 0, y: 0 });
const [isPanning, setIsPanning] = useState(false);
const panStart = useRef({ x: 0, y: 0 });
const panOffset = useRef({ x: 0, y: 0 });
const isZoomed = zoom > MIN_ZOOM;
const onFlip = useCallback((e: any) => {
setCurrentPage(e.data);
}, []);
const goToPrevPage = () => {
flipBookRef.current?.pageFlip()?.flipPrev();
};
const goToNextPage = () => {
flipBookRef.current?.pageFlip()?.flipNext();
};
const handleZoomIn = () => {
setZoom((z) => Math.min(z + ZOOM_STEP, MAX_ZOOM));
};
const handleZoomOut = () => {
setZoom((z) => {
const next = Math.max(z - ZOOM_STEP, MIN_ZOOM);
if (next <= MIN_ZOOM) setPan({ x: 0, y: 0 });
return next;
});
};
const handleZoomReset = () => {
setZoom(MIN_ZOOM);
setPan({ x: 0, y: 0 });
};
const handlePointerDown = (e: React.PointerEvent) => {
if (!isZoomed) return;
setIsPanning(true);
panStart.current = { x: e.clientX, y: e.clientY };
panOffset.current = { x: pan.x, y: pan.y };
(e.target as HTMLElement).setPointerCapture?.(e.pointerId);
e.preventDefault();
};
const handlePointerMove = (e: React.PointerEvent) => {
if (!isPanning) return;
const dx = (e.clientX - panStart.current.x) / zoom;
const dy = (e.clientY - panStart.current.y) / zoom;
setPan({
x: panOffset.current.x + dx,
y: panOffset.current.y + dy,
});
};
const handlePointerUp = () => {
setIsPanning(false);
};
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "ArrowLeft") {
goToPrevPage();
} else if (e.key === "ArrowRight") {
goToNextPage();
}
};
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, []);
if (pages.length === 0) {
return (
<div className="flex h-64 items-center justify-center text-muted-foreground">
No pages available
</div>
);
}
return (
<div className="flex flex-col items-center gap-4">
<h2 className="text-xl font-semibold">{title}</h2>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="icon"
onClick={handleZoomOut}
disabled={zoom <= MIN_ZOOM}
aria-label="Zoom out"
>
<ZoomOut className="h-4 w-4" />
</Button>
<span className="text-sm text-muted-foreground w-12 text-center">
{Math.round(zoom * 100)}%
</span>
<Button
variant="outline"
size="icon"
onClick={handleZoomIn}
disabled={zoom >= MAX_ZOOM}
aria-label="Zoom in"
>
<ZoomIn className="h-4 w-4" />
</Button>
{isZoomed && (
<Button
variant="outline"
size="icon"
onClick={handleZoomReset}
aria-label="Reset zoom"
>
<Minimize2 className="h-4 w-4" />
</Button>
)}
</div>
<div
className="flipbook-container"
style={{
overflow: "hidden",
cursor: isZoomed ? (isPanning ? "grabbing" : "grab") : "default",
}}
onPointerDown={handlePointerDown}
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
onPointerCancel={handlePointerUp}
>
<div
style={{
transform: `scale(${zoom}) translate(${pan.x}px, ${pan.y}px)`,
transformOrigin: "center center",
transition: isPanning ? "none" : "transform 0.2s ease",
pointerEvents: isZoomed ? "none" : "auto",
}}
>
{/* @ts-ignore - react-pageflip types are incomplete */}
<HTMLFlipBook
ref={flipBookRef}
width={350}
height={500}
size="stretch"
minWidth={280}
maxWidth={500}
minHeight={400}
maxHeight={700}
maxShadowOpacity={0.5}
showCover={true}
mobileScrollSupport={!isZoomed}
onFlip={onFlip}
className="flipbook"
style={{}}
startPage={0}
drawShadow={true}
flippingTime={600}
usePortrait={true}
startZIndex={0}
autoSize={true}
clickEventForward={true}
useMouseEvents={!isZoomed}
swipeDistance={30}
showPageCorners={!isZoomed}
disableFlipByClick={false}
>
{pages.map((page, index) => (
<Page key={index} src={page} pageNumber={index + 1} />
))}
</HTMLFlipBook>
</div>
</div>
<div className="flex items-center gap-4">
<Button
variant="outline"
size="icon"
onClick={goToPrevPage}
disabled={currentPage === 0}
aria-label="Previous page"
>
<ChevronLeft className="h-4 w-4" />
</Button>
<span className="text-sm text-muted-foreground">
Page {currentPage + 1} of {totalPages}
</span>
<Button
variant="outline"
size="icon"
onClick={goToNextPage}
disabled={currentPage >= totalPages - 1}
aria-label="Next page"
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
<p className="text-xs text-muted-foreground">
{isZoomed
? "Drag to pan. Use arrow buttons to turn pages"
: "Click the page corners or use arrow buttons to turn pages"}
</p>
</div>
);
}
+17
View File
@@ -93,4 +93,21 @@ export const zines: Zine[] = [
],
printablePdf: "/assets/zines/signal-101/printable.pdf",
},
{
id: "signal-201",
title: "Signal 201",
description: "Advanced usage of signal",
coverImage: "/assets/zines/signal-201/cover.jpg",
pages: [
"/assets/zines/signal-201/cover.jpg",
"/assets/zines/signal-201/page-1.jpg",
"/assets/zines/signal-201/page-2.jpg",
"/assets/zines/signal-201/page-3.jpg",
"/assets/zines/signal-201/page-4.jpg",
"/assets/zines/signal-201/page-5.jpg",
"/assets/zines/signal-201/page-6.jpg",
"/assets/zines/signal-201/page-7.jpg",
],
printablePdf: "/assets/zines/signal-201/printable.pdf",
},
];
+2 -2
View File
@@ -4,7 +4,7 @@ import { cn } from "@/lib/utils";
const title =
"Resist Tech Monopolies";
const description =
"Copyleft 2025";
`Copyleft ${new Date().getFullYear()}`;
const url = "https://placeholder.com";
const image = "/public/preview.webp";
---
@@ -40,7 +40,7 @@ const image = "/public/preview.webp";
"@type": "WebSite",
"url": "https://placeholder.com",
"name": "Resist Tech Monopolies",
"description": "Copyleft 2025"
"description": `Copyleft ${new Date().getFullYear()}`
}
</script>
-81
View File
@@ -1,81 +0,0 @@
---
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import Layout from "../layouts/Layout.astro";
import "../styles/globals.css";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<div>
<Navbar client:load activePage="PointsOfUnity" />
<div class="pl-4 pr-4">
<h1 class="text-3xl font-semibold">Points of Unity</h1>
<p class="mb-4">
Our community agreements and decision-making process describe <em>how</em> we work together. The points of unity are the broader philosophies that we're working towards as a group. Perhaps people aren't 100% aligned individually, but this is what the group as a whole believes in when showing up to the work.
</p>
<h2 class="text-2xl font-semibold mt-10 mb-2">Grounding</h2>
<p class="mb-4">
We situate ourselves in our context - as Seattle residents and as users and creators of the technologies we aim to resist. We are seeing a long-spanning rise of fascism within national governments here and abroad. In parallel is the increasing power of tech companies over our lives through surveillance and the provisioning of everyday needs, from employment to how we get our internet utilities in the first place. There are rising income disparities across the nation, but also more specifically across everyone who can be considered a "tech worker": from a CEO, to a software engineer to a tech campus cafeteria worker to a child enslaved to mine cobalt in the Congo.
</p>
<p class="mb-4">
We know that technology intersects with and amplifies systems of oppression, from anti-Black racism to classism, ableism, cisheteropatriarchy, etc. Big tech permeates all aspects of life in Seattle, but to name a few ways:
</p>
<ul class="list-disc pl-8 mb-4 space-y-2">
<li>The rise of a <a href="https://stopsurveillancecity.wordpress.com/" class="font-bold underline hover:text-gray-600">Surveillance City</a> in Seattle facilitates the tracking of out-of-state people seeking abortions, the kidnapping of migrants, and the increased surveillance and criminalization of sex workers, the visibly unhoused, and poor and racialized communities.</li>
<li>The <strong>environmental racism</strong> and community impacts of <strong>data centers</strong>, both from the AI bubble and the demand for more data processing that comes with surveillance capitalism.</li>
<li><strong>The AI genocide</strong> in Palestine abetted by Google, Amazon, Boeing and Microsoft, all local Seattle employers.</li>
<li>Rising <strong>gentrification</strong> and <strong>income inequality</strong> that comes from financialization of real estate and the gentrifying waves of tech workers moving into the city.</li>
</ul>
<p class="mb-4">
Some of us are employed by Big Tech companies, and we are all living on stolen Coast Salish land covered by the broken Treaty of Point Elliot of 1855; we understand that we are contributing - directly or indirectly - to these oppressions, and seek to use our skills towards more liberatory ends.
</p>
<h2 class="text-2xl font-semibold mt-10 mb-2">Theory of Change</h2>
<p class="mb-4">
We are committed to fostering our collective understanding of resisting tech monopolies and building a constellation of alternative technologies that exist outside of exploitative, oppressive systems. We seek out radical possibilities by building connections across our communities with others struggling to resist tech monopolies and reject work that expands the reach of these monopolies.
</p>
<p class="mb-4">
We don't need to passively wait around for somebody to come implement these solutions. Making improvements to our lives and our communities paves the way for collective liberation. Dismantling smaller elements of a system is practice for uprooting the whole and replacing it with a constellation of liberatory alternatives.
</p>
<h2 class="text-2xl font-semibold mt-10 mb-2">Role of Technology</h2>
<p class="mb-4">
"We believe our communities and organizing efforts can and should harness the possibilities of new technology." (<a href="https://mijente.net/our-dna/" class="underline hover:text-gray-600">Mijente</a>) However, we don't innovate for the sake of innovation, but aim to create with intention, listen to the perspectives of those we hope to serve (including ourselves), and hold each other accountable in this work.
</p>
<h2 class="text-2xl font-semibold mt-10 mb-2">Our Position in the Movement</h2>
<p class="mb-4">
We don't see ourselves as a vanguard organization - that is, we make no claims to being the sole authority on resisting tech monopolies and we will never tell a front-line community or group of tech users what they actually need. Everyone benefits from the work of resisting tech monopolies, and for that reason we do the work for ourselves as much as we do it for anybody we are in solidarity with.
</p>
<p class="mb-4">
However we also recognize our own privilege in being highly-skilled tech workers: because of this we center the voices of the people most vulnerable to various tech monopolies, and aim to share our expertise so that others can also become agents in co-creating technologies of liberation and resistance.
</p>
<h2 class="text-2xl font-semibold mt-10 mb-2">Friendly Vibes</h2>
<p class="mb-4">
Holding all of the above, we're also a group of friends here for the scrappy, hacky vibes. We don't take our work too seriously and we're here to work joyfully. This is first and foremost a community.
</p>
<h2 class="text-2xl font-semibold mt-10 mb-2">Acknowledgments</h2>
<p class="mb-4">
These points of unity were inspired by the Prison Library Solidarity Network, the Coalition of Anti-Racist Whites, Ruha Benjamin's work on Abolitionist Tech, and Mijente.
</p>
</div>
</div>
<Footer />
</main>
</Layout>
@@ -1,18 +1,19 @@
---
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import PointsOfUnity from "../components/PointsOfUnity.astro";
import Layout from "../layouts/Layout.astro";
import "../styles/globals.css";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<main class="px-4 flex min-h-screen flex-col justify-between">
<div>
<Navbar client:load activePage="GetInvolved" />
<section class="sticky top-0">
<Navbar client:load activePage="AboutUs" />
</section>
<div class="pl-4 pr-4">
<h1 class="text-3xl font-semibold">Get Involved</h1>
<PointsOfUnity />
</div>
</div>
<Footer />
+53 -13
View File
@@ -1,36 +1,76 @@
---
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
import BigTechAlternatives from "@/components/BigTechAlternatives.astro";
import WhatWereUpTo from "@/components/WhatWereUpTo.astro";
import Layout from "@/layouts/Layout.astro";
import "@/styles/globals.css";
import Services from "@/components/Services.astro";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<div>
<main>
<section class="sticky top-0">
<Navbar client:load activePage="Home" />
</section>
<section id="aboutus" class="scroll-m-20 px-4">
<section>
<h1 class="pb-4 text-3xl font-semibold">About Us</h1>
<div class="pl-4 pr-4 text-xl leading-[2]">
<p>
Do you believe technology can and should be used for good? Do you
think a democratic internet could liberate us? Join us!
We situate ourselves in our context - as Seattle residents and as
users and creators of the technologies we aim to resist.
</p>
<p>We share this vision, and we want to work together to achieve it.</p>
<div class="my-8">
<a
href="/aboutus/"
class="rounded-sm bg-[#fe4f60FF] px-6 py-3 font-semibold text-white transition-colors"
>
Read More
</a>
</div>
</section>
<section>
<h2 class="pb-4 text-3xl font-semibold">Get Involved</h2>
<p>
Do you believe technology can and should be used for good? Do you
think a democratic internet could liberate us? Join us! We share this
vision, and we want to work together to achieve it.
</p>
<div class="my-8">
<a
href="https://shlink.resisttechmonopolies.online/HNrZG"
class="px-6 py-3 bg-[#80aaff] text-white font-semibold rounded-md hover:bg-[#6090e0] transition-colors"
<a
href="https://shlink.resisttechmonopolies.online/HNrZG"
class="rounded-sm bg-[#80aaff] px-6 py-3 font-semibold text-white transition-colors"
target="_blank"
rel="noopener noreferrer"
>
Fill Out Our Interest Form!
</a>
</div>
</div>
</div>
</section>
</section>
<section id="whatwereupto" class="scroll-m-20 px-4">
<WhatWereUpTo />
</section>
<section id="services" class="my-8 scroll-m-20 px-4">
<Services />
</section>
<!-- <section id="calendar" class="scroll-m-20 my-8">
TODO calendar
</section> -->
<section id="bigtechalternatives" class="my-8 scroll-m-20">
<BigTechAlternatives />
</section>
<Footer />
</main></Layout
>
</main>
</Layout>
-95
View File
@@ -1,95 +0,0 @@
---
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import Layout from "../layouts/Layout.astro";
import "../styles/globals.css";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<div>
<Navbar client:load activePage="OurProjects" />
<div class="pl-4 pr-4">
<h1 class="pb-4 pt-4 text-3xl font-semibold">Our Projects</h1>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
<!-- <div class="justify-items-center">
<img
src="/assets/calendar.png"
alt="stack of books"
width="250"
class="border pb-4"
/>
<a
href="/projects/calendar/"
class="underline decoration-solid">Calendar</a
>
</div> -->
<div class="justify-items-center">
<img
src="/assets/book-club.jpg"
alt="stack of books"
width="280"
class="border"
/>
<a
href="/projects/bookclub/"
class="underline decoration-solid">Book Club</a
>
</div>
<div class="justify-items-center">
<img
src="/assets/computer.png"
alt="computer"
width="250"
class="border"
/>
<a href="/projects/alternatives/" class="underline decoration-solid">Big Tech Alternatives</a>
</div>
<div class="justify-items-center">
<img
src="/assets/zines-thumbnail.png"
alt="zines"
width="250"
class="border object-cover w-[250px] h-[250px]"
/>
<a href="/projects/zines/" class="underline decoration-solid">Zines</a>
</div>
<div class="justify-items-center">
<img
src="/assets/pet-calendar-2026-cover.jpg"
alt="photo collage of cats"
width="500"
class="border"
/>
<a
href="/projects/PetCalendar/"
class="underline decoration-solid">Pet Calendar</a
>
</div>
<div class="justify-items-center">
<img
src="/assets/pihole.jpeg"
alt="pihole parts"
width="250"
class="border"
/>
<a
href="/projects/pihole/"
class="underline decoration-solid">Pihole Workshop</a
>
</div>
</div>
</div>
</div>
<Footer />
</main>
</Layout>
+232 -244
View File
@@ -6,16 +6,25 @@ import "../../styles/globals.css";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<main class="flex min-h-screen flex-col justify-between px-4">
<div>
<Navbar client:load activePage="WhatWereWorkingOn" />
<section class="sticky top-0">
<Navbar client:load activePage="WhatWereUpTo" />
</section>
<div class="pl-4 pr-4">
<h1 class="pb-4 pt-4 text-3xl font-semibold">Pet Calendar</h1>
<div>We created a pet calendar with 12 beautiful pictures of RTM's
animal friends.
<p class="pb-2">Each month, our pets will share with you with a different way you can
Resist Big Tech Monopolies!</p>
<p class="pb-2"> If you'd like to request a calendar, email us at besties@resisttechmonopolies.online</p>
<h1 class="pb-4 pt-4 text-3xl font-semibold">Pet Calendar</h1>
<div>
We created a pet calendar with 12 beautiful pictures of RTM's animal
friends.
<p class="pb-2">
Each month, our pets will share with you with a different way you
can Resist Big Tech Monopolies!
</p>
<p class="pb-2">
If you'd like to request a calendar, email us at
besties@resisttechmonopolies.online
</p>
<div class="justify-items-center">
<img
src="/assets/pet-calendar-2026-cover.jpg"
@@ -23,321 +32,300 @@ import "../../styles/globals.css";
width="500"
class="border"
/>
</div>
<div>
<div></div>
</div>
</div>
<h1 class="pb-4 pt-4 text-2xl font-semibold">2026 Monthly RTM Tutorials</h1>
<h1 class="pb-4 pt-4 text-2xl font-semibold">
2026 Monthly RTM Tutorials
</h1>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="January">January</h2>
<div>
<p class="mb-2"> Try out Mastadon for algorithmless social media that won't sell your data!</p>
<a
class="underline mb-2"
<div>
<p class="mb-2">
Try out Mastadon for algorithmless social media that won't sell
your data!
</p>
<a
class="mb-2 underline"
href="https://joinmastodon.org/"
rel="noopener noreferrer"
target="_blank"
>Join Mastadon!
</a >
<p class="mb-2"> Difficulty rating: 🌶️/5
</p>
</div>
</div>
</a>
<p class="mb-2">Difficulty rating: 🌶️/5</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="February">February</h2>
<div>
<p class="mb-2"> Learn about hosting your own instances of tech services to protect your data!</p>
<a
class="underline mb-2"
<p class="mb-2">
Learn about hosting your own instances of tech services to
protect your data!
</p>
<a
class="mb-2 underline"
href="https://www.xda-developers.com/tips-for-self-hosting-beginners/"
rel="noopener noreferrer"
target="_blank"
>Beginner's Guide to Self-Hosting!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️🌶️🌶️/5
</p>
</div>
</div>
</a>
<p class="mb-2">Difficulty rating: 🌶️🌶️🌶️🌶️/5</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="March">March</h2>
<div>
<p class="mb-2"> Try out Calibre to read Amazon-free e-books!</p>
<a
class="underline mb-2"
<p class="mb-2">Try out Calibre to read Amazon-free e-books!</p>
<a
class="mb-2 underline"
href="https://tutorials.mediaket.net/software-tutorials/calibre-how-to-use-it.html"
rel="noopener noreferrer"
target="_blank"
>Get Started with Calibre!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️/5
</p>
</div>
</div>
</a>
<p class="mb-2">Difficulty rating: 🌶️🌶️/5</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="April">April</h2>
<div>
<p class="mb-2"> Thinking of switching off Windows or MacOS? Try Linux
for a super customizable, privacy-first operating system!
</p>
<a
class="underline mb-2"
<p class="mb-2">
Thinking of switching off Windows or MacOS? Try Linux for a
super customizable, privacy-first operating system!
</p>
<a
class="mb-2 underline"
href="https://www.geeksforgeeks.org/linux-unix/30-days-of-linux/"
rel="noopener noreferrer"
target="_blank"
>30 Days of Linux for Beginners!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️🌶️🌶️/5
</p>
</a>
<p class="mb-2">Difficulty rating: 🌶️🌶️🌶️🌶️/5</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="May">May</h2>
<div>
<p class="mb-2"> Try going outside and repping some protest fashion this Spring!</p>
<a
class="underline mb-2"
<p class="mb-2">
Try going outside and repping some protest fashion this Spring!
</p>
<a
class="mb-2 underline"
href="https://graziamagazine.com/me/articles/history-keffiyeh-palestine/"
rel="noopener noreferrer"
target="_blank"
>Learn the history and meaning of the keffiyeh!
</a >
<p class="mb-2"> Difficulty rating: 🌶️/5
</p>
</div>
</div>
</a>
<p class="mb-2">Difficulty rating: 🌶️/5</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="June">June</h2>
<div>
<p class="mb-2"> Try out Ente for a privacy-friendly, open-source photos hosting platform!</p>
<a
class="underline mb-2"
<p class="mb-2">
Try out Ente for a privacy-friendly, open-source photos hosting
platform!
</p>
<a
class="mb-2 underline"
href="https://web.archive.org/web/20260108185516/https://www.androidauthority.com/ente-photos-hands-on-3542198/"
rel="noopener noreferrer"
target="_blank"
>In-depth overeview of Ente Photos migration!
</a >
<p class="mb-2"> Difficulty rating: 🌶️/5
</p>
</a>
<p class="mb-2">Difficulty rating: 🌶️/5</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="July">July</h2>
<div>
<p class="mb-2"> Try installing your own Pi Hole on your home wifi network to block ad and malware servers!</p>
<a
class="underline mb-2"
<p class="mb-2">
Try installing your own Pi Hole on your home wifi network to
block ad and malware servers!
</p>
<a
class="mb-2 underline"
href="https://protasm.com/blogs/news/an-extensive-tutorial-on-how-to-setup-a-pi-hole"
rel="noopener noreferrer"
target="_blank"
>Extensive PiHole Tutorial!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️🌶️🌶️🌶️/5
</p>
</div>
<hr class="mb-4">
<p class="mb-2"> Alternative: Install an adblocker!</p>
<a
class="underline mb-2"
href="https://adblockplus.org/"
>Install Adblock Plus!
</a >
<p class="mb-2"> Difficulty rating: 🌶️/5
</p>
</div>
</a>
<p class="mb-2">Difficulty rating: 🌶️🌶️🌶️🌶️🌶️/5</p>
</div>
<hr class="mb-4" />
<p class="mb-2">Alternative: Install an adblocker!</p>
<a class="mb-2 underline" href="https://adblockplus.org/"
>Install Adblock Plus!
</a>
<p class="mb-2">Difficulty rating: 🌶️/5</p>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="August">August</h2>
<div>
<p class="mb-2"> Try out DuckDuckGo as a privacy-friendly alternative to Google search and Chrome!</p>
<a
class="underline mb-2"
<p class="mb-2">
Try out DuckDuckGo as a privacy-friendly alternative to Google
search and Chrome!
</p>
<a
class="mb-2 underline"
href="https://duckduckgo.com/"
rel="noopener noreferrer"
target="_blank"
>Download Duck Duck Go!
</a >
<p class="mb-2"> Difficulty rating: 🌶️/5
</p>
</div>
</div>
</a>
<p class="mb-2">Difficulty rating: 🌶️/5</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="September">September</h2>
<h2 class="mb-4 text-2xl font-semibold" id="September">
September
</h2>
<div>
<p class="mb-2"> Instead of relying on surveillance for safety, try making friends with your neighbors!</p>
<a
class="underline mb-2"
<p class="mb-2">
Instead of relying on surveillance for safety, try making
friends with your neighbors!
</p>
<a
class="mb-2 underline"
href="https://www.youtube.com/watch?v=1-Xl6dhDV3Q"
> How to Build Radical Community!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️🌶️/5
rel="noopener noreferrer"
target="_blank"
>
How to Build Radical Community!
</a>
<p class="mb-2">Difficulty rating: 🌶️🌶️🌶️/5</p>
(hey making friends in Seattle is hard, okay?)
</div>
<hr class="mb-4" />
<p class="mb-2">
Alternative: Neighbor Unions (buliding community power!)
</p>
(hey making friends in Seattle is hard, okay?)
<a
class="mb-2 underline"
href="https://social-ecology.org/wp/neighbor-union-cohort/"
rel="noopener noreferrer"
target="_blank"
>Learn about Neighbor Unions!
</a>
</div>
<hr class="mb-4">
<p class="mb-2"> Alternative: Neighbor Unions (buliding community power!)</p>
<a
class="underline mb-2"
href="https://social-ecology.org/wp/neighbor-union-cohort/"
>Learn about Neighbor Unions!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️🌶️/5
</p>
<p class="mb-2">Difficulty rating: 🌶️🌶️🌶️/5</p>
</div>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="October">October</h2>
<div>
<p class="mb-2"> Try out JellyFin to host your own media server
and share with friends. You can break up with Netflix, Hulu, Disney+, and more!
</p>
<a
class="underline mb-2"
href="https://jellyfin.org/docs/general/quick-start/"
>Quickstart for JellyFin!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️🌶️🌶️/5
</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="November">November</h2>
<div>
<p class="mb-2"> Try out Meet.coop as an open-source, privacy-friendly, and renewable energy-powered alternative to Zoom and Hangouts! </p>
<a
class="underline mb-2"
href="https://www.meet.coop/"
>Make a Meet.coop account!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️/5
</p>
</div>
<hr class="mb-4">
<p class="mb-2"> Free alternative: Jitsi</p>
<a
class="underline mb-2"
href="https://meet.jit.si/"
>Start a Meeting with Jitsi!
</a >
<p class="mb-2"> Difficulty rating: 🌶️/5
</p>
</div>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="December">December</h2>
<div>
<p class="mb-2"> Try out Radicale as a privacy-friendly, open-source alternative to Google Calendar and Outlook!</p>
<a
class="underline mb-2"
href="https://radicale.org/master.html#simple-5-minute-setup"
>Setting up Radicale Tutorial!
</a >
<p class="mb-2"> Difficulty rating: 🌶️🌶️🌶️🌶️🌶️/5
</p>
</div>
<hr class="mb-4">
<p class="mb-2"> Alternative: Proton Calendar</p>
<a
class="underline mb-2"
href="https://calendar.proton.me/"
>Switch to Proton Calendar!
</a >
<p class="mb-2"> Difficulty rating: 🌶️/5
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<Footer />
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="October">October</h2>
<div>
<p class="mb-2">
Try out JellyFin to host your own media server and share with
friends. You can break up with Netflix, Hulu, Disney+, and more!
</p>
<a
class="mb-2 underline"
href="https://jellyfin.org/docs/general/quick-start/"
rel="noopener noreferrer"
target="_blank"
>Quickstart for JellyFin!
</a>
<p class="mb-2">Difficulty rating: 🌶️🌶️🌶️🌶️/5</p>
</div>
</div>
</div>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="November">November</h2>
<div>
<p class="mb-2">
Try out Meet.coop as an open-source, privacy-friendly, and renewable
energy-powered alternative to Zoom and Hangouts!
</p>
<a
class="mb-2 underline"
href="https://www.meet.coop/"
rel="noopener noreferrer"
target="_blank"
>Make a Meet.coop account!
</a>
<p class="mb-2">Difficulty rating: 🌶️🌶️/5</p>
</div>
<hr class="mb-4" />
<p class="mb-2">Free alternative: Jitsi</p>
<a
class="mb-2 underline"
href="https://meet.jit.si/"
rel="noopener noreferrer"
target="_blank"
>Start a Meeting with Jitsi!
</a>
<p class="mb-2">Difficulty rating: 🌶️/5</p>
</div>
</div>
</main>
</Layout>
<div class="flex flex-col gap-8">
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold" id="December">December</h2>
<div>
<p class="mb-2">
Try out Radicale as a privacy-friendly, open-source alternative to
Google Calendar and Outlook!
</p>
<a
class="mb-2 underline"
href="https://radicale.org/master.html#simple-5-minute-setup"
rel="noopener noreferrer"
target="_blank"
>Setting up Radicale Tutorial!
</a>
<p class="mb-2">Difficulty rating: 🌶️🌶️🌶️🌶️🌶️/5</p>
</div>
<hr class="mb-4" />
<p class="mb-2">Alternative: Proton Calendar</p>
<a
class="mb-2 underline"
href="https://calendar.proton.me/"
rel="noopener noreferrer"
target="_blank"
>Switch to Proton Calendar!
</a>
<p class="mb-2">Difficulty rating: 🌶️/5</p>
</div>
</div>
<Footer />
-99
View File
@@ -1,99 +0,0 @@
---
import Navbar from "../../components/Navbar";
import Footer from "../../components/Footer";
import Layout from "../../layouts/Layout.astro";
import "../../styles/globals.css";
import Card from "../../components/Card.astro";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<div>
<Navbar client:load activePage="OurProjects" />
<div class="pl-4 pr-4">
<h1 class="pb-4 pt-4 text-3xl font-semibold">Big Tech Alternatives</h1>
<p class="mb-4">Explore open-source and privacy-focused alternatives to popular big tech software.</p>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
<Card
title="Browsers"
bigTech="Chrome (Google)"
alternatives={[
{ name: 'Firefox', description: 'Privacy-focused browser.', url: 'https://www.mozilla.org/firefox' },
]}
bgClass="bg-blue-50"
/>
<Card
title="Cloud Storage"
bigTech="Google Drive"
alternatives={[
{ name: 'Nextcloud', description: 'Self-hosted file sync and share.', url: 'https://nextcloud.com' }
]}
bgClass="bg-indigo-50"
/>
<Card
title="Email"
bigTech="Gmail (Google)"
alternatives={[
{ name: 'ProtonMail', description: 'Encrypted email.', url: 'https://protonmail.com' },
]}
bgClass="bg-yellow-50"
/>
<Card
title="Maps"
bigTech="Google Maps"
alternatives={[
{ name: 'OpenStreetMap', description: 'Community-driven maps.', url: 'https://www.openstreetmap.org' }
]}
bgClass="bg-teal-50"
/>
<Card
title="Messaging"
bigTech="WhatsApp (Meta), Slack"
alternatives={[
{ name: 'Signal', description: 'Encrypted messaging (alternative to WhatsApp).', url: 'https://signal.org' },
{ name: 'Element', description: 'Matrix-based chat (alternative to Slack).', url: 'https://element.io' }
]}
bgClass="bg-red-50"
/>
<Card
title="Search Engines"
bigTech="Google Search"
alternatives={[
{ name: 'DuckDuckGo', description: 'Privacy-protecting search.', url: 'https://duckduckgo.com' },
]}
bgClass="bg-green-50"
/>
<Card
title="Social Media"
bigTech="Twitter (X), Facebook (Meta)"
alternatives={[
{ name: 'Mastodon', description: 'Decentralized social network (alternative to Twitter).', url: 'https://joinmastodon.org' },
]}
bgClass="bg-purple-50"
/>
<Card
title="Video Platforms"
bigTech="YouTube (Google)"
alternatives={[
{ name: 'PeerTube', description: 'Decentralized video hosting.', url: 'https://joinpeertube.org' },
]}
bgClass="bg-pink-50"
/>
</div>
</div>
</div>
<Footer />
</main>
</Layout>
+12 -6
View File
@@ -6,9 +6,12 @@ import "../../styles/globals.css";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<main class="flex min-h-screen flex-col justify-between px-4">
<div>
<Navbar client:load activePage="OurProjects" />
<section class="sticky top-0">
<Navbar client:load activePage="WhatWereUpTo" />
</section>
<div class="px-4">
<h1 class="pb-4 pt-4 text-3xl font-semibold">Book Club</h1>
<p class="mb-4">
@@ -23,17 +26,20 @@ import "../../styles/globals.css";
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold">Current Book</h2>
<div>
Race After Technology - Ruha Benjamin (see <a
Teaching Community Technology Handbook - Detroit Community
Technology Project (read for free <a
class="underline"
href="https://www.dropbox.com/scl/fi/jnzvtiry7jn3xrts2n703/RAT-Discussion-Guide.pdf?rlkey=pq6ovaeydhcm8yi2u7lzzmiuj&e=2&dl=0"
>discussion guide
</a>)
href="https://detroitcommunitytech.org/?q=teachcommtech"
rel="noopener noreferrer"
target="_blank">here</a
>)
</div>
</div>
<div class="rounded-lg border p-6">
<h2 class="mb-4 text-2xl font-semibold">Past Books</h2>
<ul class="list-disc pl-6">
<li>Holding Change - Adrienne Maree Brown</li>
<li>Race After Technology - Ruha Benjamin</li>
<li>Common Circuits - Luis Felipe R. Murillo</li>
<li>Internet for the People - Ben Tarnoff</li>
-21
View File
@@ -1,21 +0,0 @@
---
import Navbar from "../../components/Navbar";
import Footer from "../../components/Footer";
import Layout from "../../layouts/Layout.astro";
import "../../styles/globals.css";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<div>
<Navbar client:load activePage="Calendar" />
<div class="pl-4 pr-4">
<h1 class="pb-4 pt-4 text-3xl font-semibold">Calendar</h1>
<div class="flex flex-col gap-4">
<p class="mb-4">See what we're doing next!</p>
</div>
</div>
</div>
<Footer />
</main>
</Layout>
-19
View File
@@ -1,19 +0,0 @@
---
import Navbar from "../../components/Navbar";
import Footer from "../../components/Footer";
import Layout from "../../layouts/Layout.astro";
import "../../styles/globals.css";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<div>
<Navbar />
<div class="pl-4 pr-4">
<h1 class="text-3xl font-semibold">Pihole</h1>
</div>
</div>
<Footer />
</main>
</Layout>
+6 -23
View File
@@ -7,13 +7,16 @@ import "../../styles/globals.css";
---
<Layout>
<main class="flex min-h-screen flex-col justify-between">
<main class="px-4 flex min-h-screen flex-col justify-between">
<div>
<Navbar client:load activePage="OurProjects" />
<section class="sticky top-0">
<Navbar client:load activePage="WhatWereUpTo" />
</section>
<div class="px-4">
<h1 class="pb-4 pt-4 text-3xl font-semibold">Zines</h1>
<p class="mb-6">
We create and share zines about technology, power, and resistance. You are welcome to download the printable version to fold and distribute!
We create and share zines about technology, power, and resistance. You
are welcome to download the printable version to fold and distribute!
</p>
<ZineGrid client:load />
@@ -22,23 +25,3 @@ import "../../styles/globals.css";
<Footer />
</main>
</Layout>
<style>
:global(.flipbook-container) {
display: flex;
justify-content: center;
align-items: center;
min-height: 520px;
}
:global(.flipbook) {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
:global(.page) {
background-color: white;
display: flex;
align-items: center;
justify-content: center;
}
</style>
+79
View File
@@ -0,0 +1,79 @@
---
import { Download } from "lucide-react";
import Navbar from "../../../components/Navbar";
import Footer from "../../../components/Footer";
import Layout from "../../../layouts/Layout.astro";
import { ShareZineButton } from "../../../components/zines/ShareZineButton";
import { buttonVariants } from "../../../components/ui/button";
import { zines, type Zine } from "../../../data/zines";
import "../../../styles/globals.css";
export async function getStaticPaths() {
return zines.map((zine) => ({
params: { id: zine.id },
props: { zine },
}));
}
interface Props {
zine: Zine;
}
const { zine } = Astro.props;
---
<Layout>
<main class="px-4 flex min-h-screen flex-col justify-between">
<div>
<section class="sticky top-0">
<Navbar client:load activePage="OurProjects" />
</section>
<div class="px-4">
<a
href="/projects/zines"
class="inline-block pb-4 pt-4 text-sm underline"
>
← Back to all zines
</a>
<div class="mx-auto max-w-3xl">
<h1 class="text-2xl font-semibold">{zine.title}</h1>
{
zine.description && (
<p class="mt-1 text-sm text-muted-foreground">
{zine.description}
</p>
)
}
<div class="mt-4 flex gap-2">
<ShareZineButton client:load zineId={zine.id} />
<a
href={zine.printablePdf}
download
class={buttonVariants({ variant: "outline", size: "sm" })}
>
<Download />
Printable
</a>
</div>
<div class="flex flex-col gap-4 py-6">
{
zine.pages.map((page, i) => (
<img
src={page}
alt={`Page ${i + 1} of ${zine.title}`}
loading={i === 0 ? "eager" : "lazy"}
class="h-auto w-full rounded bg-white shadow-sm"
/>
))
}
</div>
</div>
</div>
</div>
<Footer />
</main>
</Layout>
+16
View File
@@ -1,6 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
@@ -29,6 +30,7 @@
--chart-5: 27 87% 67%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
@@ -56,10 +58,12 @@
--chart-5: 340 75% 55%;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
@@ -70,8 +74,20 @@
src: url("/fonts/Geist[wght].ttf") format("truetype");
font-display: swap;
}
@font-face {
font-family: "GeistMono";
src: url("/fonts/GeistMono[wght].ttf") format("truetype");
font-display: swap;
}
.hamburger {
cursor: pointer;
}
.hamburger .line {
display: block;
width: 36px;
height: 2px;
margin-bottom: 8px;
}