Skip to content

Commit 7e5501c

Browse files
authored
feat(home): homepage positioning — hero, CTAs, featured work, now (#546)
* feat(home): sharpen hero to lead with Rails value Rewrite the hero's supporting copy so it foregrounds the specific, differentiated value — fast CI and test suites, developer tooling, and team-velocity workflow work — while keeping the personal, understated voice and the existing primary CTA above the fold. Closes #517 * feat(home): add clear hero call-to-action buttons Replace the single text-link CTA with a button group: a primary "Read the writing" action above the fold plus two secondary CTAs ("Browse projects", "Listen to Remote Ruby") that route visitors to the most important destinations. CTA copy stays goal-aligned without signalling job-seeking. Closes #518 * feat(home): add featured work proof section Surface the strongest proof points above the post list: a data-driven "featured work" grid (Podia, Remote Ruby, and flagship open source) with a concise explanation and link per card, so visitors grasp the technical depth within seconds instead of digging. Items live in src/_data/featured.yml and render through a new FeaturedCard component. Closes #519 * feat(home): add a current-activity "now" section Add a compact "now" strip directly below the hero that surfaces the freshest real-world signals — current role, latest Remote Ruby episode (with date), and most recent talk — pulled from existing site data so it stays current automatically. Reorder the homepage so "now" and featured work lead, keeping old post dates from dominating the first screen. Closes #520
1 parent 13160c4 commit 7e5501c

7 files changed

Lines changed: 185 additions & 10 deletions

File tree

src/_components/featured_card.erb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<%= render Link.new(
2+
href: field(:href),
3+
external: true,
4+
class: "group flex flex-col gap-1 bg-sage-1 hover:bg-sage-2 transition-colors py-3.5 md:p-5"
5+
) do %>
6+
<div class="flex items-center gap-2.5 mb-1">
7+
<span
8+
class="
9+
inline-flex items-center gap-1.5 font-semibold text-[15px] tracking-tight text-sage-12
10+
"
11+
>
12+
<%= field(:title) %>
13+
<%= render Icon.new(
14+
name: "arrow_top_right",
15+
class: "w-3 h-3 text-sage-8 group-hover:text-mint-11 transition-colors",
16+
) %>
17+
</span>
18+
<% if field(:kind) %>
19+
<span
20+
class="
21+
ml-auto font-mono text-[10px] uppercase tracking-[0.06em] rounded-full border
22+
px-1.5 py-px text-sage-10 border-sage-4
23+
"
24+
>
25+
<%= field(:kind) %>
26+
</span>
27+
<% end %>
28+
</div>
29+
<div class="text-[13.5px] leading-snug text-sage-11 my-1 mb-4 text-pretty"><%= field(:desc) %></div>
30+
<% if field(:label) %>
31+
<div class="mt-auto font-mono text-[11.5px] text-sage-10"><%= field(:label) %></div>
32+
<% end %>
33+
<% end %>

src/_components/featured_card.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Card highlighting a featured proof point (work, open source, podcast, writing).
2+
#
3+
# Reads a plain hash from `src/_data/featured.yml` so the homepage owns the
4+
# content while the design system owns the treatment.
5+
class FeaturedCard < Bridgetown::Component
6+
# @param item [Hash] a featured item from `src/_data/featured.yml`
7+
def initialize(item:)
8+
@item = item
9+
end
10+
11+
# Reads a field by either string or symbol key.
12+
# @param key [String, Symbol] the field name
13+
# @return [Object, nil] the value, or nil if absent
14+
def field(key)
15+
@item[key.to_s] || @item[key.to_sym]
16+
end
17+
end

src/_components/home_hero.erb

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
text-[16.5px] text-sage-11 max-w-[600px] leading-[1.65] m-0 mb-4 text-pretty
2222
"
2323
>
24-
I write about Rails, Ruby tooling, and the stuff I learn while shipping
25-
software for a living. Most posts started as a note I was writing for
26-
myself and turned out to be useful for somebody else.
24+
I spend my time on the parts of Rails that compound — fast CI and test
25+
suites, developer tooling, and the workflow details that make a team
26+
quicker. Most of what I learn shipping software for a living ends up
27+
written down here.
2728
</p>
2829
<p
2930
class="
@@ -36,11 +37,19 @@
3637
<%= render Link.new(href: "/speaking/", variant: :inline) do %>talks and podcasts<% end %>
3738
here. Mostly so I can find them again later.
3839
</p>
39-
<%= render Link.new(href: "/posts/", variant: :action) do %>
40-
Read the writing
41-
<%= render Icon.new(
42-
name: "arrow_right",
43-
class: "transition-transform group-hover:translate-x-1",
44-
) %>
45-
<% end %>
40+
<div class="mt-8 flex flex-wrap items-center gap-3">
41+
<%= render Button.new(href: "/posts/", variant: :primary, size: :xl) do %>
42+
Read the writing
43+
<%= render Icon.new(
44+
name: "arrow_right",
45+
class: "ml-1.5 inline-block transition-transform group-hover:translate-x-0.5",
46+
) %>
47+
<% end %>
48+
<%= render Button.new(href: "/projects/", variant: :secondary, size: :xl) do %>
49+
Browse projects
50+
<% end %>
51+
<%= render Button.new(href: "https://remoteruby.com/", variant: :ghost, size: :xl) do %>
52+
Listen to Remote Ruby
53+
<% end %>
54+
</div>
4655
</section>

src/_components/home_now.erb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<ul class="flex flex-col gap-3.5 m-0 p-0 list-none">
2+
<li class="grid grid-cols-[92px_1fr] gap-4 max-sm:grid-cols-1 max-sm:gap-0.5">
3+
<span class="font-mono text-[11.5px] text-sage-10 lowercase pt-px">building</span>
4+
<span class="text-[14.5px] leading-snug text-sage-11 text-pretty">
5+
Shipping Ruby on Rails at
6+
<%= render Link.new(href: "https://www.podia.com/", variant: :inline) do %>Podia<% end %>,
7+
with a soft spot for CI, tooling, and developer experience.
8+
</span>
9+
</li>
10+
<% if episode_url %>
11+
<li class="grid grid-cols-[92px_1fr] gap-4 max-sm:grid-cols-1 max-sm:gap-0.5">
12+
<span class="font-mono text-[11.5px] text-sage-10 lowercase pt-px">podcasting</span>
13+
<span class="text-[14.5px] leading-snug text-sage-11 text-pretty">
14+
Co-hosting
15+
<%= render Link.new(href: episode_url, variant: :inline) do %>Remote Ruby<% end %><% if episode_date %>, latest episode out <%= episode_date %><% end %>.
16+
</span>
17+
</li>
18+
<% end %>
19+
<% if talk_field(:title) %>
20+
<li class="grid grid-cols-[92px_1fr] gap-4 max-sm:grid-cols-1 max-sm:gap-0.5">
21+
<span class="font-mono text-[11.5px] text-sage-10 lowercase pt-px">speaking</span>
22+
<span class="text-[14.5px] leading-snug text-sage-11 text-pretty">
23+
Most recently gave
24+
<%= render Link.new(href: "/speaking/", variant: :inline) do %><%= talk_field(:title) %><% end %><% if talk_field(:venue) %> at <%= talk_field(:venue) %><% end %><% if talk_date %> (<%= talk_date %>)<% end %>.
25+
</span>
26+
</li>
27+
<% end %>
28+
</ul>

src/_components/home_now.rb

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Compact "currently" strip of the freshest real-world signals.
2+
#
3+
# Surfaces active work (role, latest podcast episode, most recent talk) so the
4+
# homepage shows current momentum instead of leaning on blog post dates, which
5+
# can read as stale even when the work behind the scenes is active.
6+
class HomeNow < Bridgetown::Component
7+
# @param episode [Hash, nil] latest episode data (`site.data.remote_ruby`)
8+
# @param talk [Hash, nil] most recent talk from `src/_data/talks.yml`
9+
def initialize(episode:, talk:)
10+
@episode = episode
11+
@talk = talk
12+
end
13+
14+
# @return [String, nil] the episode listen URL
15+
def episode_url
16+
@episode && @episode["url"]
17+
end
18+
19+
# @return [String, nil] the episode publish month, e.g. "Jul 2026"
20+
def episode_date
21+
raw = @episode && @episode["published_at"]
22+
return nil unless raw
23+
Date.parse(raw.to_s).strftime("%b %Y")
24+
rescue ArgumentError, TypeError
25+
nil
26+
end
27+
28+
# Reads a talk field by either string or symbol key.
29+
# @param key [String, Symbol] the field name
30+
# @return [Object, nil] the value, or nil if absent
31+
def talk_field(key)
32+
return nil unless @talk
33+
@talk[key.to_s] || @talk[key.to_sym]
34+
end
35+
36+
# @return [String, nil] the talk month, e.g. "Jun 2026"
37+
def talk_date
38+
d = talk_field(:date)
39+
return nil unless d
40+
(d.is_a?(String) ? Date.parse(d) : d).strftime("%b %Y")
41+
rescue ArgumentError, TypeError
42+
nil
43+
end
44+
end

src/_data/featured.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Curated proof points surfaced in the homepage "featured work" section.
2+
# Each item is a real thing worth pointing people to first.
3+
- title: Podia
4+
kind: work
5+
desc: Ruby on Rails engineer building the all-in-one platform creators use to sell courses, digital downloads, and community. The day job that keeps everything else grounded in real product work.
6+
href: https://www.podia.com/
7+
label: podia.com
8+
- title: Remote Ruby
9+
kind: podcast
10+
desc: The weekly podcast I co-host on Rails, Ruby, and whatever the community is talking about that week — 350+ episodes and counting.
11+
href: https://remoteruby.com/
12+
label: remoteruby.com
13+
- title: Rails Extension Power Pack
14+
kind: open source
15+
desc: A curated VS Code extension pack for Ruby on Rails development. One of my most-installed open source projects.
16+
href: https://github.com/andrewmcodes/rails-extension-power-pack
17+
label: github.com/andrewmcodes
18+
- title: Shared GitHub Actions
19+
kind: open source
20+
desc: Reusable GitHub Actions and CI building blocks I lean on across projects — the CI-and-tooling work I care about, out in the open.
21+
href: https://github.com/andrewmcodes/actions
22+
label: github.com/andrewmcodes

src/index.erb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,28 @@ title: andrewm.codes
1212
<%= render HomeHero.new %>
1313

1414
<%= render PageSection.new(spacing: :home_first) do %>
15+
<%= render SectionHead.new(
16+
title: "now",
17+
description: "What I'm actively working on right now.",
18+
) %>
19+
<%= render HomeNow.new(episode: latest_episode, talk: latest_talk) %>
20+
<% end %>
21+
22+
<% if site.data.featured&.any? %>
23+
<%= render PageSection.new(spacing: :home) do %>
24+
<%= render SectionHead.new(
25+
title: "featured work",
26+
description: "A few things I'm proud of and point people to most often.",
27+
) %>
28+
<%= render CardGrid.new do %>
29+
<% site.data.featured.each do |item| %>
30+
<%= render FeaturedCard.new(item: item) %>
31+
<% end %>
32+
<% end %>
33+
<% end %>
34+
<% end %>
35+
36+
<%= render PageSection.new(spacing: :home) do %>
1537
<%= render SectionHead.new(
1638
title: "recent posts",
1739
link_label: "all posts",

0 commit comments

Comments
 (0)