Sayfa.SEO (Sayfa v0.5.0)

Copy Markdown View Source

Generates SEO meta tags for HTML pages.

Produces Open Graph, Twitter Card, description, canonical URL tags, JSON-LD structured data, and hreflang alternate links. Designed to be called from the base template:

<%= Sayfa.SEO.meta_tags(@content, @site) %>
<%= Sayfa.SEO.json_ld(assigns[:content], @site) %>
<%= Sayfa.SEO.hreflang_tags(assigns[:content], @site, assigns[:archive_alternates]) %>

Summary

Functions

Builds the full URL for a content item.

Generates favicon link tags for the HTML head.

Generates hreflang alternate link tags for multilingual content.

Generates a JSON-LD structured data script tag.

Generates HTML meta tags for SEO.

Functions

content_url(content, config)

@spec content_url(Sayfa.Content.t(), map()) :: String.t()

Builds the full URL for a content item.

Examples

iex> content = %Sayfa.Content{title: "T", body: "", slug: "hello", meta: %{"url_prefix" => "articles", "lang_prefix" => ""}}
iex> config = %{base_url: "https://example.com"}
iex> Sayfa.SEO.content_url(content, config)
"https://example.com/articles/hello"

iex> content = %Sayfa.Content{title: "T", body: "", slug: "about", meta: %{"url_prefix" => "", "lang_prefix" => ""}}
iex> config = %{base_url: "https://example.com"}
iex> Sayfa.SEO.content_url(content, config)
"https://example.com/about"

favicon_tags()

@spec favicon_tags() :: String.t()

Generates favicon link tags for the HTML head.

Returns link tags for favicon.ico, favicon.svg, apple-touch-icon, and web app manifest.

Examples

iex> Sayfa.SEO.favicon_tags() =~ ~s(rel="icon")
true

hreflang_tags(content, config, archive_alternates \\ nil)

@spec hreflang_tags(Sayfa.Content.t() | nil, map(), map() | nil) :: String.t()

Generates hreflang alternate link tags for multilingual content.

Reads content.meta["hreflang_alternates"] (a list of {lang, path} tuples) and renders <link rel="alternate" hreflang="..." href="..."> for each. When multiple alternates exist, also adds an x-default pointing to the content's own URL.

Returns an empty string when content is nil or has no alternates.

Examples

iex> config = %{base_url: "https://example.com"}
iex> content = %Sayfa.Content{title: "T", body: "", slug: "hello", meta: %{"url_prefix" => "articles", "lang_prefix" => "", "hreflang_alternates" => [{"en", "/articles/hello"}, {"tr", "/tr/articles/merhaba"}]}}
iex> html = Sayfa.SEO.hreflang_tags(content, config)
iex> html =~ ~s(hreflang="en")
true
iex> html =~ ~s(hreflang="tr")
true
iex> html =~ ~s(hreflang="x-default")
true

json_ld(content, config)

@spec json_ld(Sayfa.Content.t() | nil, map()) :: String.t()

Generates a JSON-LD structured data script tag.

For articles/notes, generates a BlogPosting schema. For pages, generates a WebPage schema. For nil (list pages), generates a WebSite schema.

Examples

iex> config = %{title: "My Site", base_url: "https://example.com", author: "Jane"}
iex> content = %Sayfa.Content{title: "Hello", body: "<p>World</p>", slug: "hello", date: ~D[2024-01-15], meta: %{"content_type" => "articles", "url_prefix" => "articles", "lang_prefix" => ""}}
iex> html = Sayfa.SEO.json_ld(content, config)
iex> html =~ "BlogPosting"
true
iex> html =~ "application/ld+json"
true

meta_tags(content, config, page_url \\ nil)

@spec meta_tags(Sayfa.Content.t() | nil, map(), String.t() | nil) :: String.t()

Generates HTML meta tags for SEO.

When content is a %Content{}, generates tags specific to that content. When content is nil (e.g., list pages), uses site-level defaults.

Examples

iex> config = %{title: "My Site", base_url: "https://example.com", description: "A blog"}
iex> content = %Sayfa.Content{title: "Hello", body: "<p>World</p>", slug: "hello", meta: %{"url_prefix" => "articles"}}
iex> html = Sayfa.SEO.meta_tags(content, config)
iex> html =~ ~s(og:title)
true
iex> html =~ ~s(canonical)
true