port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add slot-aware BlogPostLayout with default region components
Extracts BlogPostMeta, BlogPostCover, BlogPostBody as standalone exports so
consumers can wrap or restyle individual regions without forking the layout.
BlogPostLayout now accepts ReactNode slot props for meta, cover, title, body,
sidebar, and comments — undefined uses the default, null omits the region, a
ReactNode overrides it. FeaturedSidebar is the default sidebar when config is
supplied; CollapsibleComments is the default comments slot when post.atUri is
set.
Emits <link rel="site.standard.document" href={post.atUri}> at the top of the
rendered tree when post.atUri is set, eliminating the consumer-side layout
that previously hand-rolled the same tag (item 3 of PLAN-COMPOSABLE-LAYOUT.md).
Adds optional siteOrigin to BlogConfig for canonical URL construction in
forthcoming generateBlogPostMetadata (item 4).
Backward compat: existing callers (src/routes/blog-post-page.tsx) pass no
config and no slots, so the sidebar defaults to null and the rest fall back
to the new defaults — title, date line, and body still render. The body
default now wraps post.body in ReadMoreContent + MarkdownContent rather than
echoing post.description; this is the intentional behavior shift the plan
called out.
Tests walk the returned React element tree (mocking FeaturedSidebar to a sync
stub) and cover default slots, slot overrides, omission via null, classNames
merging, the notFound path, and the link-tag emission tied to atUri rather
than the comments slot.
Add slot-aware BlogPostLayout with default region components
Extracts BlogPostMeta, BlogPostCover, BlogPostBody as standalone exports so
consumers can wrap or restyle individual regions without forking the layout.
BlogPostLayout now accepts ReactNode slot props for meta, cover, title, body,
sidebar, and comments — undefined uses the default, null omits the region, a
ReactNode overrides it. FeaturedSidebar is the default sidebar when config is
supplied; CollapsibleComments is the default comments slot when post.atUri is
set.
Emits <link rel="site.standard.document" href={post.atUri}> at the top of the
rendered tree when post.atUri is set, eliminating the consumer-side layout
that previously hand-rolled the same tag (item 3 of PLAN-COMPOSABLE-LAYOUT.md).
Adds optional siteOrigin to BlogConfig for canonical URL construction in
forthcoming generateBlogPostMetadata (item 4).
Backward compat: existing callers (src/routes/blog-post-page.tsx) pass no
config and no slots, so the sidebar defaults to null and the rest fall back
to the new defaults — title, date line, and body still render. The body
default now wraps post.body in ReadMoreContent + MarkdownContent rather than
echoing post.description; this is the intentional behavior shift the plan
called out.
Tests walk the returned React element tree (mocking FeaturedSidebar to a sync
stub) and cover default slots, slot overrides, omission via null, classNames
merging, the notFound path, and the link-tag emission tied to atUri rather
than the comments slot.
Add slot-aware BlogPostLayout with default region components
Extracts BlogPostMeta, BlogPostCover, BlogPostBody as standalone exports so
consumers can wrap or restyle individual regions without forking the layout.
BlogPostLayout now accepts ReactNode slot props for meta, cover, title, body,
sidebar, and comments — undefined uses the default, null omits the region, a
ReactNode overrides it. FeaturedSidebar is the default sidebar when config is
supplied; CollapsibleComments is the default comments slot when post.atUri is
set.
Emits <link rel="site.standard.document" href={post.atUri}> at the top of the
rendered tree when post.atUri is set, eliminating the consumer-side layout
that previously hand-rolled the same tag (item 3 of PLAN-COMPOSABLE-LAYOUT.md).
Adds optional siteOrigin to BlogConfig for canonical URL construction in
forthcoming generateBlogPostMetadata (item 4).
Backward compat: existing callers (src/routes/blog-post-page.tsx) pass no
config and no slots, so the sidebar defaults to null and the rest fall back
to the new defaults — title, date line, and body still render. The body
default now wraps post.body in ReadMoreContent + MarkdownContent rather than
echoing post.description; this is the intentional behavior shift the plan
called out.
Tests walk the returned React element tree (mocking FeaturedSidebar to a sync
stub) and cover default slots, slot overrides, omission via null, classNames
merging, the notFound path, and the link-tag emission tied to atUri rather
than the comments slot.
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
port blog components
- AuthorCard, BlogPagination, BlueskyComments, BlueskyEmbed,
CategoryFilter, CollapsibleComments, MarkdownContent, PostList,
ReadMoreContent, ScrollLogger, SortButton, Subscribe
- Category chips render via <Toggle pressed> (was Button variant
"toggled") so the submodule works against stock shadcn primitives
with no consumer-side variant additions
- Hardcoded color/spacing replaced with semantic Tailwind tokens
(text-primary, bg-background, border-border, …) — consumer
retunes via CSS variables, submodule never hardcodes brand
- BlueskyComments/Subscribe accept theme prop layered over sensible
defaults; <sequoia-*> custom-element registration is dynamic-
imported through the consumer's @/components/sequoia-* path
- ReadMoreContent + ScrollLogger use useBlogTelemetry() so all
client-side events route through BlogConfig.telemetry
- MarkdownContent exposes overridable remarkPlugins / rehypePlugins
/ embeds props with the current defaults (remarkGfm,
rehypePrettyCode, BlueskyEmbed)
- example/ adds Next env.d.ts + tsconfig + stub sequoia-* modules so
in-submodule tsc resolves @/components/* without the real sequoia
runtime
- Barrel re-exports the component set (renamed to AuthorCard to
avoid collision with the Author type)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>