Skip to content

feat(shell): SEO/OG meta tags, per-page descriptions, host-run OG image routine#49

Merged
mhenrixon merged 1 commit into
mainfrom
issue-48-seo-og-meta-tags
Jul 3, 2026
Merged

feat(shell): SEO/OG meta tags, per-page descriptions, host-run OG image routine#49
mhenrixon merged 1 commit into
mainfrom
issue-48-seo-og-meta-tags

Conversation

@mhenrixon

Copy link
Copy Markdown
Owner

Summary

Closes #48. Every docs-kit page now ships a complete SEO <head> — meta
description, Open Graph, Twitter Card, canonical, favicon, robots, and
theme-color — so a link shared to Slack/Discord/X/LinkedIn renders a rich card
instead of a bare URL. It's all config-driven; a site that sets nothing still gets
a valid minimal card, so this is fully backwards-compatible.

Native Phlex (no meta-tags gem, no new runtime dependency), matching how the
brand-marks / topbar-links features were added.

What's new

  • DocsUI::MetaTags — a Phlex component that reads DocsKit.configuration +
    the per-page title/description and emits the meta/link tags inside Shell's head.
  • c.seo config block (DocsKit::SeoConfig) with backwards-safe defaults:
    description, og_image, og_type, twitter_card/site/creator, locale,
    site_url, robots, favicon, theme_color.
  • Per-page descriptionsDocsUI::Page.description "...", falling back to the
    page's #lead, then to the site-wide c.seo.description.
  • OG image routine — docs-kit ships a neutral 1200×630 default so og:image is
    never broken, and installs a docs_kit:og rake task that screenshots a site's
    own landing page into app/assets/images/og/. The screenshot tooling
    (shot-scraper / chromium) is resolved at task runtime and is never a
    docs-kit runtime dependency — DocsKit::OgGenerator is loaded only by the task.
    A guard spec keeps the shipped default present. This mirrors phlex-reactive's
    vendored-client re-sync: a documented command + a guard spec, not a cron.
  • Install path — the generator documents c.seo, installs the task + default
    image; docs-kit new reminds the owner to run it. README + .claude/rules/seo.md
    • CHANGELOG updated.

Decisions (confirmed with the requester on #48)

Fork Chosen Why
Meta engine Native DocsUI::MetaTags On-pattern, dependency-free, pure-Phlex head
OG images Config path + shipped default + host-run rake Image is each site's own branding; no binary blobs/browser in the gem's CI
Page descriptions Authorable + #lead fallback Best SEO, least authoring friction

Rejected: the meta-tags gem (hard dep + non-Phlex head), a runtime GET /og.png
endpoint (larger surface), committing screenshots into the gem (blobs every site
overrides), and a cron refresh (phlex-reactive's precedent is a guard spec).

Test plan

  • bundle exec rake714 examples, 0 failures; coverage 94.45% (100% for
    Configuration and SeoConfig), rubocop clean.
  • gem build --strict packages cleanly and includes the OG image + new files.
  • Backwards compat verified: an unconfigured site renders a valid minimal card and
    its <head> is a strict superset of before.
  • No CSS classes emitted — application.tailwind.css untouched, no build:css needed.

New/changed specs:

  • spec/docs_kit/seo_config_spec.rb — defaults + per-field round-trip
  • spec/docs_ui/meta_tags_spec.rb — description/OG/Twitter/canonical/favicon/robots/
    theme-color, escaping (attribute-breakout containment), minimal-default, request branch
  • spec/docs_ui/shell_spec.rb — Shell threads title/description into MetaTags
  • spec/docs_ui/page_spec.rbself.class.description || lead resolution
  • spec/docs_kit/og_generator_spec.rb — shooter resolution + per-tool command building
  • spec/docs_kit/og_image_spec.rb — shipped default present + valid PNG (guard)
  • spec/generators/install_generator_spec.rbc.seo block + og.rake + default image

https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX

…ge routine

Every docs-kit page now emits a complete SEO <head> — meta description, Open
Graph, Twitter Card, canonical, favicon, robots, theme-color — so shared links
render a rich card instead of a bare URL. It's all config-driven; a site that
sets nothing still gets a valid minimal card, so it's fully backwards-compatible.

## Summary
- New DocsUI::MetaTags Phlex component (no runtime dep) reads DocsKit.configuration
  + the per-page title/description; rendered inside Shell's <head>.
- New c.seo config block (DocsKit::SeoConfig) with backwards-safe defaults:
  description, og_image, og_type, twitter_card/site/creator, locale, site_url,
  robots, favicon, theme_color.
- Per-page description: DocsUI::Page.description DSL, falling back to the page #lead.
- Ships a neutral 1200x630 default OG image; installs a `docs_kit:og` rake task
  that screenshots a site's OWN landing page (host-side headless browser via
  DocsKit::OgGenerator — never a gem runtime dependency). Guard spec keeps the
  shipped default present (phlex-reactive-style: documented command + guard, no cron).
- Wired into the install generator (documents c.seo, installs the task + image)
  and the `docs-kit new` message; README + .claude/rules/seo.md + CHANGELOG.

## Test Coverage
- spec/docs_kit/seo_config_spec.rb: defaults + per-field round-trip (100% SeoConfig)
- spec/docs_ui/meta_tags_spec.rb: description/OG/Twitter/canonical/favicon/robots/
  theme-color, escaping (attribute-breakout containment), minimal-default, request branch
- spec/docs_ui/shell_spec.rb: Shell threads title/description into MetaTags (+ compat)
- spec/docs_ui/page_spec.rb: description = self.class.description || lead resolution
- spec/docs_kit/og_generator_spec.rb: shooter resolution + per-tool command building
- spec/docs_kit/og_image_spec.rb: shipped default present + valid PNG (guard)
- spec/generators/install_generator_spec.rb: c.seo block + og.rake + default image

## Verification
- [x] bundle exec rake (714 examples, 0 failures; coverage 94.45%, 100% Config/SeoConfig)
- [x] bundle exec rubocop — no offenses
- [x] gem build --strict — packages cleanly, includes the OG image + new files
- [x] backwards compatible — unconfigured site renders a valid minimal card, head is a superset
- [x] no CSS classes emitted — application.tailwind.css untouched, no build needed

Refs #48

Claude-Session: https://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX
@mhenrixon mhenrixon self-assigned this Jul 3, 2026
@mhenrixon mhenrixon added the enhancement New feature or request label Jul 3, 2026
@mhenrixon mhenrixon merged commit c68be86 into main Jul 3, 2026
4 checks passed
@mhenrixon mhenrixon deleted the issue-48-seo-og-meta-tags branch July 4, 2026 14:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SEO + social-share (OG/Twitter) meta tags, per-page descriptions, and a host-run OG-image routine

1 participant