mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-12 06:06:37 +03:00
feat(detector): flag Fraunces, Geist, and the new monoculture fonts
Adds Fraunces, Geist (Sans/Mono), Mona Sans, Plus Jakarta Sans, Space Grotesk, Recoleta, and Instrument Sans to OVERUSED_FONTS. Brand-domain exceptions for Geist on vercel.com / nextjs.org / v0.app and Mona Sans on github.com / githubnext.com. Updates four should-pass fixtures that used the now-flagged faces as "good examples"; switches to Newsreader + Karla. Adds positive assertions for Fraunces and Geist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
856b90e52b
commit
b5c203f38a
@@ -61,7 +61,13 @@ const BORDER_SAFE_TAGS = new Set(
|
||||
);
|
||||
|
||||
const OVERUSED_FONTS = new Set([
|
||||
// Older monoculture (still ubiquitous):
|
||||
'inter', 'roboto', 'open sans', 'lato', 'montserrat', 'arial', 'helvetica',
|
||||
// Newer monoculture (the Anthropic-skill / Vercel / GitHub default wave):
|
||||
'fraunces', 'instrument sans',
|
||||
'geist', 'geist sans', 'geist mono',
|
||||
'mona sans',
|
||||
'plus jakarta sans', 'space grotesk', 'recoleta',
|
||||
]);
|
||||
|
||||
// Brand-associated fonts: don't flag these as "overused" on the brand's own domains.
|
||||
@@ -70,10 +76,16 @@ const GOOGLE_DOMAINS = [
|
||||
'google.com', 'youtube.com', 'android.com', 'chromium.org',
|
||||
'chrome.com', 'web.dev', 'gstatic.com', 'firebase.google.com',
|
||||
];
|
||||
const VERCEL_DOMAINS = ['vercel.com', 'nextjs.org', 'v0.app'];
|
||||
const GITHUB_DOMAINS = ['github.com', 'githubnext.com'];
|
||||
const BRAND_FONT_DOMAINS = {
|
||||
'roboto': GOOGLE_DOMAINS,
|
||||
'google sans': GOOGLE_DOMAINS,
|
||||
'product sans': GOOGLE_DOMAINS,
|
||||
'geist': VERCEL_DOMAINS,
|
||||
'geist sans': VERCEL_DOMAINS,
|
||||
'geist mono': VERCEL_DOMAINS,
|
||||
'mona sans': GITHUB_DOMAINS,
|
||||
};
|
||||
|
||||
function isBrandFontOnOwnDomain(font) {
|
||||
@@ -116,7 +128,7 @@ const ANTIPATTERNS = [
|
||||
category: 'slop',
|
||||
name: 'Overused font',
|
||||
description:
|
||||
'Inter, Roboto, Open Sans, Lato, Montserrat, and Arial are used on millions of sites. Choose a distinctive font that gives your interface personality.',
|
||||
'Inter, Roboto, Fraunces, Geist, Plus Jakarta Sans, and Space Grotesk are used on so many sites they no longer feel distinctive. Each new wave of AI-generated UIs converges on the same handful of faces. Choose a face that gives your interface personality.',
|
||||
skillSection: 'Typography',
|
||||
skillGuideline: 'overused fonts like Inter',
|
||||
},
|
||||
|
||||
@@ -57,7 +57,13 @@ const BORDER_SAFE_TAGS = new Set(
|
||||
);
|
||||
|
||||
const OVERUSED_FONTS = new Set([
|
||||
// Older monoculture (still ubiquitous):
|
||||
'inter', 'roboto', 'open sans', 'lato', 'montserrat', 'arial', 'helvetica',
|
||||
// Newer monoculture (the Anthropic-skill / Vercel / GitHub default wave):
|
||||
'fraunces', 'instrument sans',
|
||||
'geist', 'geist sans', 'geist mono',
|
||||
'mona sans',
|
||||
'plus jakarta sans', 'space grotesk', 'recoleta',
|
||||
]);
|
||||
|
||||
// Brand-associated fonts: don't flag these as "overused" on the brand's own domains.
|
||||
@@ -66,10 +72,16 @@ const GOOGLE_DOMAINS = [
|
||||
'google.com', 'youtube.com', 'android.com', 'chromium.org',
|
||||
'chrome.com', 'web.dev', 'gstatic.com', 'firebase.google.com',
|
||||
];
|
||||
const VERCEL_DOMAINS = ['vercel.com', 'nextjs.org', 'v0.app'];
|
||||
const GITHUB_DOMAINS = ['github.com', 'githubnext.com'];
|
||||
const BRAND_FONT_DOMAINS = {
|
||||
'roboto': GOOGLE_DOMAINS,
|
||||
'google sans': GOOGLE_DOMAINS,
|
||||
'product sans': GOOGLE_DOMAINS,
|
||||
'geist': VERCEL_DOMAINS,
|
||||
'geist sans': VERCEL_DOMAINS,
|
||||
'geist mono': VERCEL_DOMAINS,
|
||||
'mona sans': GITHUB_DOMAINS,
|
||||
};
|
||||
|
||||
function isBrandFontOnOwnDomain(font) {
|
||||
@@ -112,7 +124,7 @@ const ANTIPATTERNS = [
|
||||
category: 'slop',
|
||||
name: 'Overused font',
|
||||
description:
|
||||
'Inter, Roboto, Open Sans, Lato, Montserrat, and Arial are used on millions of sites. Choose a distinctive font that gives your interface personality.',
|
||||
'Inter, Roboto, Fraunces, Geist, Plus Jakarta Sans, and Space Grotesk are used on so many sites they no longer feel distinctive. Each new wave of AI-generated UIs converges on the same handful of faces. Choose a face that gives your interface personality.',
|
||||
skillSection: 'Typography',
|
||||
skillGuideline: 'overused fonts like Inter',
|
||||
},
|
||||
@@ -2870,10 +2882,10 @@ const REGEX_MATCHERS = [
|
||||
test: (m, line) => +m[1] >= 3 && hasBorderRadius(line),
|
||||
fmt: (m) => m[0] },
|
||||
// --- Overused font ---
|
||||
{ id: 'overused-font', regex: /font-family\s*:\s*['"]?(Inter|Roboto|Open Sans|Lato|Montserrat|Arial|Helvetica)\b/gi,
|
||||
{ id: 'overused-font', regex: /font-family\s*:\s*['"]?(Inter|Roboto|Open Sans|Lato|Montserrat|Arial|Helvetica|Fraunces|Geist Sans|Geist Mono|Geist|Mona Sans|Plus Jakarta Sans|Space Grotesk|Recoleta|Instrument Sans)\b/gi,
|
||||
test: () => true,
|
||||
fmt: (m) => m[0] },
|
||||
{ id: 'overused-font', regex: /fonts\.googleapis\.com\/css2?\?family=(Inter|Roboto|Open\+Sans|Lato|Montserrat)\b/gi,
|
||||
{ id: 'overused-font', regex: /fonts\.googleapis\.com\/css2?\?family=(Inter|Roboto|Open\+Sans|Lato|Montserrat|Fraunces|Plus\+Jakarta\+Sans|Space\+Grotesk|Instrument\+Sans|Mona\+Sans|Geist)\b/gi,
|
||||
test: () => true,
|
||||
fmt: (m) => `Google Fonts: ${m[1].replace(/\+/g, ' ')}` },
|
||||
// --- Pure black background ---
|
||||
|
||||
@@ -140,8 +140,18 @@ describe('detectText — overused fonts', () => {
|
||||
expect(f.some(r => r.antipattern === 'overused-font')).toBe(true);
|
||||
});
|
||||
|
||||
test('detects Fraunces (current AI-default monoculture)', () => {
|
||||
const f = detectText("h1 { font-family: 'Fraunces', Georgia, serif; }", 'test.css');
|
||||
expect(f.some(r => r.antipattern === 'overused-font')).toBe(true);
|
||||
});
|
||||
|
||||
test('detects Geist (Vercel-default monoculture)', () => {
|
||||
const f = detectText("body { font-family: 'Geist', sans-serif; }", 'test.css');
|
||||
expect(f.some(r => r.antipattern === 'overused-font')).toBe(true);
|
||||
});
|
||||
|
||||
test('does not flag distinctive fonts', () => {
|
||||
const f = detectText("body { font-family: 'Instrument Sans', sans-serif; }", 'test.css');
|
||||
const f = detectText("body { font-family: 'Karla', sans-serif; }", 'test.css');
|
||||
expect(f.filter(r => r.antipattern === 'overused-font')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ export const Card = styled.div`
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
font-family: 'Geist', system-ui, sans-serif;
|
||||
font-family: 'Karla', system-ui, sans-serif;
|
||||
`;
|
||||
|
||||
export const Hero = styled.section`
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
|
||||
<style>
|
||||
.sidebar {
|
||||
font-family: 'Geist', system-ui, sans-serif;
|
||||
font-family: 'Karla', system-ui, sans-serif;
|
||||
background: #fafafa;
|
||||
}
|
||||
</style>
|
||||
|
||||
+5
-5
@@ -4,23 +4,23 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Typography — Clean Patterns</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400;500;600&family=Fraunces:opsz,wght@9..144,400;9..144,700&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Newsreader:opsz,wght@6..72,400;6..72,700&family=Karla:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Instrument Sans', system-ui, sans-serif;
|
||||
font-family: 'Karla', system-ui, sans-serif;
|
||||
background: #f9fafb;
|
||||
padding: 2rem;
|
||||
font-size: 16px;
|
||||
color: #111827;
|
||||
}
|
||||
h1 {
|
||||
font-family: 'Fraunces', Georgia, serif;
|
||||
font-family: 'Newsreader', Georgia, serif;
|
||||
font-size: 48px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
h2 {
|
||||
font-family: 'Fraunces', Georgia, serif;
|
||||
font-family: 'Newsreader', Georgia, serif;
|
||||
font-size: 32px;
|
||||
font-weight: 700;
|
||||
margin: 2rem 0 0.75rem;
|
||||
@@ -35,7 +35,7 @@
|
||||
<p>This page uses distinctive fonts, proper pairing, and strong hierarchy.</p>
|
||||
|
||||
<h2>Two Font Families</h2>
|
||||
<p>Fraunces (serif) for headings, Instrument Sans for body. Clear contrast in both structure and personality.</p>
|
||||
<p>Newsreader (serif) for headings, Karla for body. Clear contrast in both structure and personality, and neither face has been adopted into the current AI-default monoculture.</p>
|
||||
|
||||
<h3>Strong Size Hierarchy</h3>
|
||||
<p>Sizes range from 12px to 48px — a 4:1 ratio with clear visual steps.</p>
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ defineProps({
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
font-family: 'Geist', system-ui, sans-serif;
|
||||
font-family: 'Karla', system-ui, sans-serif;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user