Automation & Agents
Premium Feel Is a Cost Problem, Not a Tech Problem: Lighthouse 100 With Zero Animation Libraries, Plus CSS/JS Snippets and a 4-Point Security Hardening
When you want that expensive AI-brand look, the reflex is to install GSAP or Framer Motion. But the premium feel comes from restraint and layering, not from libraries. This note gives you copy-paste code and explains the hidden culprit that dragged a performance score down to 56.
One Core Idea
Premium feel = restraint + layering, not piled-up effects. Well-crafted sites mostly subtract: layered backgrounds, few and precise animations, fast loads. Every extra library usually buys you maintenance burden and a lower score, not better polish.
Step by Step (about half a day)
Step 1 | Four tiers of dark background
Don’t use flat black. Define four backgrounds from deep to light:
:root {
--bg-deep: #060608; /* bottom layer, holds gradient glow */
--bg: #101014; /* page background */
--surface: #18181D; /* cards */
--surface-2: #1F1F26; /* card hover, nested elements */
}
Reference only the variables site-wide, never hard-coded colors. For light mode, override the same set inside @media (prefers-color-scheme: light).
Step 2 | Hero background: CSS gradient, not a big image
A large image or video behind the hero is the number one performance-score killer. Layer two radial gradients for a glow instead:
.hero::before {
content: '';
position: absolute;
inset: -6rem -30vw 0;
background:
radial-gradient(600px 320px at 18% 0%, rgba(79,70,229,.28), transparent 70%),
radial-gradient(520px 300px at 88% 24%, rgba(180,83,9,.14), transparent 70%);
pointer-events: none;
z-index: -1;
}
Add overflow-x: clip to body, or you’ll get a horizontal scrollbar.
Step 3 | Glass navbar (one place only)
.nav { background: var(--bg); }
@supports (backdrop-filter: blur(12px)) {
.nav {
background: color-mix(in srgb, var(--bg) 72%, transparent);
backdrop-filter: blur(12px);
}
}
Blur is heavy on phone GPUs, so limit it to one or two spots site-wide. Unsupported browsers fall back to a solid color.
Step 4 | Hover micro-interaction: 2px is enough
.card { transition: transform .2s ease, border-color .2s ease; }
@media (prefers-reduced-motion: no-preference) {
.card:hover { transform: translateY(-2px); }
.button:active { transform: scale(.98); }
}
Step 5 | Scroll reveal: 20 native lines, no AOS or GSAP
<script>
document.documentElement.classList.add('js');
const els = document.querySelectorAll('[data-reveal]');
const io = new IntersectionObserver((entries) => {
for (const e of entries) {
if (e.isIntersecting) { e.target.classList.add('is-visible'); io.unobserve(e.target); }
}
}, { threshold: 0.15 });
els.forEach((el) => io.observe(el));
</script>
@media (prefers-reduced-motion: no-preference) {
html.js [data-reveal] { opacity: 0; transform: translateY(16px); transition: .6s ease; }
html.js [data-reveal].is-visible { opacity: 1; transform: none; }
}
Add a data-reveal attribute to any block that should animate in. Without JS, or when the user turns motion off, content still shows normally (progressive enhancement).
Step 6 | Four-point security hardening (static sites need it too)
- A 404 page: without one, many hosts (such as Cloudflare Pages) return 200 for any made-up URL, and Google concludes you have infinite duplicate pages (soft-404), which hurts SEO. Test with a nonsense URL and confirm you get a 404.
- Security headers: Cloudflare Pages and Netlify both read a
_headersfile at the site root:
/*
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; frame-ancestors 'none'
If your pages embed a third-party iframe, add frame-src https://their-domain to the Content-Security-Policy.
- Third-party embeds load on click: don’t put subscription forms or video iframes in the page by default. Show a button and create the iframe with JS only after the click. Until then the browser makes zero third-party requests, which helps both privacy and first-paint speed.
- A privacy page: if you collect email, one page should say what you collect, who processes it, how to unsubscribe and how to request deletion. 300 words is enough; having none is the problem.
Step 7 | Score it
Run Lighthouse (built into Chrome and Edge DevTools, or npx lighthouse your-url). Target: performance, accessibility and SEO all at 90 or above.
Traps You Will Hit
- A local ad blocker polluting the score: if the score is oddly bad, and FCP, LCP and Speed Index are identical while Total Blocking Time is 0ms, suspect the measuring environment, not the site. Software like AdGuard injects huge scripts into the test browser (here 1.7MB; the site itself loaded in 23ms, yet the first run scored only 56). Add
--blocked-url-patterns="*adguard*"on the command line;--disable-extensionsdoes nothing because the injection happens at the network layer. - CSP blocking your own script: many frameworks (Astro, for one) inline small JS snippets into the HTML, which conflicts with
script-src 'self'. In Astro, setvite: { build: { assetsInlineLimit: 0 } }to force external files. On other frameworks, ask an AI with the prompt below. - A wrong hreflang is the same as none: every page must declare both itself and its other-language twin (self-reference plus reciprocal links). Miss one side and Google ignores the whole set. Point x-default at your main language.
When You’re Stuck, Ask an AI Like This
Copy, paste, replace the [bracketed] parts, and include the full error text verbatim:
I'm a beginner adding [security headers / a click-to-load subscribe box / bilingual hreflang] to a static site.
My setup: [framework and version, e.g. Astro] + [host, e.g. Cloudflare Pages].
What I'm seeing: [paste the error message or unexpected behavior verbatim].
Please explain the cause first, then give me a step-by-step fix, one action per step.
One-Line Summary
Keep out what doesn’t belong and the premium feel and the 100 score follow. When a score looks wrong, check whether the measuring environment is clean before you touch the site.
This is part of an ongoing series of field notes and AI tool tests. To get the full SOPs and future posts, subscribe to the newsletter.
Support this work:Support on Ko-fi