Skip to content

Repository files navigation

A lone microphone in a spotlight on an otherwise empty stage

Lily Livered: A Maximalist, One-Page, Logo-Only Astro Template

Lighthouse Performance Lighthouse Accessibility Lighthouse Best Practices Lighthouse SEO

Mozilla Observatory

CI

GitHub last commit GitHub repo size GitHub stars

Built with Astro Deploy: Cloudflare Pages Checked with Biome Renovate enabled License: Apache-2.0

↗ Live Demo · Your email-only domains are naked, and you know it. Here's a pair of pants.

You bought a domain. Maybe you're not ready to build the site yet. Maybe it's just an email domain and always will be. Either way, you are ready to stop staring at a parking page that says "Future home of something quite cool" like it's 2003.

Lily Livered is a one-page logo site for domains that deserve better than nothing but aren't getting a full website today. Drop in your SVG, edit one config file, push to Cloudflare Pages. Done before your coffee gets cold.

What this repo demonstrates: a one-page site built like a product with a roadmap. Tagged semver releases (v1.0.0 through v2.0.4) with a written migration guide, Renovate keeping dependencies current, Biome as a single lint-and-format pass, and a Lighthouse workflow that runs three times per push and takes the median so the badge number holds still. Security headers and a Mozilla Observatory grade sit on the front page because the tradeoffs behind them are written down, not buried. Small surface, real shipping discipline.

What you get:

  • Lighthouse 100 on Accessibility, Best Practices, SEO, and yes, Performance, even with the demo's absurd logo (a genuinely enormous one could still nudge it down).
  • A fun resizable logo that looks good on everything from a phone to a projector. Playing with the three size settings counts as an interactive feature.
  • Inline-rendered SVG logo so it's not casually right-click-downloadable. See About the logo and "protection" for what it does and doesn't do.
  • Three tiers of site metrics and visitor tracking to keep tabs on that baseline traffic flow
  • robots.txt and llms.txt so you can tell the cool kids what's going on
  • A 404 page, because even a single-page site needs to handle disappointment gracefully

About that Performance score: The demo site ships with a deliberately absurd 72 KB SVG logo: a 16th-century woodcut skull with 30,000+ path coordinates. It's a stress test, not a template recommendation, and it still scores 100. The logo is inlined as a data: URI background-image, which is exactly what Lighthouse clocks as the Largest Contentful Paint. A genuinely enormous logo could still cost you a point or two; the badge workflow runs Lighthouse three times per push and takes the median to keep the number steady.

Quick Start

git clone https://github.com/vinsonconsulting/lily-livered.git my-site
cd my-site
npm ci

Then do two things:

  1. Drop your logo into src/assets/logo.svg (it gets inlined into the page at build time, so there's no /logo.svg URL on the deployed site).
  2. Edit src/config.js with your details (see below). robots.txt and the sitemap are generated from your siteUrl at build time. Also replace public/llms.txt with your own content, and replace public/favicon.svg with your own brand mark (or delete it and set hasFavicon: false).

Preview it:

npm run dev    # → localhost:4321

Deploy it:

git add . && git commit -m "My domain has pants now" && git push

The One File You Actually Edit

src/config.js is where everything lives:

export const config = {
  // ◈ Site Info
  siteName: 'Acme Corporation',
  siteDescription: 'Fine Products for Coyotes Since 1949',
  siteUrl: 'https://acme.example.com',

  // ◈ Analytics. Paste your IDs, or leave '' to disable.
  googleAnalyticsId: '', // e.g., 'G-XXXXXXXXXX'
  clarityProjectId: '', // e.g., 'abc123xyz'
  cloudflareAnalyticsToken: '', // e.g., 'abcd1234...'

  // ◈ Colors
  backgroundColor: '#000000',
  textColor: '#ffffff',
  lightBackgroundColor: '#ffffff',  // used when colorScheme is 'auto'
  lightTextColor: '#000000',        // used when colorScheme is 'auto'
  colorScheme: 'dark', // 'dark' | 'light' | 'auto'

  // ◈ Logo size. How much screen to fill.
  logoSize: 'normal', // 'normal' | 'large' | 'massive'

  // ◈ Fade-in animation
  fadeIn: true,
  fadeInDuration: 1.2, // seconds

  // ◈ Social sharing image (set true if you've added public/og-image.png)
  hasOgImage: false,

  // ◈ Favicon (set true if you've added public/favicon.svg)
  hasFavicon: false,

  // ◈ Structured data (JSON-LD for search engines)
  enableStructuredData: true,
  organizationType: 'Organization', // 'Organization', 'LocalBusiness', 'Person'
};

Logo Sizes

Setting What it does When to use it
'normal' Comfortable fit, breathing room Most logos
'large' Fills most of the screen Bold wordmarks, wide logos
'massive' Edge-to-edge domination You want the logo and nothing else

Color Scheme

Setting What it does When to use it
'dark' Always dark background Default. Most logos look best on dark.
'light' Always light background Light-colored logos, bright brands
'auto' Follows system preference You want to play nice with OS dark mode

When set to 'auto', the site uses backgroundColor/textColor for dark mode and lightBackgroundColor/lightTextColor for light mode. Make sure your logo looks good on both backgrounds, or use an SVG with its own prefers-color-scheme media query.

Your Logo (SVG)

Replace src/assets/logo.svg with yours. The logo lives under src/ (not public/) so Astro can inline it into the HTML at build time. No /logo.svg URL is served to visitors. See About the logo and "protection" for the reasoning.

A few ground rules:

  • Convert text to outlines. No font dependency headaches.
  • Center it in the viewBox. If it looks off-center on the page, it's off-center in the file.
  • White or light colors. Dark background is the default.
  • Run it through SVGO. Smaller file, faster page (and smaller inlined HTML).

If your logo looks weirdly positioned: open it in Illustrator/Figma/Inkscape, select all, center on artboard, re-export.

About the logo and "protection"

The hero logo is inlined directly into the page HTML as a data: URI behind a CSS background-image, imported with Astro's ?raw so the SVG is never emitted as a separate asset. It's not served as a file. (A CSS background-image is also a Largest-Contentful-Paint candidate, so Lighthouse can still score Performance; an inline <svg> of <path>s can't be, which is why this isn't a bare <svg>.) That intentionally breaks the easy download paths:

  • Right-click → "Save Image As..." doesn't appear (no <img> element, plus the context menu is suppressed)
  • Drag-to-desktop doesn't produce a draggable image (draggable="false" + user-drag: none)
  • https://yoursite.com/logo.svg returns 404. The URL simply doesn't exist.

This is friction, not protection. A determined user can View Source, copy the inlined SVG data URI out of the page, or screenshot the rendered page. There's no way to stop that, and we don't pretend otherwise. The goal here is to deter casual saves (the average visitor won't bother), not to make the logo unreachable.

If you actively want the logo to be downloadable (e.g., for press/brand-asset purposes), the simplest path is to also drop a copy at public/logo.svg so it's served alongside the inlined version. You'll lose the friction but gain the download.

robots.txt

robots.txt is generated at build time by src/pages/robots.txt.js using siteUrl from config.js, so there's nothing to edit. Set siteUrl correctly and the file just works. Output looks like:

User-agent: *
Allow: /
Sitemap: https://yourdomain.com/sitemap-index.xml

The sitemap itself is auto-generated by @astrojs/sitemap, also from siteUrl. One config field, two correct files.

llms.txt

Edit public/llms.txt so AI systems know who you are. This follows the llmstxt.org spec. The template ships with a placeholder. Replace it with your actual company info, services, and contact details.

Analytics (Pick Your Poison)

Three options. All optional. All run off the main thread via Partytown so they don't slow anything down.

Service What it tells you Get it at
Google Analytics 4 Everything. Too much, probably. analytics.google.com
Microsoft Clarity Session recordings, heatmaps, rage clicks clarity.microsoft.com
Cloudflare Web Analytics Privacy-friendly basics, ~1KB CF Dashboard ↗ Web Analytics

Paste the ID into config.js. Leave blank to disable. They stack. Use one, two, or all three.

About Partytown and your security score: Partytown is only loaded when at least one analytics ID is configured. If all three are blank, it's excluded entirely. This matters because Partytown injects an inline bootstrap script, which forces 'unsafe-inline' into your Content Security Policy's script-src directive. Mozilla Observatory penalizes that by 20 points, enough to drop your grade from A+ to B+.

Out of the box (no analytics), the template scores A+ on Observatory and keeps a tight CSP. When you add an analytics ID, you'll need to add 'unsafe-inline' back to script-src in public/_headers:

script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.clarity.ms;

That's a deliberate tradeoff: Observatory drops to B+, but you get off-thread analytics with zero main-thread cost. Partytown's inline script is the bootstrap that makes that possible. There's no way around it until Astro's experimental hash-based CSP graduates and can generate per-build nonces.

Optional Extras

Favicon. The template ships with a placeholder public/favicon.svg and hasFavicon: true in config.js. Replace the file with your own brand mark, or delete the file and set hasFavicon: false to ship without one. The <link rel="icon"> tag is only emitted when hasFavicon: true, so there's no broken icon request either way. SVG favicons scale perfectly and support dark mode via CSS prefers-color-scheme media queries embedded in the SVG itself. In v1 the favicon fell back to /logo.svg when no favicon was provided; v2 removed that fallback because the logo is now inlined and has no public URL.

Social sharing image. Create a 1200×630 public/og-image.png (your logo on your background color), then set hasOgImage: true in config.js. This is what shows up when someone shares your link. The image meta tags are only emitted when you opt in, so no broken image URLs ship out of the box.

Deploying to Cloudflare Pages

  1. Push this repo to GitHub
  2. Cloudflare Dashboard ↗ Pages ↗ Create project
  3. Connect your repo
  4. Build settings:
    • Build command: npm run build
    • Output directory: dist
    • Framework preset: Astro
  5. Add your custom domain

Every git push after that auto-deploys.

Custom Domain Setup

After your Pages project is live on its *.pages.dev URL, point your domain to it:

  1. In Cloudflare Dashboard: Pages ↗ your project ↗ Custom domains ↗ Set up a custom domain
  2. Enter your domain (e.g., brand.example.com or example.com)
  3. Cloudflare handles the DNS. If your domain is already on Cloudflare DNS (and it should be), the CNAME record is added automatically.
  4. SSL is automatic. Give it a few minutes for the certificate to provision.

If your domain uses a different DNS provider: You'll need to add a CNAME record manually:

Type Name Target
CNAME @ or subdomain your-project.pages.dev

Some DNS providers don't support CNAME at the apex (@). In that case, either use a subdomain or transfer your DNS to Cloudflare (free plan works fine).

Email-only domains: If you're using this template specifically for a domain that only handles email, make sure your MX records are set up before you add the domain to Pages. Cloudflare Pages won't interfere with existing MX records, but it's good hygiene to confirm email still works after the DNS change.

File Map

src/
├── config.js            ← Edit this. All your settings.
├── assets/
│   └── logo.svg         ← Replace this with your logo. Inlined into HTML at build time.
├── lib/
│   └── colors.js        ← Shared color-scheme helper. Leave alone.
└── pages/
    ├── index.astro      ← The page. You probably don't need to touch this.
    ├── 404.astro        ← 404 page. Also probably leave it alone.
    └── robots.txt.js    ← Generates robots.txt from config.siteUrl.
public/
├── favicon.svg          ← Replace with your brand mark (or delete + set hasFavicon: false).
├── llms.txt             ← Edit with your company info.
├── _headers             ← Security headers for Cloudflare Pages.
└── og-image.png         ← Optional. For social media previews.
biome.json               ← Lint/format config.
renovate.json            ← Automated dependency updates.

Commands

npm run dev          # Dev server at localhost:4321
npm run build        # Production build → dist/
npm run preview      # Preview the production build locally
npm run check        # Lint + format check via Biome
npm run check:fix    # Auto-fix lint and format issues
npm run check:astro  # Astro diagnostics (.astro template errors)
npm run format       # Format check only (no lint)
npm run format:fix   # Apply Biome formatting

Upgrading from v1 to v2

If you forked this template before v2.0.0, a few things changed:

  • Logo moves: public/logo.svgsrc/assets/logo.svg. The build will fail with an Astro import error until you move it.
  • Node 22.12+ required: v2 follows Astro 6, which dropped Node 18 and 20. Bump your Cloudflare Pages NODE_VERSION env var to 22 if it isn't already.
  • Favicon doesn't fall back to the logo: in v1, hasFavicon: false made the favicon point at /logo.svg. In v2 there's no /logo.svg URL, so the favicon <link> is only emitted when hasFavicon: true. The template ships with a placeholder public/favicon.svg and the flag set to true. Replace the file or flip the flag.
  • public/robots.txt was removed (earlier in v1.0.x → v2 sequence): it's now generated dynamically from config.siteUrl via src/pages/robots.txt.js. If you customized the old static file, port your customizations into the endpoint.

What's Under the Hood

  • Astro 6. Static site generator, zero client JS by default.
  • Partytown. Runs analytics scripts off the main thread (loaded only when analytics are configured).
  • Cloudflare Pages. Edge deployment, free tier is generous.
  • Biome. Linting and formatting in one Rust binary.
  • Renovate. Automated dependency updates so your clone doesn't rot.
  • Semantic HTML, prefers-reduced-motion support, high-contrast defaults
  • Auto-generated sitemap via @astrojs/sitemap
  • Open Graph, Twitter Cards, JSON-LD structured data, all auto-generated from your config
  • Security headers: HSTS, CSP, CORP, COOP, X-Frame-Options, Referrer-Policy, Permissions-Policy
  • Lighthouse 100 on Accessibility, Best Practices, and SEO. Performance scales with your logo.

FAQ

"Isn't this over-engineered for a one-page site?"

Yes, gloriously so. That's the point. Your domain gets the same edge deployment, security headers, structured data, and analytics pipeline as a site with a hundred pages, except you configured it in sixty seconds. The over-engineering is a feature, not a bug. You're welcome.

"Why does the demo logo look like it was drawn by a 16th-century monk?"

Because it was (see credits below). We wanted to prove the template handles complex SVGs gracefully. Your clean, modern logo will load faster and score higher on Performance.

"A 72 KB SVG? For a template about simplicity?"

The template is simple. The demo logo is a stress test: a 16th-century woodcut with 30,000+ coordinates, SVGO-optimized from 236 KB down to 72 KB (32 KB on the wire with compression). If it can handle this and still score in the high 80s on Performance, your logo doesn't stand a chance of slowing it down.

"Does it support dark mode / light mode?"

Yes. Set colorScheme: 'auto' in config.js and provide colors for both modes. The default is 'dark' because most logo-only sites look better on a dark background, and changing the default would break existing deployments. If your logo works on both dark and light backgrounds, 'auto' is the polite choice. It respects the visitor's system preference.

"Why Cloudflare Pages specifically?"

Free tier is generous (unlimited bandwidth, 500 builds/month), deploys are fast, and the edge network is global. You could deploy this to Vercel or Netlify with minimal changes, but Cloudflare Pages is the default because it's the best deal for a site that costs nothing to run.

"The 404 page used to load a Google Font. What happened?"

We replaced it with a system font stack. The original used Bebas Neue from Google Fonts, which meant an external network request on an error page (the one page that should load instantly and independently). The current stack (Impact / Arial Black / system sans-serif with text-transform: uppercase) hits the same bold display aesthetic without any external dependency.

"Why three analytics services?"

Because they do different things and they're all free. GA4 is the kitchen sink. Clarity gives you session recordings and heatmaps. Cloudflare Web Analytics is privacy-friendly and weighs 1 KB. Pick one, pick all three, pick none. They're off the main thread via Partytown so they don't affect performance.

Demo Logo Credit

The demo site logo is adapted from a woodcut on page 181 of Claude Paradin's Devises heroïques (Lyon: Jean de Tournes & Guillaume Gazeau, 1557). The image is in the public domain.

License

Apache-2.0. Use it, fork it, ship it. See LICENSE and NOTICE.

About

Your email-only domains are naked, and you know it. Put this up instead. Astro + Cloudflare Pages.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages