Skip to content

Commit a658770

Browse files
committed
perf(frontend): load FontAwesome CDN/Kit non-blocking; add CDN tests
- FontAwesome CDN CSS now uses async preload+onload pattern instead of blocking <link rel="stylesheet"> - FontAwesome Kit JS now uses `defer` attribute to keep it off the critical path - Update integration tests to assert the new async/defer tag formats
1 parent a47f5cb commit a658770

File tree

2 files changed

+22
-34
lines changed

2 files changed

+22
-34
lines changed

framework/core/src/Frontend/FrontendServiceProvider.php

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,26 +103,20 @@ public function register(): void
103103
if ($fontAwesome->useCdn()) {
104104
$cdnUrl = $fontAwesome->cdnUrl();
105105
if (! empty($cdnUrl)) {
106-
// Preload CDN CSS for better performance
107-
$document->preloads[] = [
108-
'href' => $cdnUrl,
109-
'as' => 'style',
110-
'crossorigin' => 'anonymous'
111-
];
112-
// Add CDN CSS with crossorigin for better caching
113-
$document->head[] = '<link rel="stylesheet" href="'.e($cdnUrl).'" crossorigin="anonymous">';
106+
// Load asynchronously — FA icons are only rendered after JS boots the
107+
// SPA, so there is no FOUC risk from deferring this stylesheet.
108+
// The <noscript> fallback covers JS-disabled browsers.
109+
$escaped = e($cdnUrl);
110+
$document->head[] = '<link rel="preload" href="'.$escaped.'" as="style" crossorigin="anonymous" onload="this.onload=null;this.rel=\'stylesheet\'">'
111+
.'<noscript><link rel="stylesheet" href="'.$escaped.'" crossorigin="anonymous"></noscript>';
114112
}
115113
} elseif ($fontAwesome->useKit()) {
116114
$kitUrl = $fontAwesome->kitUrl();
117115
if (! empty($kitUrl)) {
118-
// Preload Kit JS for better performance
119-
$document->preloads[] = [
120-
'href' => $kitUrl,
121-
'as' => 'script',
122-
'crossorigin' => 'anonymous'
123-
];
124-
// Add Kit JS with crossorigin for better caching
125-
$document->head[] = '<script src="'.e($kitUrl).'" crossorigin="anonymous"></script>';
116+
// Defer Kit JS — it has no dependencies and nothing depends on it
117+
// executing synchronously; defer keeps it out of the critical path
118+
// while preserving execution order relative to other deferred scripts.
119+
$document->head[] = '<script src="'.e($kitUrl).'" crossorigin="anonymous" defer></script>';
126120
}
127121
}
128122

framework/core/tests/integration/frontend/FontAwesomeLoadingTest.php

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ public function fontawesome_cdn_loads_css_instead_of_local_fonts()
6666

6767
$body = $response->getBody()->getContents();
6868

69-
// Should contain CDN CSS with crossorigin attribute
70-
$this->assertStringContainsString('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" crossorigin="anonymous">', $body);
69+
// Should load CDN CSS asynchronously (preload + onload swap)
70+
$this->assertStringContainsString('<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" as="style" crossorigin="anonymous" onload="this.onload=null;this.rel=\'stylesheet\'">', $body);
7171

72-
// Should contain preload for CDN CSS
73-
$this->assertStringContainsString('<link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" as="style" crossorigin="anonymous">', $body);
72+
// Should include noscript fallback
73+
$this->assertStringContainsString('<noscript><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" crossorigin="anonymous"></noscript>', $body);
7474

7575
// Should not contain local font preloads
7676
$this->assertStringNotContainsString('fa-solid-900.woff2', $body);
@@ -89,11 +89,8 @@ public function fontawesome_kit_loads_js_instead_of_local_fonts()
8989

9090
$body = $response->getBody()->getContents();
9191

92-
// Should contain Kit JS with crossorigin attribute
93-
$this->assertStringContainsString('<script src="https://kit.fontawesome.com/abc123xyz.js" crossorigin="anonymous"></script>', $body);
94-
95-
// Should contain preload for Kit JS
96-
$this->assertStringContainsString('<link rel="preload" href="https://kit.fontawesome.com/abc123xyz.js" as="script" crossorigin="anonymous">', $body);
92+
// Should load Kit JS deferred (not blocking)
93+
$this->assertStringContainsString('<script src="https://kit.fontawesome.com/abc123xyz.js" crossorigin="anonymous" defer></script>', $body);
9794

9895
// Should not contain local font preloads
9996
$this->assertStringNotContainsString('fa-solid-900.woff2', $body);
@@ -164,11 +161,11 @@ public function config_override_takes_precedence_over_database_settings()
164161

165162
$body = $response->getBody()->getContents();
166163

167-
// Should use config CDN URL with crossorigin attribute, not local fonts
168-
$this->assertStringContainsString('<link rel="stylesheet" href="https://config.example.com/fontawesome.css" crossorigin="anonymous">', $body);
164+
// Should load config CDN CSS asynchronously (preload + onload swap)
165+
$this->assertStringContainsString('<link rel="preload" href="https://config.example.com/fontawesome.css" as="style" crossorigin="anonymous" onload="this.onload=null;this.rel=\'stylesheet\'">', $body);
169166

170-
// Should contain preload for config CDN CSS
171-
$this->assertStringContainsString('<link rel="preload" href="https://config.example.com/fontawesome.css" as="style" crossorigin="anonymous">', $body);
167+
// Should include noscript fallback
168+
$this->assertStringContainsString('<noscript><link rel="stylesheet" href="https://config.example.com/fontawesome.css" crossorigin="anonymous"></noscript>', $body);
172169

173170
// Should not contain local font preloads
174171
$this->assertStringNotContainsString('fa-solid-900.woff2', $body);
@@ -194,11 +191,8 @@ public function config_kit_override_takes_precedence_over_database_cdn()
194191

195192
$body = $response->getBody()->getContents();
196193

197-
// Should use config Kit URL with crossorigin attribute
198-
$this->assertStringContainsString('<script src="https://kit.fontawesome.com/config123.js" crossorigin="anonymous"></script>', $body);
199-
200-
// Should contain preload for config Kit JS
201-
$this->assertStringContainsString('<link rel="preload" href="https://kit.fontawesome.com/config123.js" as="script" crossorigin="anonymous">', $body);
194+
// Should use config Kit URL with crossorigin attribute, deferred
195+
$this->assertStringContainsString('<script src="https://kit.fontawesome.com/config123.js" crossorigin="anonymous" defer></script>', $body);
202196

203197
// Should not contain database CDN URL
204198
$this->assertStringNotContainsString('database.example.com', $body);

0 commit comments

Comments
 (0)