Skip to content

feat: Astro redesign with industry consolidation - #827

Merged
doomspork merged 4 commits into
mainfrom
doomspork/astro-redesign
Apr 10, 2026
Merged

feat: Astro redesign with industry consolidation#827
doomspork merged 4 commits into
mainfrom
doomspork/astro-redesign

Conversation

@doomspork

@doomspork doomspork commented Apr 8, 2026

Copy link
Copy Markdown
Member

Summary

Complete site redesign migrating from Phoenix/Elixir to an Astro static site with Preact interactive islands.

Industry consolidation — The existing 89 freeform industry values have been consolidated into 17 validated categories based on GICS, with 4 new additions (Professional Services, Media & Entertainment, Government & Non-Profit, Agriculture & Food). Companies now support 1–3 industries each, enforced at build time via Zod enum validation.

  • Schema: industry field changed from z.string() to z.array(z.enum(INDUSTRY_VALUES)).min(1).max(3)
  • Data: All 326 company markdown files migrated with primary + optional secondary industries
  • UI: Company cards show primary industry tag prominently, secondary tags smaller; detail pages show all industry pills; "Browse by Industry" counts companies in all their industries
  • Search/filter: Updated for array-based matching — a company appears when filtering by any of its industries

Complexity Notes

  • The industry migration script (scripts/migrate-industries.mjs) contains the complete 89→17 mapping. This ran with 0 warnings across all 326 files.
  • Companies tagged with multiple industries will appear in counts for each industry on the home page "Browse by Industry" section. This is intentional — a fintech company counts under both Financials and Information Technology.
  • Related companies on detail pages now use industry overlap (any shared industry) rather than strict equality.

Test Steps

  1. Run npm install && npm run build — should complete with 0 errors (validates all 326 company frontmatter against Zod enum schema)
  2. Run npm run dev and check the home page:
    • Hero stats should show 17 industries
    • "Browse by Industry" section should show 17 industry pills with counts
  3. Click "Browse Companies" — filter panel should show 17 industries, clicking one filters correctly
  4. Search for "fintech" — should surface companies tagged with Financials + Information Technology
  5. Click into a company with multiple industries (e.g., Brex) — should show both industry pills
  6. Verify "Related Companies" section shows companies with overlapping industries

Checklist

  • Tests added/updated
  • Documentation updated (if applicable)

Migrate from Phoenix/Elixir to Astro static site with Preact islands.

Industry consolidation:
- Consolidate 89 freeform industry values into 17 validated categories
- Add multi-industry support (1-3 industries per company via Zod enum array)
- Add 4 new categories: Professional Services, Media & Entertainment,
  Government & Non-Profit, Agriculture & Food
- All 326 company markdown files migrated with primary/secondary industries
- Build-time validation ensures no invalid industry values

New Astro site includes:
- Static site generation with content collections
- Preact search/filter island with URL state sync
- Tailwind CSS with dark mode support
- Company cards with industry color coding and icons
- Related companies based on industry overlap
@doomspork
doomspork force-pushed the doomspork/astro-redesign branch from bbc251b to c971898 Compare April 9, 2026 02:07
@burden

burden commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator

Looking forward to this new era! The Elixir Companies website has made a lot of progress, and this newest design looks amazing!!

I noticed that companies with multiple industries show an icon only on the first tag in card/search views, while the company detail page shows icons on all industry tags.

  1. Accidental hierarchy. The icon implies "this one matters more" — which may be true, but it's communicated as a side effect, not a deliberate choice. Secondary tags read as a different element type, not as peer labels.

  2. Card-to-detail mismatch. On the card you see 💻 Information Technology + plain Financials. Open the detail page and Financials now has a 💰. The tag visually changes between contexts, breaking the mental model built while browsing.

  3. No shared contract. Three components implement the same concept three different ways. Any icon logic change requires three edits and risks further drift.


Proposed fix: a single IndustryTag component

One component, always renders {icon} {label}. Use a size prop for visual hierarchy — never a prop that toggles the icon on/off.

---
// src/components/IndustryTag.astro
const { industry, size = 'sm' } = Astro.props;
const icon = getIndustryIcon(industry);
const color = getIndustryColor(industry);
const sizeClass = size === 'sm' ? 'text-[0.65rem]' : 'text-sm';
---
<span class={`industry-tag inline-flex ${color} ${sizeClass}`}>
  <span class="mr-1">{icon}</span>
  {industry}
</span>

Then CompanyCard, SearchFilter, and [slug] all use <IndustryTag industry={ind} /> uniformly. If you want the primary industry to carry more weight, pass size="md" — differentiate through size or color intensity, not icon presence.

The icon is part of the tag's identity (it helps users pattern-match industries while scanning). Stripping it from some instances defeats that purpose.

@burden

burden commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator

The project defines the list of valid industries in two separate places:

  1. src/content/config.ts — INDUSTRY_VALUES, a string array used in the Zod schema to validate frontmatter at build time.
  2. src/utils/industries.ts — INDUSTRIES, an object keyed by industry name, mapping each to a color and emoji icon.

These are maintained by hand independently. If you add a new industry to the schema but forget to add it in utils/industries.ts, nothing breaks loudly — the UI just silently falls back to a gray badge with a generic icon. The reverse (adding to utils but not the schema) means the color/icon exists but no company can ever use that industry.

The fix is to have one canonical list. For example, define everything in utils/industries.ts:

export const INDUSTRIES = {
  "Financials": { color: "blue", icon: "💰" },
  "Health Care": { color: "red", icon: "🏥" },
  // ...
} as const;

export const INDUSTRY_VALUES = Object.keys(INDUSTRIES) as [string, ...string[]];

Then in config.ts, just import it:

import { INDUSTRY_VALUES } from '../utils/industries';
z.array(z.enum(INDUSTRY_VALUES)).min(1).max(3)

One place to add/remove/rename an industry, and both the build validation and the UI styling stay in sync automatically.

- Create shared IndustryTag component (Astro + Preact mirror) that
  always renders icon + label, using size prop for visual hierarchy
- Consolidate INDUSTRY_DATA into a single canonical object in
  utils/industries.ts; derive INDUSTRY_VALUES from its keys
- config.ts now imports INDUSTRY_VALUES from utils/industries.ts
  instead of maintaining a duplicate list
- All three rendering contexts (CompanyCard, SearchFilter, [slug])
  now use the same tag contract: icon always present, size varies

Addresses feedback from @burden on PR #827.
@doomspork

Copy link
Copy Markdown
Member Author

Re: IndustryTag component feedback

Done — created a shared IndustryTag component (src/components/IndustryTag.astro) with a size prop (sm/md) that always renders the icon. CompanyCard.astro and [slug].astro both use it now.

For the Preact island (SearchFilter.tsx), since Astro components cannot be used inside client islands, I created a matching IndustryTag function component with the same contract: always renders {icon} {label}, same size prop, same color/icon lookup.

All three contexts now render tags identically — icon is always present, hierarchy is communicated through size only. Great catch on the visual inconsistency.


Re: Single source of truth for industries

Done — consolidated everything into a single INDUSTRY_DATA object in src/utils/industries.ts that maps each industry name to its { color, icon }. INDUSTRY_VALUES is now derived from Object.keys(INDUSTRY_DATA), and config.ts imports it directly. One place to add/remove/rename an industry, and both Zod validation and UI styling stay in sync automatically.

The Astro build validates everything — Zod schema, TypeScript,
templates — not just company files. Renamed to ci.yml, broadened
triggers to run on all PRs and pushes to main (not just company
file changes), since schema or component changes can also break
the build.
Company descriptions containing markdown links like
[text](url) were rendering raw syntax on cards. Now strips
link syntax and formatting characters before truncation so
cards show clean plain text.
@doomspork
doomspork marked this pull request as ready for review April 10, 2026 23:49
@doomspork
doomspork requested a review from a team as a code owner April 10, 2026 23:49
@doomspork
doomspork merged commit 8028ab0 into main Apr 10, 2026
1 check passed
@doomspork
doomspork deleted the doomspork/astro-redesign branch April 10, 2026 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants