Skip to content

Commit 288bd1f

Browse files
committed
feat: blog identity, unified code renderer, logo-bright syntax palette
Blog section - Year-as-poster list layout with sticky spine, warm blogAccent color - Split BlogMeta into byline (top) and colophon (bottom) - Magazine HR treatment scoped to blog routes - Drop 100ch content cap so blog matches docs width Layout - Add theme.layout.gutterFluid (clamp(2rem, 5vw, 5rem)) token - Navbar and docs/blog/hero content share the same fluid gutter - HeroContent styled component replaces plain .hero-content class - Update markdown-to-jsx to 9.7.15 (restores releases page) Syntax highlighting - Unify all code blocks through HighlightedCode component — static CodeBlock, interactive LiveEdit, and HomepageHeroEditor share the same annotation pipeline and token rendering - Extract codeTextMixin so all code blocks share font weight, colors, family, and letter-spacing - CSS grammar extension: tokenize CSS value keywords (inline-block, ease-in-out, all, etc.) as constants - TSX grammar extension: re-tokenize __html template literals as JS so dangerouslySetInnerHTML script content gets full highlighting - TSX grammar extension: recognize styled.div<Generic> template literals so their CSS content still renders with CSS sub-grammar - JSX tag depth annotator: 4-level complementary palette (cyan, blue, amber, teal) stamped on tag fragments only, not embedded JS inside attribute expressions - Property access chain depth: stamp deep class on 2nd+ member so the leaf of props.theme.fg renders distinctly - Optional chaining fix: stamp property-access on identifiers after ?. so the depth counter continues through them - Function machinery group: attr-name, arrow, and parameter share brand pink with function calls - Operators removed from theme dict so arrow wins cascade - Code palette matches PlatonicLogo brightness profile in dark mode (L 0.82, high chroma across all chromatic tokens) - Warm-tinted neutrals (hue 60) so code text feels inky not disabled - codeProperty and codePropertyDeep for chain members - Medium font weight in light mode, light weight in dark mode
1 parent 88d1b15 commit 288bd1f

19 files changed

Lines changed: 990 additions & 207 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ __diff_output__
1616
.next
1717
next-env.d.ts
1818
*.tsbuildinfo
19+
20+
# agent state
21+
.claude/

app/blog/blog.css

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
/* Route-level overrides for blog pages.
22
Static CSS to avoid the createGlobalStyle SSR -> client hydration flash. */
33

4-
[data-e2e-id='content'] {
5-
max-width: 100ch;
6-
}
7-
84
[data-e2e-id='content'] img {
95
max-width: 100%;
106
height: auto;
117
border-radius: 4px;
128
}
9+
10+
/* Magazine-style section break: short warm rule, generous air. */
11+
[data-e2e-id='content'] hr {
12+
border: 0;
13+
margin: 3.5rem auto;
14+
width: 4rem;
15+
height: 2px;
16+
background: var(--sc-color-blogAccent);
17+
opacity: 0.6;
18+
}

app/homepage.css

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,6 @@
1111
padding-bottom: 3rem;
1212
}
1313

14-
.hero-content {
15-
box-sizing: border-box;
16-
font-family: var(--font-sans);
17-
margin: 0 auto;
18-
max-width: 72rem;
19-
padding: 4rem 2rem 0;
20-
width: 100%;
21-
}
22-
2314
.hero-tagline {
2415
font-family: var(--font-display);
2516
font-weight: var(--sc-fontWeight-display);
@@ -51,10 +42,6 @@
5142
}
5243

5344
@media (max-width: 62.5em) {
54-
.hero-content {
55-
padding: 5rem 2rem 0;
56-
}
57-
5845
.hero-tagline,
5946
.hero-subtitle {
6047
text-align: center;

app/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import CelebrationEffect from '../components/CelebrationEffect';
22
import Footer from '../components/Footer';
33
import HomepageBadges from '../components/HomepageBadges';
4-
import HomepageHeroEditor from '../components/HomepageHeroEditor';
4+
import HomepageHeroEditor, { HeroContent } from '../components/HomepageHeroEditor';
55
import LatestBlogPost from '../components/LatestBlogPost';
66
import { HomepageLogos, HomepageShowcase } from '../components/HomepageShowcase';
77
import HomepageGettingStartedContent from '../components/HomepageGettingStarted';
@@ -13,7 +13,7 @@ export default function Index() {
1313
<CelebrationEffect />
1414

1515
<div className="hero-header">
16-
<div className="hero-content">
16+
<HeroContent>
1717
<HomepageHeroEditor>
1818
<LatestBlogPost />
1919

@@ -24,7 +24,7 @@ export default function Index() {
2424

2525
<HomepageBadges />
2626
</HomepageHeroEditor>
27-
</div>
27+
</HeroContent>
2828

2929
<p className="hero-used-by">Used by folks at</p>
3030
<HomepageLogos />

components/BlogListPage.tsx

Lines changed: 80 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,37 @@ import DocsLayout from './DocsLayout';
55
import Link from './Link';
66
import rem from '../utils/rem';
77
import { theme, font } from '../utils/theme';
8-
import { linkStyle } from './Link';
98
import { formatDate } from '../utils/formatDate';
109
import { postsByYear, years } from '../utils/blog';
11-
import DotSeparator from './DotSeparator';
1210

1311
export default function BlogListPage() {
1412
return (
1513
<DocsLayout title="Blog">
16-
<p>
14+
<Lede>
1715
Articles, tutorials, and announcements from the styled-components community. Originally published on{' '}
1816
<Link href="https://medium.com/styled-components" inline>
1917
Medium
2018
</Link>
2119
.
22-
</p>
20+
</Lede>
2321

2422
{years.map(year => (
2523
<YearSection key={year}>
26-
<YearHeading id={year}>{year}</YearHeading>
24+
<Year id={year} aria-label={`Posts from ${year}`}>
25+
{year}
26+
</Year>
2727
<PostList>
2828
{postsByYear[year].map(post => (
2929
<PostItem key={post.slug}>
3030
<PostLink href={`/blog/${post.slug}`} unstyled>
3131
<PostTitle>{post.title}</PostTitle>
32+
{post.description && <PostDescription>{post.description}</PostDescription>}
33+
<PostMeta>
34+
<Author>{post.author}</Author>
35+
<Divider></Divider>
36+
<time dateTime={post.date}>{formatDate(post.date)}</time>
37+
</PostMeta>
3238
</PostLink>
33-
<PostMeta>
34-
<Author>{post.author}</Author>
35-
<DotSeparator />
36-
<time dateTime={post.date}>{formatDate(post.date)}</time>
37-
</PostMeta>
38-
{post.description && <PostDescription>{post.description}</PostDescription>}
3939
</PostItem>
4040
))}
4141
</PostList>
@@ -45,76 +45,111 @@ export default function BlogListPage() {
4545
);
4646
}
4747

48+
const Lede = styled.p`
49+
font-family: ${font.sans};
50+
font-size: ${theme.text.md};
51+
color: ${theme.color.textSecondary};
52+
max-width: 60ch;
53+
margin-bottom: ${theme.space[16]};
54+
`;
55+
4856
const YearSection = styled.section`
49-
margin-top: ${theme.space[8]};
57+
display: grid;
58+
grid-template-columns: minmax(0, 13rem) minmax(0, 1fr);
59+
gap: ${rem(48)};
60+
margin-bottom: ${theme.space[16]};
61+
62+
@container content (max-width: 700px) {
63+
grid-template-columns: 1fr;
64+
gap: ${rem(12)};
65+
}
5066
`;
5167

52-
const YearHeading = styled.h2`
53-
font-size: ${theme.text.xl};
54-
font-weight: ${theme.fontWeight.bold};
55-
margin: 0 0 ${rem(8)} 0;
56-
padding-bottom: ${rem(8)};
57-
border-bottom: 1px solid ${theme.color.border};
68+
const Year = styled.h2`
69+
font-family: ${font.display};
70+
font-size: clamp(3rem, 7vw, 4.5rem);
71+
font-weight: ${theme.fontWeight.display};
72+
line-height: 0.9;
73+
letter-spacing: -0.03em;
74+
color: ${theme.color.blogAccent};
75+
margin: 0;
76+
position: sticky;
77+
top: ${rem(90)};
78+
align-self: start;
79+
font-variant-numeric: tabular-nums;
80+
81+
@container content (max-width: 700px) {
82+
position: static;
83+
font-size: clamp(2.5rem, 12vw, 3.5rem);
84+
}
5885
`;
5986

6087
const PostList = styled.div`
6188
display: flex;
6289
flex-direction: column;
6390
`;
6491

65-
const PostItem = styled.div`
66-
padding: ${rem(20)} 0;
67-
border-bottom: 1px solid ${theme.color.border};
92+
const PostItem = styled.article`
93+
padding: ${rem(24)} 0;
6894
69-
&:last-child {
70-
border-bottom: none;
95+
& + & {
96+
border-top: 1px solid ${theme.color.blogAccentMuted};
97+
}
98+
99+
&:first-child {
100+
padding-top: ${rem(8)};
71101
}
72102
`;
73103

74104
const PostLink = styled(Link)`
75-
${linkStyle}
105+
display: block;
106+
color: inherit;
107+
text-decoration: none;
76108
`;
77109

78110
const PostTitle = styled.h3`
79111
font-family: ${font.display};
80-
font-size: ${theme.text.xl};
81-
font-weight: ${theme.fontWeight.medium};
82-
margin: 0 0 ${rem(6)} 0;
83-
color: inherit;
84-
transition: color ${theme.duration.normal};
112+
font-size: ${theme.text['2xl']};
113+
font-weight: ${theme.fontWeight.display};
114+
line-height: 1.15;
115+
letter-spacing: -0.01em;
116+
margin: 0 0 ${rem(10)} 0;
117+
color: ${theme.color.text};
118+
transition: color ${theme.duration.normal} ${theme.ease.out};
85119
86120
${PostLink}:hover & {
87-
opacity: 0.8;
121+
color: ${theme.color.blogAccent};
88122
}
89123
90124
@container content (max-width: 600px) {
91-
font-size: ${theme.text.lg};
125+
font-size: ${theme.text.xl};
92126
}
93127
`;
94128

129+
const PostDescription = styled.p`
130+
font-family: ${font.sans};
131+
font-size: ${theme.text.base};
132+
line-height: 1.55;
133+
margin: 0 0 ${rem(12)};
134+
color: ${theme.color.textSecondary};
135+
max-width: 62ch;
136+
`;
137+
95138
const PostMeta = styled.div`
96139
font-family: ${font.sans};
97140
font-size: ${theme.text.sm};
98-
color: ${theme.color.textSecondary};
141+
color: ${theme.color.textMuted};
99142
display: flex;
100143
flex-wrap: wrap;
101-
gap: ${rem(8)};
102-
align-items: center;
144+
gap: ${rem(10)};
145+
align-items: baseline;
103146
`;
104147

105148
const Author = styled.span`
106149
font-weight: ${theme.fontWeight.semibold};
107-
color: ${theme.color.textMuted};
108-
`;
109-
110-
const PostDescription = styled.p`
111-
font-family: ${font.sans};
112-
font-size: ${theme.text.base};
113-
margin: ${rem(8)} 0 0;
114-
line-height: 1.5;
115150
color: ${theme.color.textSecondary};
151+
`;
116152

117-
@container content (max-width: 500px) {
118-
display: none;
119-
}
153+
const Divider = styled.span.attrs({ 'aria-hidden': 'true' })`
154+
color: ${theme.color.blogAccentMuted};
120155
`;

0 commit comments

Comments
 (0)