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
@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"
@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
@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
@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
@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