Optimize: drop Bootstrap CDN, add caching headers & image hints

Performance / self-hosting
- Remove the render-blocking Bootstrap 4 CDN stylesheet (~160 KB + extra
  DNS/TLS to a third party) and replace it with ~60 lines of self-hosted
  grid/utility CSS covering the small subset actually used (container, row,
  col-lg-5/6/7, order-lg, align-items-center, text-center, table-responsive,
  spacing, and the button base). Verified across desktop/tablet/mobile.
- Add a Cloudflare Pages `_headers` file: 30-day cache for static media and
  icons, 1-day for css/js, plus baseline security headers
  (X-Content-Type-Options, Referrer-Policy).
- Add decoding="async" to all lazily-loaded images.

The site now loads no third-party CSS; only Google Fonts remain external.
SCSS source kept in sync (new components/_grid partial; button base + reset
mirrored in main.scss).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 06:25:18 -04:00
parent 50e8b1bcd3
commit b69e0baaa0
5 changed files with 188 additions and 27 deletions

49
css/components/_grid.scss Normal file
View File

@@ -0,0 +1,49 @@
// Minimal grid & utilities — self-hosted replacement for the small subset of
// Bootstrap 4 this site used. Removes a render-blocking ~160 KB external
// stylesheet. Breakpoint lg = 992px (one past $m-break: 991px).
.container {
width: 100%;
max-width: 1140px;
margin-left: auto;
margin-right: auto;
padding-left: 15px;
padding-right: 15px;
}
.row {
display: flex;
flex-wrap: wrap;
margin-left: -15px;
margin-right: -15px;
> [class*="col-"] {
position: relative;
width: 100%;
padding-left: 15px;
padding-right: 15px;
}
}
// mobile-first: columns are full width until the lg breakpoint
[class*="col-lg-"] { flex: 0 0 100%; max-width: 100%; }
.align-items-center { align-items: center; }
.text-center { text-align: center; }
.mb-4 { margin-bottom: 1.5rem; }
.table-responsive {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (min-width: 992px) {
.col-lg-5 { flex: 0 0 41.6667%; max-width: 41.6667%; }
.col-lg-6 { flex: 0 0 50%; max-width: 50%; }
.col-lg-7 { flex: 0 0 58.3333%; max-width: 58.3333%; }
.order-lg-1 { order: 1; }
.order-lg-2 { order: 2; }
.mb-lg-0 { margin-bottom: 0; }
}

View File

@@ -34,9 +34,71 @@ html { scroll-behavior: smooth; }
p, a, li, span, div, td, th { font-family: "Lato", sans-serif; }
h1, h2, h3, h4, h5, h6 { font-family: "Libre Baskerville", serif; }
img { max-width: 100%; }
img { max-width: 100%; vertical-align: middle; }
h1, h2, h3, h4, h5, h6 { margin-top: 0; }
p { margin-top: 0; margin-bottom: 1rem; }
/* ============================================================
MINIMAL GRID & UTILITIES
Self-hosted replacement for the small subset of Bootstrap 4
this site used (grid, a few utilities, button base). Removes a
render-blocking ~160 KB external stylesheet. Breakpoint lg = 992px.
============================================================ */
.container {
width: 100%;
max-width: 1140px;
margin-left: auto;
margin-right: auto;
padding-left: 15px;
padding-right: 15px;
}
.row {
display: flex;
flex-wrap: wrap;
margin-left: -15px;
margin-right: -15px;
}
.row > [class*="col-"] {
position: relative;
width: 100%;
padding-left: 15px;
padding-right: 15px;
}
/* mobile-first: columns are full width until the lg breakpoint */
[class*="col-lg-"] { flex: 0 0 100%; max-width: 100%; }
.align-items-center { align-items: center; }
.text-center { text-align: center; }
.mb-4 { margin-bottom: 1.5rem; }
.table-responsive {
display: block;
width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@media (min-width: 992px) {
.col-lg-5 { flex: 0 0 41.6667%; max-width: 41.6667%; }
.col-lg-6 { flex: 0 0 50%; max-width: 50%; }
.col-lg-7 { flex: 0 0 58.3333%; max-width: 58.3333%; }
.order-lg-1 { order: 1; }
.order-lg-2 { order: 2; }
.mb-lg-0 { margin-bottom: 0; }
}
/* ---------- Buttons ---------- */
.btn {
display: inline-block;
vertical-align: middle;
text-align: center;
border: 1px solid transparent;
line-height: 1.5;
cursor: pointer;
text-decoration: none;
user-select: none;
}
.btn {
font-family: "Libre Baskerville", serif;
font-size: 14px;
@@ -46,10 +108,18 @@ img { max-width: 100%; }
}
.btn + .btn { margin-left: 0.5rem; }
.btn-light {
color: #212529;
background-color: #f8f9fa;
border-color: #f8f9fa;
}
.btn-primary {
color: var(--white);
background-color: var(--gold);
border-color: var(--gold);
}
.btn-primary:hover { color: var(--white); }
.btn-primary:hover {
background-color: var(--gold-dark);
border-color: var(--gold-dark);

View File

@@ -22,9 +22,19 @@ $l-break: 1199px;
* { box-sizing: border-box; }
@import 'components/grid';
//btns
.btn {
@include Libre;
display: inline-block;
vertical-align: middle;
text-align: center;
border: 1px solid transparent;
line-height: 1.5;
cursor: pointer;
text-decoration: none;
user-select: none;
font-size: 14px;
letter-spacing: 0.3px;
padding: 0.55rem 1.5rem;
@@ -33,10 +43,19 @@ $l-break: 1199px;
& + .btn { margin-left: 0.5rem; }
}
.btn-light {
color: #212529;
background-color: #f8f9fa;
border-color: #f8f9fa;
}
.btn-primary {
color: $white;
background-color: $gold;
border-color: $gold;
&:hover { color: $white; }
&:hover {
background-color: darken($gold, 10%);
border-color: darken($gold, 10%);
@@ -73,9 +92,12 @@ p, a, li, span, div, td, th {
h1, h2, h3, h4, h5, h6 {
@include Libre;
margin-top: 0;
}
img { max-width: 100%; }
p { margin-top: 0; margin-bottom: 1rem; }
img { max-width: 100%; vertical-align: middle; }
.page-content {
padding-top: 92px;