- Best way to learn is to teach. When I find something interesting I write a short tutorial — mainly to
- consolidate my own understanding, but if it helps someone else that's a win.
-
-
- Had to really try my best to not touch anything here. Every tutorial here is exacty how it was when I wrote
- it in sixth form.
-
+
-
- 3 Jan 2021
- Summed-area tables
- Counting cells in a summed-area table, investigating how they work and why
- they're efficient.
+
@@ -396,12 +455,10 @@
& the
p5.js team for starting this obsession.
diff --git a/script.js b/script.js
index 981bfa2..813906f 100644
--- a/script.js
+++ b/script.js
@@ -22,3 +22,30 @@ window.addEventListener('scroll', () => {
a.style.color = a.getAttribute('href') === `#${current}` ? 'var(--mauve)' : '';
});
});
+
+// ─── Project filters ──────────────────────────────────────────────
+const filterChips = document.querySelectorAll('.filter-chip');
+const projectCards = document.querySelectorAll('#project-grid .project-card');
+
+if (filterChips.length > 0 && projectCards.length > 0) {
+ const applyFilter = (filter) => {
+ projectCards.forEach(card => {
+ const tech = (card.dataset.tech || '').split(/\s+/).filter(Boolean);
+ const shouldShow = filter === 'all' || tech.includes(filter);
+ card.classList.toggle('is-hidden', !shouldShow);
+ });
+ };
+
+ filterChips.forEach(chip => {
+ chip.addEventListener('click', () => {
+ const selected = chip.dataset.filter || 'all';
+
+ filterChips.forEach(other => {
+ other.classList.toggle('active', other === chip);
+ });
+
+ applyFilter(selected);
+ });
+ });
+}
+
diff --git a/style.css b/style.css
index 44f7321..427c2e7 100644
--- a/style.css
+++ b/style.css
@@ -515,6 +515,8 @@ section.full-width {
.tag-mauve { background: rgba(203,166,247,0.12); color: var(--mauve); }
.tag-yellow { background: rgba(249,226,175,0.12); color: var(--yellow); }
.tag-sky { background: rgba(137,220,235,0.12); color: var(--sky); }
+.tag-red { background: rgba(243,139,168,0.12); color: var(--red); }
+.tag-pink { background: rgba(245,194,231,0.12); color: var(--pink); }
/* ─── Tutorials / Projects shared ─────────────────────── */
.section-heading {
@@ -535,6 +537,71 @@ section.full-width {
margin-bottom: 3rem;
}
+.projects-subheading {
+ margin: 0 0 0.9rem;
+ font-size: 0.7rem;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--mauve);
+}
+
+.card-grid.card-grid--featured {
+ margin-bottom: 2rem;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+#projects .section-intro {
+ margin-bottom: 1.8rem;
+ max-width: 640px;
+}
+
+#project-grid {
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
+}
+
+#project-grid .card {
+ padding: 1.2rem;
+}
+
+.project-filters {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ border: none;
+ padding: 0;
+ min-width: 0;
+ margin-bottom: 1.25rem;
+}
+
+.filter-chip {
+ border: 1px solid var(--surface1);
+ background: var(--mantle);
+ color: var(--subtext0);
+ border-radius: 999px;
+ padding: 0.32rem 0.8rem;
+ font-size: 0.68rem;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ font-family: 'JetBrains Mono', monospace;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+.filter-chip:hover {
+ border-color: var(--mauve);
+ color: var(--text);
+}
+
+.filter-chip.active {
+ background: rgba(203,166,247,0.18);
+ border-color: rgba(203,166,247,0.7);
+ color: var(--mauve);
+}
+
+.card.is-hidden {
+ display: none;
+}
+
/* ─── Card grid ────────────────────────────────────────── */
.card-grid {
display: grid;
@@ -725,6 +792,7 @@ footer a:hover { color: var(--mauve); }
nav { padding: 0 1.5rem; }
.nav-links { gap: 1.2rem; }
section { padding: 6rem 1.5rem 4rem; }
+ .card-grid.card-grid--featured { grid-template-columns: 1fr; }
.about-columns { grid-template-columns: 1fr; }
.about-block-tag { width: 4rem; }
.skills-grid { grid-template-columns: 1fr; }