Skip to content

Commit 0015a74

Browse files
Mazizovaclaude
andauthored
Add the MySQL Production Stack map as an unlisted Resources page with a full-screen view (#149)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent c5764af commit 0015a74

7 files changed

Lines changed: 220 additions & 0 deletions

File tree

‎assets/scss/_image-view.scss‎

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
// A single large image with a full-screen view (layouts/_default/image-view.html).
2+
3+
.map-page {
4+
max-width: 1320px;
5+
margin: 0 auto;
6+
padding: 120px 2rem 80px;
7+
}
8+
9+
.map-header {
10+
margin: 1rem 0 1.5rem;
11+
12+
h1 { margin-bottom: 0.5rem; }
13+
}
14+
15+
.map-lead {
16+
max-width: 760px;
17+
font-size: 1.125rem;
18+
color: $muted;
19+
margin: 0;
20+
}
21+
22+
.map-toolbar {
23+
display: flex;
24+
flex-wrap: wrap;
25+
gap: 0.625rem;
26+
margin-bottom: 1.25rem;
27+
}
28+
29+
.map-btn {
30+
display: inline-flex;
31+
align-items: center;
32+
padding: 0.5rem 1rem;
33+
font: inherit;
34+
font-size: 0.9375rem;
35+
font-weight: 600;
36+
color: $accent;
37+
background: rgba($accent, 0.08);
38+
border: 1px solid rgba($accent, 0.3);
39+
border-radius: 8px;
40+
text-decoration: none;
41+
cursor: pointer;
42+
transition: background 0.15s ease;
43+
44+
&:hover { background: rgba($accent, 0.16); }
45+
}
46+
47+
.map-btn--primary {
48+
color: #05121a;
49+
background: $accent;
50+
border-color: $accent;
51+
52+
&:hover { background: lighten($accent, 8%); }
53+
}
54+
55+
.map-stage {
56+
position: relative;
57+
margin: 0;
58+
border: 1px solid $dark-border;
59+
border-radius: 12px;
60+
overflow: hidden;
61+
background: #ffffff;
62+
63+
img {
64+
display: block;
65+
width: 100%;
66+
height: auto;
67+
cursor: zoom-in;
68+
}
69+
70+
.map-close { display: none; }
71+
72+
// Full-window view: the map fits the screen, a click zooms to its native size.
73+
&.is-full {
74+
position: fixed;
75+
inset: 0;
76+
z-index: 1000;
77+
border: 0;
78+
border-radius: 0;
79+
overflow: auto;
80+
background: #0b0e17;
81+
display: flex;
82+
align-items: center;
83+
justify-content: center;
84+
85+
img {
86+
width: auto;
87+
height: auto;
88+
max-width: 100vw;
89+
max-height: 100vh;
90+
object-fit: contain;
91+
cursor: zoom-in;
92+
}
93+
94+
.map-close {
95+
display: flex;
96+
position: fixed;
97+
top: 14px;
98+
right: 14px;
99+
z-index: 2;
100+
width: 40px;
101+
height: 40px;
102+
align-items: center;
103+
justify-content: center;
104+
font-size: 1.125rem;
105+
color: #ffffff;
106+
background: rgba(15, 23, 42, 0.8);
107+
border: 1px solid rgba(255, 255, 255, 0.35);
108+
border-radius: 50%;
109+
cursor: pointer;
110+
}
111+
112+
&.is-zoomed {
113+
display: block;
114+
115+
img {
116+
max-width: none;
117+
max-height: none;
118+
width: 2000px;
119+
cursor: zoom-out;
120+
}
121+
}
122+
}
123+
}
124+
125+
html.map-locked { overflow: hidden; }
126+
127+
@media (max-width: 700px) {
128+
.map-page { padding: 96px 1rem 56px; }
129+
.map-btn { flex: 1 1 auto; justify-content: center; }
130+
}

‎assets/scss/_light-theme.scss‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,10 @@
12421242
.resource-case-title { color: #0f172a; }
12431243
.post-author-role { color: #64748b; }
12441244
.resource-case-meta { color: #64748b; }
1245+
.map-lead { color: #64748b; }
1246+
.map-btn { color: #0272a5; background: rgba(0, 200, 255, 0.08); border-color: rgba(0, 200, 255, 0.3); }
1247+
.map-btn--primary { color: #ffffff; background: #0272a5; border-color: #0272a5; }
1248+
.map-stage { border-color: #dde3ed; }
12451249
.post-content .update-banner { background: rgba(2, 114, 165, 0.07); border-color: rgba(2, 114, 165, 0.3); color: #334155; strong { color: #0f172a; } }
12461250
.post-content .guest-comment { background: #ffffff; border-color: #dde3ed; border-left-color: #00c8ff; .guest-comment-label { color: #0272a5; background: rgba(2, 114, 165, 0.08); border-color: rgba(2, 114, 165, 0.3); } .guest-comment-signature { color: #0f172a; } .guest-comment-cta { border-top-color: #dde3ed; color: #334155; } }
12471251

‎assets/scss/main.scss‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ img {
4242
@import "event-landing";
4343
@import "home";
4444
@import "showcase";
45+
@import "image-view";
4546
@import "footer";
4647
@import "light-theme";
4748

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
title: "The MySQL Production Stack"
3+
description: "A field guide to tools for MySQL-compatible databases, layer by layer: drivers and ORMs, proxies, sharding, clustering, database servers, observability, analytics, deployment and backup."
4+
url: "/resources/mysql-production-stack/"
5+
layout: "image-view"
6+
date: 2026-10-26
7+
images:
8+
- map.jpg
9+
# The map itself, shown on the page (the JPEG above is the sharing image).
10+
map_image: "map.webp"
11+
map_alt: "A map of the MySQL ecosystem, layer by layer. Application: drivers and ORMs. Proxy and cache. Sharding. Cluster and replication. Database server. Alongside: observability (dashboards, terminal tools, query and configuration tools), stream to analytics (BI, analytics stores, change data capture), deploy and operate (Kubernetes operators, platforms) and protect (backup, recovery, forensics)."
12+
---
445 KB
Loading
192 KB
Loading

‎layouts/_default/image-view.html‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{{/* A single large image (a map or diagram) with a full-screen view.
2+
Front matter: map_image (file in the page bundle), map_alt. Open the page with #full
3+
to land straight in the full-screen view. */}}
4+
{{ define "main" }}
5+
{{ $img := .Resources.GetMatch (.Params.map_image | default "map.webp") }}
6+
<div class="map-page">
7+
<nav class="resources-breadcrumb" aria-label="Breadcrumb">
8+
<a href="/resources/">Resources</a>
9+
<span class="breadcrumb-sep" aria-hidden="true">/</span>
10+
<span>{{ .Title }}</span>
11+
</nav>
12+
13+
<header class="map-header">
14+
<h1>{{ .Title }}</h1>
15+
{{ with .Description }}<p class="map-lead">{{ . }}</p>{{ end }}
16+
</header>
17+
18+
{{ with $img }}
19+
<div class="map-toolbar">
20+
<button type="button" class="map-btn map-btn--primary" id="map-fullscreen">Full screen</button>
21+
<a href="{{ .RelPermalink }}" class="map-btn" target="_blank" rel="noopener">Open the image ↗</a>
22+
<a href="{{ .RelPermalink }}" class="map-btn" download>Download</a>
23+
<a href="/resources/software/" class="map-btn">Browse the tools →</a>
24+
</div>
25+
26+
<figure class="map-stage" id="map-stage">
27+
<button type="button" class="map-close" id="map-close" aria-label="Close full screen">✕</button>
28+
<img src="{{ .RelPermalink }}" alt="{{ $.Params.map_alt }}" width="{{ .Width }}" height="{{ .Height }}" id="map-img">
29+
</figure>
30+
{{ end }}
31+
</div>
32+
33+
<script>
34+
(function () {
35+
var stage = document.getElementById('map-stage');
36+
if (!stage) return;
37+
var openBtn = document.getElementById('map-fullscreen');
38+
var closeBtn = document.getElementById('map-close');
39+
var img = document.getElementById('map-img');
40+
41+
function open() {
42+
stage.classList.add('is-full');
43+
document.documentElement.classList.add('map-locked');
44+
if (stage.requestFullscreen) { try { stage.requestFullscreen().catch(function () {}); } catch (e) {} }
45+
if (location.hash !== '#full') history.replaceState(null, '', '#full');
46+
}
47+
function close() {
48+
stage.classList.remove('is-full', 'is-zoomed');
49+
document.documentElement.classList.remove('map-locked');
50+
if (document.fullscreenElement && document.exitFullscreen) { try { document.exitFullscreen(); } catch (e) {} }
51+
if (location.hash === '#full') history.replaceState(null, '', location.pathname + location.search);
52+
}
53+
54+
openBtn.addEventListener('click', open);
55+
closeBtn.addEventListener('click', close);
56+
img.addEventListener('click', function () {
57+
if (!stage.classList.contains('is-full')) { open(); return; }
58+
stage.classList.toggle('is-zoomed');
59+
});
60+
document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && stage.classList.contains('is-full')) close(); });
61+
document.addEventListener('fullscreenchange', function () {
62+
if (!document.fullscreenElement && stage.classList.contains('is-full')) close();
63+
});
64+
65+
// Landing on /#full shows the map filling the window (browsers only allow the
66+
// true full-screen mode after a click, so this uses the in-page full-window view).
67+
if (location.hash === '#full') {
68+
stage.classList.add('is-full');
69+
document.documentElement.classList.add('map-locked');
70+
}
71+
})();
72+
</script>
73+
{{ end }}

0 commit comments

Comments
 (0)