feat(shell): SEO/OG meta tags, per-page descriptions, host-run OG image routine#49
Merged
Merged
Conversation
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #48. Every docs-kit page now ships a complete SEO
<head>— metadescription, 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-tagsgem, no new runtime dependency), matching how thebrand-marks / topbar-links features were added.
What's new
DocsUI::MetaTags— a Phlex component that readsDocsKit.configuration+the per-page title/description and emits the meta/link tags inside
Shell's head.c.seoconfig block (DocsKit::SeoConfig) with backwards-safe defaults:description,og_image,og_type,twitter_card/site/creator,locale,site_url,robots,favicon,theme_color.DocsUI::Page.description "...", falling back to thepage's
#lead, then to the site-widec.seo.description.og:imageisnever broken, and installs a
docs_kit:ograke task that screenshots a site'sown 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::OgGeneratoris 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.
c.seo, installs the task + defaultimage;
docs-kit newreminds the owner to run it. README +.claude/rules/seo.mdDecisions (confirmed with the requester on #48)
DocsUI::MetaTags#leadfallbackRejected: the
meta-tagsgem (hard dep + non-Phlex head), a runtimeGET /og.pngendpoint (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 rake→ 714 examples, 0 failures; coverage 94.45% (100% forConfigurationandSeoConfig),rubocopclean.gem build --strictpackages cleanly and includes the OG image + new files.its
<head>is a strict superset of before.application.tailwind.cssuntouched, nobuild:cssneeded.New/changed specs:
spec/docs_kit/seo_config_spec.rb— defaults + per-field round-tripspec/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 MetaTagsspec/docs_ui/page_spec.rb—self.class.description || leadresolutionspec/docs_kit/og_generator_spec.rb— shooter resolution + per-tool command buildingspec/docs_kit/og_image_spec.rb— shipped default present + valid PNG (guard)spec/generators/install_generator_spec.rb—c.seoblock +og.rake+ default imagehttps://claude.ai/code/session_01FPQb6z3YwcKRMbvoJhdxnX