perf: use digested font paths in CSS and preloads

Add path_resolver parameter to font generation functions so both
font preloads and CSS @font-face declarations use the same digested
paths in production. This prevents duplicate font downloads when
preloads were using digested paths but CSS used non-digested paths.

- Add path_resolver parameter to Fonts.generate_font_faces/2,
  Fonts.preload_links/2, and Fonts.generate_all_font_faces/1
- Update CSSGenerator.generate/2 to accept path_resolver
- Update CSSCache.warm/0 to use Endpoint.static_path/1
- Move CSSCache to start after Endpoint in supervision tree
- Add 1-year cache headers for static assets

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-25 09:32:06 +00:00
parent 03fb98afc4
commit 9783199691
6 changed files with 81 additions and 42 deletions

View File

@@ -7,8 +7,11 @@
<meta name="description" content={assigns[:page_description] || @theme_settings.site_description || "Welcome to #{@theme_settings.site_name}"} />
<.live_title><%= assigns[:page_title] || @theme_settings.site_name %></.live_title>
<!-- Preload critical fonts for the current typography preset -->
<%= for preload <- SimpleshopTheme.Theme.Fonts.preload_links(@theme_settings.typography) do %>
<link rel="preload" href={preload.href} as={preload.as} type={preload.type} crossorigin />
<%= for preload <- SimpleshopTheme.Theme.Fonts.preload_links(
@theme_settings.typography,
&SimpleshopThemeWeb.Endpoint.static_path/1
) do %>
<link rel="preload" href={preload.href} as="font" type="font/woff2" crossorigin />
<% end %>
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
<script defer phx-track-static src={~p"/assets/js/app.js"}>

View File

@@ -20,11 +20,16 @@ defmodule SimpleshopThemeWeb.Endpoint do
# When code reloading is disabled (e.g., in production),
# the `gzip` option is enabled to serve compressed
# static files generated by running `phx.digest`.
#
# Cache headers: 1 year for all static assets. Digested assets (with hash
# in filename) use `immutable`. Fonts/mockups/images rarely change and
# benefit from aggressive caching.
plug Plug.Static,
at: "/",
from: :simpleshop_theme,
gzip: not code_reloading?,
only: SimpleshopThemeWeb.static_paths()
only: SimpleshopThemeWeb.static_paths(),
cache_control_for_etags: "public, max-age=31536000, immutable"
if Code.ensure_loaded?(Tidewave) do
plug Tidewave