Skip to content

Commit 89682bb

Browse files
dougaitkenclaude
andcommitted
Redesign site with curated homepage and structured company data
- Update color palette to Balanced Blend (Indigo/Teal/Cyan/Periwinkle) - Migrate 853 company profiles to structured frontmatter schema - Add dynamic browse pages for technologies, regions, and remote policies - Create curated homepage with featured companies and browse sections - Add /companies/ page with full searchable directory - Add persistent nav search component - Add Fathom analytics (production only) - Update privacy policy for Fathom compliance Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 28dd9d1 commit 89682bb

File tree

877 files changed

+11388
-5534
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

877 files changed

+11388
-5534
lines changed

.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,6 @@ temp
5454
package-lock.json.bak
5555
yarn.lock.bak
5656

57-
# Migration test directory (remove this after migration is complete)
58-
company-profiles-test
57+
# Local tools and working files
58+
_tools
59+
backup-companies-old

eleventy.config.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@ dotenv.config();
1515
import yaml from 'js-yaml';
1616

1717
// config import
18-
import {getAllPosts, getAllCompanies, showInSitemap, tagList} from './src/_config/collections.js';
18+
import {
19+
getAllPosts,
20+
getAllCompanies,
21+
getFeaturedCompanies,
22+
getRecentCompanies,
23+
getCompaniesByRegion,
24+
getCompaniesByTech,
25+
showInSitemap,
26+
tagList
27+
} from './src/_config/collections.js';
1928
import events from './src/_config/events.js';
2029
import filters from './src/_config/filters.js';
2130
import plugins from './src/_config/plugins.js';
@@ -42,6 +51,10 @@ export default async function (eleventyConfig) {
4251
// --------------------- Collections
4352
eleventyConfig.addCollection('allPosts', getAllPosts);
4453
eleventyConfig.addCollection('companies', getAllCompanies);
54+
eleventyConfig.addCollection('featuredCompanies', getFeaturedCompanies);
55+
eleventyConfig.addCollection('recentCompanies', getRecentCompanies);
56+
eleventyConfig.addCollection('companiesByRegion', getCompaniesByRegion);
57+
eleventyConfig.addCollection('companiesByTech', getCompaniesByTech);
4558
eleventyConfig.addCollection('showInSitemap', showInSitemap);
4659
eleventyConfig.addCollection('tagList', tagList);
4760

src/_config/collections.js

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import {
2+
featuredCompanySlugs,
3+
regionLabels,
4+
techLabels
5+
} from '../_data/companyHelpers.js';
6+
17
/** All blog posts as a collection. */
28
export const getAllPosts = collection => {
39
return collection.getFilteredByGlob('./src/blog/**/*.md').reverse();
@@ -6,23 +12,92 @@ export const getAllPosts = collection => {
612
/** All company profiles as a collection, sorted alphabetically */
713
export const getAllCompanies = collection => {
814
return collection.getFilteredByGlob('./src/companies/**/*.md').sort((a, b) => {
9-
const nameA = a.data.name.toLowerCase();
10-
const nameB = b.data.name.toLowerCase();
15+
const nameA = (a.data.title || '').toLowerCase();
16+
const nameB = (b.data.title || '').toLowerCase();
1117
return nameA.localeCompare(nameB);
1218
});
1319
};
1420

21+
/** Featured companies - manually curated list */
22+
export const getFeaturedCompanies = collection => {
23+
const companies = collection.getFilteredByGlob('./src/companies/**/*.md');
24+
return featuredCompanySlugs
25+
.map(slug => companies.find(c => c.data.slug === slug || c.fileSlug === slug))
26+
.filter(Boolean)
27+
.slice(0, 8);
28+
};
29+
30+
/** Recently added companies (by addedAt date in frontmatter) */
31+
export const getRecentCompanies = collection => {
32+
const companies = collection.getFilteredByGlob('./src/companies/**/*.md');
33+
return [...companies]
34+
.filter(c => c.data.addedAt)
35+
.sort((a, b) => {
36+
const dateA = new Date(a.data.addedAt);
37+
const dateB = new Date(b.data.addedAt);
38+
return dateB - dateA;
39+
})
40+
.slice(0, 12);
41+
};
42+
43+
/** Companies grouped by region */
44+
export const getCompaniesByRegion = collection => {
45+
const companies = collection.getFilteredByGlob('./src/companies/**/*.md');
46+
const regionGroups = {};
47+
48+
// Initialize all regions
49+
Object.keys(regionLabels).forEach(region => {
50+
regionGroups[region] = [];
51+
});
52+
53+
companies.forEach(company => {
54+
const region = company.data.region || 'other';
55+
if (!regionGroups[region]) {
56+
regionGroups[region] = [];
57+
}
58+
regionGroups[region].push(company);
59+
});
60+
61+
return regionGroups;
62+
};
63+
64+
/** Companies grouped by technology */
65+
export const getCompaniesByTech = collection => {
66+
const companies = collection.getFilteredByGlob('./src/companies/**/*.md');
67+
const techGroups = {};
68+
69+
// Initialize all technologies
70+
Object.keys(techLabels).forEach(tech => {
71+
techGroups[tech] = [];
72+
});
73+
74+
companies.forEach(company => {
75+
const technologies = company.data.technologies || [];
76+
technologies.forEach(tech => {
77+
if (!techGroups[tech]) {
78+
techGroups[tech] = [];
79+
}
80+
techGroups[tech].push(company);
81+
});
82+
});
83+
84+
return techGroups;
85+
};
86+
1587
/** All relevant pages as a collection for sitemap.xml */
1688
export const showInSitemap = collection => {
1789
return collection.getFilteredByGlob('./src/**/*.{md,njk}');
1890
};
1991

20-
/** All tags from all posts as a collection - excluding custom collections */
92+
/** All tags from blog posts as a collection - excluding custom collections */
2193
export const tagList = collection => {
2294
const tagsSet = new Set();
23-
collection.getAll().forEach(item => {
24-
if (!item.data.tags) return;
25-
item.data.tags.filter(tag => !['posts', 'docs', 'all'].includes(tag)).forEach(tag => tagsSet.add(tag));
95+
// Only get tags from blog posts, not from companies or other content
96+
collection.getFilteredByGlob('./src/blog/**/*.md').forEach(item => {
97+
if (!item.data.tags || !Array.isArray(item.data.tags)) return;
98+
item.data.tags
99+
.filter(tag => typeof tag === 'string' && !['posts', 'docs', 'all'].includes(tag))
100+
.forEach(tag => tagsSet.add(tag));
26101
});
27102
return Array.from(tagsSet).sort();
28103
};

src/_data/companyHelpers.js

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Helper functions for company data
3+
* Now uses structured frontmatter fields directly
4+
*/
5+
6+
/**
7+
* Region enum to display name mapping
8+
*/
9+
export const regionLabels = {
10+
'worldwide': 'Worldwide',
11+
'americas': 'Americas',
12+
'europe': 'Europe',
13+
'americas-europe': 'Americas & Europe',
14+
'asia-pacific': 'Asia Pacific',
15+
'other': 'Other'
16+
};
17+
18+
/**
19+
* Remote policy enum to display name mapping
20+
*/
21+
export const remotePolicyLabels = {
22+
'fully-remote': 'Fully Remote',
23+
'remote-first': 'Remote First',
24+
'hybrid': 'Hybrid',
25+
'remote-friendly': 'Remote Friendly'
26+
};
27+
28+
/**
29+
* Company size enum to display name mapping
30+
*/
31+
export const companySizeLabels = {
32+
'tiny': '1-10 employees',
33+
'small': '11-50 employees',
34+
'medium': '51-200 employees',
35+
'large': '201-1000 employees',
36+
'enterprise': '1000+ employees'
37+
};
38+
39+
/**
40+
* Technology tag to display name mapping
41+
*/
42+
export const techLabels = {
43+
'javascript': 'JavaScript',
44+
'python': 'Python',
45+
'ruby': 'Ruby',
46+
'go': 'Go',
47+
'java': 'Java',
48+
'php': 'PHP',
49+
'rust': 'Rust',
50+
'dotnet': '.NET',
51+
'elixir': 'Elixir',
52+
'scala': 'Scala',
53+
'cloud': 'Cloud',
54+
'devops': 'DevOps',
55+
'mobile': 'Mobile',
56+
'data': 'Data',
57+
'ml': 'ML/AI',
58+
'sql': 'SQL',
59+
'nosql': 'NoSQL',
60+
'search': 'Search'
61+
};
62+
63+
/**
64+
* Get display label for region
65+
*/
66+
export function getRegionLabel(region) {
67+
return regionLabels[region] || region || 'Other';
68+
}
69+
70+
/**
71+
* Get display label for remote policy
72+
*/
73+
export function getRemotePolicyLabel(policy) {
74+
return remotePolicyLabels[policy] || policy || 'Unknown';
75+
}
76+
77+
/**
78+
* Get display label for company size
79+
*/
80+
export function getCompanySizeLabel(size) {
81+
return companySizeLabels[size] || size || 'Unknown';
82+
}
83+
84+
/**
85+
* Get display label for technology
86+
*/
87+
export function getTechLabel(tech) {
88+
return techLabels[tech] || tech;
89+
}
90+
91+
/**
92+
* Featured companies list - manually curated high-quality examples
93+
*/
94+
export const featuredCompanySlugs = [
95+
'github',
96+
'gitlab',
97+
'automattic',
98+
'zapier',
99+
'buffer',
100+
'doist',
101+
'elastic',
102+
'hashicorp',
103+
'stripe',
104+
'shopify',
105+
'netlify',
106+
'vercel'
107+
];

0 commit comments

Comments
 (0)