Compare commits

..
47 Commits
Author SHA1 Message Date
Tanner 8c6e807653 Mobile menu header nav spacing fixes 2026-09-29 23:01:58 +00:00
Tanner 0725d3eb1a Convert srcset images to 1600, 800, 400px 2026-09-29 22:16:36 +00:00
Tanner bb8fd62041 Tools Back to Top links go to Search 2026-09-27 22:16:56 +00:00
Tanner 1745ea9e93 Tools page sticky header margin fixes 2026-09-27 20:47:24 +00:00
Tanner 2f1bb6deb5 Make gear icon smaller 2026-09-27 20:29:24 +00:00
Tanner 40d5b54055 Fix footer source code external link icon 2026-09-27 20:26:12 +00:00
Tanner e2934f84b8 Always display header font 2026-09-27 20:00:17 +00:00
Tanner a4069d27e4 Stop cache busting styles.css with query param 2026-09-27 19:55:42 +00:00
Tanner b33a72ced0 Move static assets to public/ 2026-09-27 19:18:35 +00:00
Tanner dde33d0907 Update footer links, add source code 2026-09-27 19:09:50 +00:00
Tanner fbf3dbe33e Update Electonics, Sewing, Lasers, 3D printer photos 2026-09-27 18:04:33 +00:00
Tanner 21785ed493 Cache bust styles.css 2026-09-26 01:12:25 +00:00
Tanner 557fa92e1f Reorder header: Spaces Tools Membership Contact Portal Wiki 2026-09-26 01:10:39 +00:00
Tanner 9e730bbcfd Remove About page 2026-09-26 01:08:04 +00:00
Tanner 05593346fc Tool page fixes 2026-09-26 01:02:50 +00:00
Tanner 7bac8b4011 Add Tools page search 2026-09-26 00:48:55 +00:00
Tanner 03b124f295 Add Tools page 2026-09-26 00:33:25 +00:00
Tanner 8f1d545ab4 Add 400px versions of the home page images 2026-09-24 16:13:06 +00:00
Tanner f5cfa74dde Don't lazy load the four home page images 2026-09-24 01:09:32 +00:00
Tanner d3d2421ac3 Cache bust styles.css with query param 2026-09-24 00:54:54 +00:00
Tanner 428398820c Make links look better 2026-09-24 00:42:02 +00:00
Tanner 3ad2c47bb3 Put Spaces URL back to /the-space/ 2026-09-24 00:33:21 +00:00
Tanner b9c7791f95 Refine About, Membership, and Spaces pages 2026-09-24 00:30:56 +00:00
Tanner 35c513d08b Reorder header, make mobile 3x2 2026-09-24 00:24:30 +00:00
Tanner 1a264905ea Rename About to Spaces, add About page 2026-09-23 20:58:23 +00:00
Tanner 933720125a Add external link icons 2026-09-23 20:50:51 +00:00
Tanner 07056bd10b Update README 2026-09-19 15:35:41 +00:00
Tanner 34625ceead Switch to SVG logo 2026-09-17 21:02:46 +00:00
Tanner a4197cc050 Make fallback font smaller 2026-09-17 20:45:09 +00:00
Tanner abdb760aaf Add WebP instructions to AGENTS.md 2026-09-17 20:35:52 +00:00
Tanner 92c8906f24 Add WebP versions of photos 2026-09-17 20:34:40 +00:00
Tanner e1b3056b25 Downsize home gallery images 2026-09-17 20:28:50 +00:00
Tanner b52eecdd1c Convert fonts from TTF to WOFF2 2026-09-17 20:08:29 +00:00
Tanner dc18f437ae H1 font load optional 2026-09-17 20:06:57 +00:00
Tanner 07dcf2b49d Footer text color to white 2026-09-17 19:59:17 +00:00
Tanner 328e9ca8de Give home page images better names 2026-09-17 19:52:21 +00:00
Tanner 9ef29ca0ab Lazy load Google Maps iframe 2026-09-17 19:49:17 +00:00
Tanner 445549b9ec Add facility links back to home page 2026-09-17 19:21:47 +00:00
Tanner fd49cd15ab Fix mini tour video link text 2026-09-16 18:40:48 +00:00
Tanner fb5fcf2f2b Lazy load images, explicity dimensions 2026-09-16 18:37:26 +00:00
Tanner d4a810ca05 Return spaces images to original width 2026-09-16 18:28:59 +00:00
Tanner 5794154487 Preload styles.css 2026-09-16 18:28:26 +00:00
Tanner a0c0d8f4dc Fix spacing 2026-09-16 18:23:35 +00:00
Tanner ce14df4b53 Add README 2026-09-16 18:12:06 +00:00
Tanner 82cfb0440d Switch links to https 2026-09-16 18:02:08 +00:00
Tanner 264bf8c01f Add Open Graph data 2026-09-16 18:00:27 +00:00
Tanner 44082bdab6 Fix headers, make consistent 2026-09-16 17:49:09 +00:00
384 changed files with 3117 additions and 939 deletions
+25 -4
View File
@@ -6,7 +6,7 @@ This is the static website for Protospace, a non-profit makerspace in Calgary, A
## Source of truth
- Edit the HTML, CSS, and metadata files directly.
- Edit the HTML, CSS, and metadata files directly under `public/`.
- Do not create or reintroduce a static-site generator.
- Do not add a build script that overwrites manually maintained pages.
- There is intentionally no `build.js` or equivalent generator.
@@ -14,9 +14,10 @@ This is the static website for Protospace, a non-profit makerspace in Calgary, A
## Page structure
- Every page must be an `index.html` inside its own route directory:
- Every page must be an `index.html` inside its own route directory under `public/`:
- `/index.html`
- `/the-space/index.html`
- `/tools/index.html`
- `/join-us/index.html`
- `/contact/index.html`
- `/wood-shop/index.html`
@@ -66,7 +67,7 @@ Keep source code pleasant for humans to read and edit.
When adding or editing a page, match the existing structure:
- Header with Protospace logo and links to About, Membership, Wiki, Portal, and Contact
- Header with Protospace logo and links to Spaces, Tools, Membership, Contact, Portal, and Wiki
- Main page content with one meaningful `<h1>`
- Sidebar containing the current open-house information, address, parking details, map iframe, and search control where applicable
- Footer containing YouTube, FAQ, and Facebook links
@@ -82,13 +83,31 @@ If this information changes, update the sidebar, relevant page copy, JSON-LD, an
## Assets
- Store local images in `/assets/`.
- Store local images in `public/assets/`.
- Use descriptive filenames rather than WordPress upload names such as `photo_6_2023-06-06_16-39-08.jpg`.
- Remove unused downloaded image variants when it is safe to do so.
- Update every reference when renaming or removing an asset.
- Do not link page content to the old WordPress upload paths.
- Give every image a useful `alt` attribute. Use `alt=""` only for genuinely decorative images.
- Keep image dimensions or CSS aspect ratios where possible to reduce layout shift.
- For every JPEG photo used in page content, generate a matching WebP version and serve it with the original JPEG as the fallback in a `<picture>` element:
```html
<picture>
<source
type="image/webp"
srcset="/assets/example.webp 640w"
sizes="(max-width: 760px) 100vw, 750px"
/>
<img
width="640"
height="480"
src="/assets/example.jpg"
alt="Descriptive photo description"
/>
</picture>
```
- Keep the WebP and fallback `srcset` candidates equivalent, preserve the fallback image's `alt`, dimensions, loading behavior, and decoding attributes, and verify that every referenced variant exists.
## Accessibility
@@ -152,6 +171,7 @@ Before finishing an HTML change:
- Run structural validation when available, for example:
```bash
cd public/
npx html-validate \
--rule doctype-style:off \
--rule void-style:off \
@@ -165,6 +185,7 @@ Before finishing an HTML change:
- Start a local static server and smoke-test every route:
```bash
cd public/
python3 -m http.server 8765
```
+56
View File
@@ -0,0 +1,56 @@
# Protospace Website
The Protospace WordPress site has been replaced with static HTML and CSS pages. Static files improve security, reduce maintenance, and provide faster page loads without a database or server-side CMS.
## Editing the site
1. Open the [Protospace repository](https://github.com/Protospace/main-website) on GitHub and click **Fork**. Create the fork under your own GitHub account.
2. Clone your fork and configure the Protospace repository as `upstream`:
```bash
git clone git@github.com:YOUR-USERNAME/main-website.git
cd main-website/
git remote add upstream git@github.com:Protospace/main-website.git
```
3. Create a branch for your change:
```bash
git checkout -b update-page
```
4. Edit the HTML pages, `public/styles.css`, or files in `public/assets/`. Preview the site locally from the public directory with any static web server, for example:
```bash
cd public/
python3 -m http.server
```
5. Commit your changes and push the branch to your fork:
```bash
git add .
git commit -m "Update page content"
git push -u origin update-page
```
6. Go to your fork on GitHub and click **Compare & pull request**. Set the base repository to `Protospace/main-website`, choose `master` as the base branch, describe what changed, and submit the pull request.
Maintainers will review the pull request, request changes if needed, and merge it when it is ready. After your pull request is merged, you can sync your fork before starting another change:
```bash
git checkout master
git fetch upstream
git reset --hard upstream/master
git push origin master --force-with-lease
```
There is no build step. Edit the generated HTML and CSS files directly. Keep the existing responsive layout, local asset paths, accessibility text, metadata, and external Wiki/Portal links intact.
## Deploying changes
The production server will automatically pull changes from this repo every 5 minutes.
## Using a coding agent
A coding agent can be helpful for larger content, layout, accessibility, and consistency changes. Give it a focused task and ask it to inspect the existing files before editing. Follow the repository instructions in `AGENTS.md` when present, and review the resulting diff before opening a pull request.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

-119
View File
@@ -1,119 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" href="/assets/favicon.png" type="image/png" />
<meta name="description" content="Explore Protospace’s electronics lab in Calgary, with workbenches, test equipment, soldering tools, and components for member projects." />
<link rel="canonical" href="https://protospace.ca/electronics-lab/" />
<title>Electronics Lab – Protospace</title>
<link rel="stylesheet" href="/styles.css" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": [
"LocalBusiness",
"NonprofitOrganization"
],
"@id": "https://protospace.ca/#organization",
"name": "Protospace",
"description": "Protospace is a volunteer-run, non-profit makerspace in Calgary with shared woodworking, metalworking, sewing, electronics, laser cutting, and 3D printing facilities.",
"url": "https://protospace.ca/",
"logo": "https://protospace.ca/assets/logo.png",
"image": "https://protospace.ca/assets/FrontProto-1024x455.jpg",
"telephone": "+1-587-774-5672",
"email": "info@protospace.ca",
"address": {
"@type": "PostalAddress",
"streetAddress": "1530 27th Avenue NE, Bay 108",
"addressLocality": "Calgary",
"addressRegion": "AB",
"postalCode": "T2E 7S6",
"addressCountry": "CA"
},
"areaServed": "Calgary, Alberta, Canada",
"priceRange": "$55/month",
"sameAs": [
"https://www.facebook.com/CalgaryProtospace/",
"https://www.youtube.com/user/calgaryprotospace/playlists"
],
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": "https://schema.org/Tuesday",
"opens": "19:00",
"closes": "21:00",
"description": "Public open house every Tuesday from 7:00 pm to 9:00 pm, including holidays. Visitors can drop in for a tour."
},
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+1-587-774-5672",
"email": "info@protospace.ca",
"contactType": "customer service"
}
}
</script>
</head>
<body>
<header>
<div class="nav-wrap">
<a class="brand" href="/"><img src="/assets/logo.png" alt="Protospace" /></a>
<nav class="nav">
<a href="/the-space/">About</a>
<a href="/join-us/">Membership</a>
<a href="http://wiki.protospace.ca">Wiki</a>
<a href="http://my.protospace.ca">Portal</a>
<a href="/contact/">Contact</a>
</nav>
</div>
</header>
<div class="content">
<div class="layout">
<main>
<h1>Electronics Lab</h1>
<div class="entry">
<figure>
<img width="1280" height="960" src="/assets/electronics-lab-2.jpg" alt="Electronics lab equipment" />
</figure>
<figure>
<img width="1024" height="768" src="/assets/electronics-lab-3-1024x768.jpg" alt="Electronics workbench and equipment" />
</figure>
</div>
</main>
<aside class="sidebar">
<section>
<h2>2026 Schedule:</h2>
<p class="green">
Open House every Tuesday from 7-9 pm (even holidays). Just drop in for a tour!
</p>
<p>
<a href="https://youtu.be/B9w5G6Rn99A"><b>Click here for a mini tour video</b></a>
</p>
</section>
<section>
<p>1530 27th Avenue NE<br />Bay 108<br />Calgary, Alberta, Canada<br />T2E 7S6</p>
<p><b>Parking:</b> For open house, park in the front lot (southeast) or on the street.</p>
<iframe
class="map"
src="https://maps.google.com/maps?width=300&amp;height=100&amp;hl=en&amp;q=Protospace%2C%20Calgary&amp;ie=UTF8&amp;t=&amp;z=12&amp;iwloc=B&amp;output=embed"
title="Map to Protospace"
></iframe>
</section>
</aside>
</div>
</div>
<footer>
<div class="footer-inner">
<div class="footer-links">
<div><a href="https://www.youtube.com/user/calgaryprotospace/playlists">YouTube</a></div>
<div><a href="http://wiki.protospace.ca/Faq">F.A.Q.</a></div>
<div><a href="https://www.facebook.com/CalgaryProtospace/">Facebook</a></div>
</div>
<h4>PROTOSPACE 2026 · All rights reserved</h4>
</div>
</footer>
</body>
</html>
+159
View File
@@ -0,0 +1,159 @@
#!/usr/bin/env python3
"""Export the Protospace Wiki tool catalog and its thumbnails.
Usage:
python3 export_tools.py
The wiki uses a small JavaScript challenge. The equivalent cookie is sent on
both the page request and each thumbnail request.
"""
from __future__ import annotations
import json
import re
import sys
from html.parser import HTMLParser
from pathlib import Path
from urllib.parse import urljoin, urlparse
from urllib.request import Request, urlopen
WIKI_URL = "https://wiki.protospace.ca/Tools_we_have"
WIKI_ROOT = "https://wiki.protospace.ca"
COOKIE = "human_check=verified"
ROOT = Path(__file__).resolve().parent
PUBLIC_ROOT = ROOT / "public"
OUTPUT = PUBLIC_ROOT / "tools.json"
IMAGE_DIR = PUBLIC_ROOT / "assets" / "tools"
class ToolsParser(HTMLParser):
"""Extract ordered category/gallery data from the MediaWiki HTML."""
def __init__(self) -> None:
super().__init__(convert_charrefs=True)
self.category: str | None = None
self.in_h2 = False
self.h2_text: list[str] = []
self.item: dict | None = None
self.item_depth = 0
self.anchor: dict | None = None
self.items: list[dict] = []
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
attrs_dict = dict(attrs)
if tag == "h2":
self.in_h2 = True
self.h2_text = []
elif tag == "li" and "gallerybox" in (attrs_dict.get("class") or "").split():
self.item = {"category": self.category, "anchors": [], "src": None}
self.item_depth = 1
elif self.item is not None and tag == "li":
self.item_depth += 1
elif self.item is not None and tag == "a":
self.anchor = {"href": attrs_dict.get("href"), "text": []}
elif self.item is not None and tag == "img" and self.item["src"] is None:
self.item["src"] = attrs_dict.get("src")
def handle_startendtag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
self.handle_starttag(tag, attrs)
self.handle_endtag(tag)
def handle_endtag(self, tag: str) -> None:
if tag == "h2" and self.in_h2:
category = " ".join("".join(self.h2_text).split())
if category and category.lower() != "contents":
self.category = category
self.in_h2 = False
elif tag == "a" and self.anchor is not None:
self.anchor["text"] = " ".join("".join(self.anchor["text"]).split())
if self.item is not None:
self.item["anchors"].append(self.anchor)
self.anchor = None
elif tag == "li" and self.item is not None:
self.item_depth -= 1
if self.item_depth == 0:
if self.item["category"] and self.item["category"].lower() != "removed equipment":
self.items.append(self.item)
self.item = None
def handle_data(self, data: str) -> None:
if self.in_h2:
self.h2_text.append(data)
if self.anchor is not None:
self.anchor["text"].append(data)
def fetch(url: str) -> tuple[bytes, str]:
request = Request(url, headers={"Cookie": COOKIE, "User-Agent": "Protospace tools exporter/1.0"})
with urlopen(request, timeout=30) as response:
content_type = response.headers.get_content_type()
return response.read(), content_type
def image_filename(src: str) -> str:
parts = [part for part in urlparse(src).path.split("/") if part]
# MediaWiki thumbnails end with e.g. 120px-182.jpg; the preceding part is
# the original filename and is stable even when thumbnail dimensions change.
if len(parts) >= 2:
return parts[-2]
return Path(parts[-1]).name
def main() -> int:
try:
page_bytes, content_type = fetch(WIKI_URL)
except Exception as exc:
print(f"Could not fetch {WIKI_URL}: {exc}", file=sys.stderr)
return 1
if content_type != "text/html" or b"Verifying you are human" in page_bytes:
print("The wiki returned the human-verification page; check the cookie.", file=sys.stderr)
return 1
parser = ToolsParser()
parser.feed(page_bytes.decode("utf-8", errors="replace"))
IMAGE_DIR.mkdir(parents=True, exist_ok=True)
exported: list[dict[str, str]] = []
skipped = 0
for raw in parser.items:
anchors = raw["anchors"]
name_anchor = next((a for a in anchors if "_ID:" in (a["href"] or "") or " ID:" in a["text"]), None)
name = name_anchor["text"] if name_anchor else ""
id_match = re.search(r"\bID:(\d+)\s*$", name)
photo_src = raw["src"]
if not name or not id_match or not photo_src:
skipped += 1
continue
photo_url = urljoin(WIKI_ROOT, photo_src)
source_filename = image_filename(photo_url)
filename = f"{id_match.group(1)}{Path(source_filename).suffix.lower()}"
destination = IMAGE_DIR / filename
try:
image_bytes, image_type = fetch(photo_url)
if not image_type.startswith("image/") or not image_bytes:
raise ValueError(f"response was {image_type}, not an image")
destination.write_bytes(image_bytes)
except Exception as exc:
print(f"Skipping {name}: broken photo ({exc})", file=sys.stderr)
skipped += 1
continue
link = urljoin(WIKI_ROOT, name_anchor["href"])
exported.append(
{
"ID": id_match.group(1),
"name": re.sub(r"\s*ID:\d+\s*$", "", name).strip(),
"photo file": f"/assets/tools/{filename}",
"link": link,
"category": raw["category"],
}
)
OUTPUT.write_text(json.dumps(exported, ensure_ascii=False, indent=2) + "\n")
print(f"Exported {len(exported)} tools to {OUTPUT}")
print(f"Skipped {skipped} tools without valid metadata or photos")
return 0
if __name__ == "__main__":
raise SystemExit(main())
-121
View File
@@ -1,121 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" href="/assets/favicon.png" type="image/png" />
<meta name="description" content="Explore Protospace’s laser cutting area in Calgary, including laser cutters and desktop CNC equipment for member projects." />
<link rel="canonical" href="https://protospace.ca/laser-cutters/" />
<title>Laser Cutters – Protospace</title>
<link rel="stylesheet" href="/styles.css" />
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": [
"LocalBusiness",
"NonprofitOrganization"
],
"@id": "https://protospace.ca/#organization",
"name": "Protospace",
"description": "Protospace is a volunteer-run, non-profit makerspace in Calgary with shared woodworking, metalworking, sewing, electronics, laser cutting, and 3D printing facilities.",
"url": "https://protospace.ca/",
"logo": "https://protospace.ca/assets/logo.png",
"image": "https://protospace.ca/assets/FrontProto-1024x455.jpg",
"telephone": "+1-587-774-5672",
"email": "info@protospace.ca",
"address": {
"@type": "PostalAddress",
"streetAddress": "1530 27th Avenue NE, Bay 108",
"addressLocality": "Calgary",
"addressRegion": "AB",
"postalCode": "T2E 7S6",
"addressCountry": "CA"
},
"areaServed": "Calgary, Alberta, Canada",
"priceRange": "$55/month",
"sameAs": [
"https://www.facebook.com/CalgaryProtospace/",
"https://www.youtube.com/user/calgaryprotospace/playlists"
],
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": "https://schema.org/Tuesday",
"opens": "19:00",
"closes": "21:00",
"description": "Public open house every Tuesday from 7:00 pm to 9:00 pm, including holidays. Visitors can drop in for a tour."
},
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+1-587-774-5672",
"email": "info@protospace.ca",
"contactType": "customer service"
}
}
</script>
</head>
<body>
<header>
<div class="nav-wrap">
<a class="brand" href="/"><img src="/assets/logo.png" alt="Protospace" /></a>
<nav class="nav">
<a href="/the-space/">About</a>
<a href="/join-us/">Membership</a>
<a href="http://wiki.protospace.ca">Wiki</a>
<a href="http://my.protospace.ca">Portal</a>
<a href="/contact/">Contact</a>
</nav>
</div>
</header>
<div class="content">
<div class="layout">
<main>
<h1>Laser Cutters</h1>
<div class="entry">
<figure>
<img width="1280" height="960" src="/assets/laser-cutters-1.jpg" alt="Laser cutter in the laser cutting area" />
</figure>
<p>Our laser cutting area also includes two mini desktop CNC mills:</p>
<figure>
<img width="1280" height="960" src="/assets/laser-cutters-3.jpg" alt="Laser cutter and CNC equipment" />
</figure>
</div>
</main>
<aside class="sidebar">
<section>
<h2>2026 Schedule:</h2>
<p class="green">
Open House every Tuesday from 7-9 pm (even holidays). Just drop in for a tour!
</p>
<p>
<a href="https://youtu.be/B9w5G6Rn99A"><b>Click here for a mini tour video</b></a>
</p>
</section>
<section>
<p>1530 27th Avenue NE<br />Bay 108<br />Calgary, Alberta, Canada<br />T2E 7S6</p>
<p><b>Parking:</b> For open house, park in the front lot (southeast) or on the street.</p>
<iframe
class="map"
src="https://maps.google.com/maps?width=300&amp;height=100&amp;hl=en&amp;q=Protospace%2C%20Calgary&amp;ie=UTF8&amp;t=&amp;z=12&amp;iwloc=B&amp;output=embed"
title="Map to Protospace"
></iframe>
</section>
</aside>
</div>
</div>
<footer>
<div class="footer-inner">
<div class="footer-links">
<div><a href="https://www.youtube.com/user/calgaryprotospace/playlists">YouTube</a></div>
<div><a href="http://wiki.protospace.ca/Faq">F.A.Q.</a></div>
<div><a href="https://www.facebook.com/CalgaryProtospace/">Facebook</a></div>
</div>
<h4>PROTOSPACE 2026 · All rights reserved</h4>
</div>
</footer>
</body>
</html>
@@ -7,7 +7,15 @@
<link rel="icon" href="/assets/favicon.png" type="image/png" />
<meta name="description" content="Explore Protospace’s 3D printer village in Calgary, with filament and resin printers available to trained members." />
<link rel="canonical" href="https://protospace.ca/3d-printers/" />
<meta property="og:title" content="3D Printers – Protospace" />
<meta property="og:description" content="Explore Protospace’s 3D printer village in Calgary, with filament and resin printers available to trained members." />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://protospace.ca/3d-printers/" />
<meta property="og:image" content="https://protospace.ca/assets/3d-printers-1-800.jpg" />
<meta property="og:site_name" content="Protospace" />
<meta name="twitter:card" content="summary_large_image" />
<title>3D Printers – Protospace</title>
<link rel="preload" href="/styles.css" as="style" />
<link rel="stylesheet" href="/styles.css" />
<script type="application/ld+json">
{
@@ -58,13 +66,14 @@
<body>
<header>
<div class="nav-wrap">
<a class="brand" href="/"><img src="/assets/logo.png" alt="Protospace" /></a>
<a class="brand" href="/"><img width="360" height="70" src="/assets/logo-white.svg" alt="Protospace" /></a>
<nav class="nav">
<a href="/the-space/">About</a>
<a href="/the-space/">Spaces</a>
<a href="/tools/">Tools</a>
<a href="/join-us/">Membership</a>
<a href="http://wiki.protospace.ca">Wiki</a>
<a href="http://my.protospace.ca">Portal</a>
<a href="/contact/">Contact</a>
<a href="https://my.protospace.ca" class="external">Portal</a>
<a href="https://wiki.protospace.ca" class="external">Wiki</a>
</nav>
</div>
</header>
@@ -74,18 +83,25 @@
<h1>3D Printers</h1>
<div class="entry">
<figure>
<img width="1024" height="768" src="/assets/3d-printers-1-1024x768.jpg" alt="3D printer in the printer village" />
<picture>
<source type="image/webp" srcset="/assets/3d-printers-1-400.webp 400w, /assets/3d-printers-1-800.webp 800w, /assets/3d-printers-1-1600.webp 1600w" sizes="(max-width: 760px) calc(100vw - 24px), 750px" />
<img width="800" height="602" src="/assets/3d-printers-1-800.jpg" srcset="/assets/3d-printers-1-400.jpg 400w, /assets/3d-printers-1-800.jpg 800w, /assets/3d-printers-1-1600.jpg 1600w" sizes="(max-width: 760px) calc(100vw - 24px), 750px" alt="Resin 3D printers and printing supplies" fetchpriority="high" decoding="async" />
</picture>
</figure>
<figure>
<img width="1024" height="768" src="/assets/3d-printers-2-1024x768.jpg" alt="3D printer equipment" />
<picture>
<source type="image/webp" srcset="/assets/3d-printers-2-400.webp 400w, /assets/3d-printers-2-800.webp 800w, /assets/3d-printers-2-1600.webp 1600w" sizes="(max-width: 760px) calc(100vw - 24px), 750px" />
<img width="800" height="602" src="/assets/3d-printers-2-800.jpg" srcset="/assets/3d-printers-2-400.jpg 400w, /assets/3d-printers-2-800.jpg 800w, /assets/3d-printers-2-1600.jpg 1600w" sizes="(max-width: 760px) calc(100vw - 24px), 750px" alt="3D printer village with filament and resin printers" loading="lazy" decoding="async" />
</picture>
</figure>
<figure>
<img width="1024" height="768" src="/assets/3d-printers-3-1024x768.jpg" alt="3D printer in operation" />
<picture>
<source type="image/webp" srcset="/assets/3d-printers-3-400.webp 400w, /assets/3d-printers-3-800.webp 800w, /assets/3d-printers-3-1600.webp 1600w" sizes="(max-width: 760px) calc(100vw - 24px), 750px" />
<img width="800" height="602" src="/assets/3d-printers-3-800.jpg" srcset="/assets/3d-printers-3-400.jpg 400w, /assets/3d-printers-3-800.jpg 800w, /assets/3d-printers-3-1600.jpg 1600w" sizes="(max-width: 760px) calc(100vw - 24px), 750px" alt="Desktop 3D printers and print supplies" loading="lazy" decoding="async" />
</picture>
</figure>
<p></p>
</div>
</main>
<aside class="sidebar">
@@ -95,7 +111,7 @@
Open House every Tuesday from 7-9 pm (even holidays). Just drop in for a tour!
</p>
<p>
<a href="https://youtu.be/B9w5G6Rn99A"><b>Click here for a mini tour video</b></a>
<a href="https://youtu.be/B9w5G6Rn99A" class="external">Watch the Protospace mini tour video</a>
</p>
</section>
<section>
@@ -103,6 +119,7 @@
<p><b>Parking:</b> For open house, park in the front lot (southeast) or on the street.</p>
<iframe
class="map"
loading="lazy"
src="https://maps.google.com/maps?width=300&amp;height=100&amp;hl=en&amp;q=Protospace%2C%20Calgary&amp;ie=UTF8&amp;t=&amp;z=12&amp;iwloc=B&amp;output=embed"
title="Map to Protospace"
></iframe>
@@ -113,11 +130,11 @@
<footer>
<div class="footer-inner">
<div class="footer-links">
<div><a href="https://www.youtube.com/user/calgaryprotospace/playlists">YouTube</a></div>
<div><a href="http://wiki.protospace.ca/Faq">F.A.Q.</a></div>
<div><a href="https://www.facebook.com/CalgaryProtospace/">Facebook</a></div>
<div><a href="https://www.youtube.com/user/calgaryprotospace/playlists" class="external">YouTube</a></div>
<div><a href="https://github.com/Protospace" class="external">GitHub</a></div>
<div><a href="https://www.facebook.com/CalgaryProtospace/" class="external">Facebook</a></div>
</div>
<h4>PROTOSPACE 2026 · All rights reserved</h4>
<p>© PROTOSPACE 2026 · <a href="https://github.com/Protospace/main-website" class="external">Source Code</a></p>
</div>
</footer>
</body>
Binary file not shown.

After

Width:  |  Height:  |  Size: 461 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Before

Width:  |  Height:  |  Size: 252 KiB

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Some files were not shown because too many files have changed in this diff Show More