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

Merged
JoeBot merged 1 commits from 20260622_optimizations into main 2026-06-23 06:48:14 -04:00
5 changed files with 188 additions and 27 deletions
Showing only changes of commit b69e0baaa0 - Show all commits

22
_headers Normal file
View File

@@ -0,0 +1,22 @@
# Cache-Control for static, stable-by-filename assets
/img/*
Cache-Control: public, max-age=2592000
/favicon.svg
Cache-Control: public, max-age=2592000
/favicon-16.png
Cache-Control: public, max-age=2592000
/favicon-32.png
Cache-Control: public, max-age=2592000
/apple-touch-icon.png
Cache-Control: public, max-age=2592000
# CSS/JS have no cache-busting hash yet; cache modestly
/css/*
Cache-Control: public, max-age=86400
/js/*
Cache-Control: public, max-age=86400
# Baseline security headers on every response
/*
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin

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;

View File

@@ -19,8 +19,6 @@
<link rel="apple-touch-icon" href="apple-touch-icon.png">
<meta name="theme-color" content="#231F20">
<!-- Bootstrap grid/utilities -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -87,7 +85,7 @@
<p>Create lifelong memories with your family in a luxury, custom home within a secure and quiet community. Welcome home to Pine Creek.</p>
</div>
<div class="col-lg-6">
<img class="feature-img" src="img/gallery/IMG_1964.jpg" alt="A stately custom stone home in Pine Creek" loading="lazy">
<img class="feature-img" src="img/gallery/IMG_1964.jpg" alt="A stately custom stone home in Pine Creek" loading="lazy" decoding="async">
</div>
</div>
</section>
@@ -106,9 +104,9 @@
<div class="col-lg-7">
<div class="gallery" data-gallery="vineyard" aria-roledescription="carousel">
<div class="gallery-track">
<img class="gallery-img is-active" src="img/gallery/IMG_1937.jpg" alt="Tuscan-style Vineyard home with terracotta tile roof" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1941.jpg" alt="Mediterranean-style Vineyard home with terracotta tile roof" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1943.jpg" alt="Spanish-Tuscan Vineyard estate with stucco and stone" loading="lazy">
<img class="gallery-img is-active" src="img/gallery/IMG_1937.jpg" alt="Tuscan-style Vineyard home with terracotta tile roof" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1941.jpg" alt="Mediterranean-style Vineyard home with terracotta tile roof" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1943.jpg" alt="Spanish-Tuscan Vineyard estate with stucco and stone" loading="lazy" decoding="async">
</div>
<button class="gallery-arrow prev" aria-label="Previous image">
<svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
@@ -138,16 +136,16 @@
<div class="col-lg-7 order-lg-1">
<div class="gallery" data-gallery="estate" aria-roledescription="carousel">
<div class="gallery-track">
<img class="gallery-img is-active" src="img/gallery/IMG_1947.jpg" alt="Brick and stone French country estate" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1922.jpg" alt="Stately brick custom home on a manicured lawn" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1945.jpg" alt="Stone custom home with circular drive" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1971.jpg" alt="French country home with stone accents" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1977.jpg" alt="Brick and stone estate with mature landscaping" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_2978.jpg" alt="Custom estate framed by trees" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1985.jpg" alt="Stone manor home at dusk" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1955.jpg" alt="Stone and stucco custom home with circular drive" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1980.jpg" alt="Sprawling brick and stone estate" loading="lazy">
<img class="gallery-img" src="img/gallery/IMG_1914.jpg" alt="Brick estate with formal fountain garden" loading="lazy">
<img class="gallery-img is-active" src="img/gallery/IMG_1947.jpg" alt="Brick and stone French country estate" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1922.jpg" alt="Stately brick custom home on a manicured lawn" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1945.jpg" alt="Stone custom home with circular drive" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1971.jpg" alt="French country home with stone accents" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1977.jpg" alt="Brick and stone estate with mature landscaping" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_2978.jpg" alt="Custom estate framed by trees" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1985.jpg" alt="Stone manor home at dusk" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1955.jpg" alt="Stone and stucco custom home with circular drive" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1980.jpg" alt="Sprawling brick and stone estate" loading="lazy" decoding="async">
<img class="gallery-img" src="img/gallery/IMG_1914.jpg" alt="Brick estate with formal fountain garden" loading="lazy" decoding="async">
</div>
<button class="gallery-arrow prev" aria-label="Previous image">
<svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
@@ -175,12 +173,12 @@
<div class="col-lg-7">
<div class="gallery" data-gallery="community" aria-roledescription="carousel">
<div class="gallery-track">
<img class="gallery-img is-active" src="img/community/community-pavilion-pond.jpg" alt="Lakeside pavilion overlooking the community pond and fountain" loading="lazy">
<img class="gallery-img" src="img/community/community-pavilion.jpg" alt="Aerial view of the community pavilion and gardens" loading="lazy">
<img class="gallery-img" src="img/community/community-vineyard-gate.jpg" alt="The Vineyard entrance monument with stone cupola" loading="lazy">
<img class="gallery-img" src="img/community/community-vineyard-rows.jpg" alt="Manicured vineyard rows lining a community street" loading="lazy">
<img class="gallery-img" src="img/community/community-aerial.jpg" alt="Aerial view of Pine Creek's tree-lined streets and estates" loading="lazy">
<img class="gallery-img" src="img/community/community-forest-pond.jpg" alt="Pine Creek homes nestled among the woods near the pond" loading="lazy">
<img class="gallery-img is-active" src="img/community/community-pavilion-pond.jpg" alt="Lakeside pavilion overlooking the community pond and fountain" loading="lazy" decoding="async">
<img class="gallery-img" src="img/community/community-pavilion.jpg" alt="Aerial view of the community pavilion and gardens" loading="lazy" decoding="async">
<img class="gallery-img" src="img/community/community-vineyard-gate.jpg" alt="The Vineyard entrance monument with stone cupola" loading="lazy" decoding="async">
<img class="gallery-img" src="img/community/community-vineyard-rows.jpg" alt="Manicured vineyard rows lining a community street" loading="lazy" decoding="async">
<img class="gallery-img" src="img/community/community-aerial.jpg" alt="Aerial view of Pine Creek's tree-lined streets and estates" loading="lazy" decoding="async">
<img class="gallery-img" src="img/community/community-forest-pond.jpg" alt="Pine Creek homes nestled among the woods near the pond" loading="lazy" decoding="async">
</div>
<button class="gallery-arrow prev" aria-label="Previous image">
<svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>
@@ -204,7 +202,7 @@
<p class="section-lead">Winding, tree-lined streets &mdash; Tuscany Drive, Chardonnay Circle, Willowood Road, Stone Ridge Road, Richwood Circle, and Rolling Ridge Drive &mdash; thread through Pine Creek's gently rolling, one-acre-plus homesites.</p>
</div>
<div class="map-frame">
<img class="map-img" src="img/community-map.jpg" alt="Map of the Pine Creek community streets" loading="lazy">
<img class="map-img" src="img/community-map.jpg" alt="Map of the Pine Creek community streets" loading="lazy" decoding="async">
</div>
</section>
@@ -220,14 +218,14 @@
<p>The town is conveniently located right off I-85, within 30 minutes of uptown Charlotte and Charlotte Douglas International Airport. With growing businesses, a vast array of shops and restaurants, a beautiful downtown, sought-after Cabarrus County and private schools, and the grandeur of the NC Research Campus, Kannapolis is a wonderful place to call home.</p>
</div>
<div class="col-lg-6">
<img class="feature-img" src="img/kannapolis-nc.jpg" alt="Downtown Kannapolis, North Carolina" loading="lazy">
<img class="feature-img" src="img/kannapolis-nc.jpg" alt="Downtown Kannapolis, North Carolina" loading="lazy" decoding="async">
</div>
</div>
<div class="map-embed">
<iframe
title="Map of Pine Creek in Kannapolis, North Carolina"
src="https://www.google.com/maps?q=Tuscany%20Dr%2C%20Kannapolis%2C%20NC%2028081&z=15&output=embed"
loading="lazy" referrerpolicy="no-referrer-when-downgrade" allowfullscreen></iframe>
loading="lazy" decoding="async" referrerpolicy="no-referrer-when-downgrade" allowfullscreen></iframe>
</div>
</section>