--- title: "API Contract" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{API Contract} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- # API Contract for `shinyseo` This page is written so another agent can use the package without reading the source. ## Exported surface - `social_meta(meta)` — builds and injects metadata tags into the page `` at startup. - `update_meta(session, title, description, url, image)` — reactively updates one or more metadata fields from the server at runtime. - `write_manifest(meta, path, display, start_url, background_color)` — writes a `manifest.json` to `www/` so Shiny can serve it. ## Return value `social_meta()` returns a `shiny::tags$head()` fragment. You normally place it inside a Shiny UI definition, for example: ```r shinyseo::social_meta(list( title = "Hello", description = "Short app description.", url = "https://example.no", image = "https://example.no/share.png" )) ``` ## Input contract `meta` can be one of: - a character string containing a path to a YAML file - a named list of metadata values If `meta` is a character string, the package reads it with `yaml::read_yaml()`. If the file does not exist, the function stops with: - `Meta file not found: ` ## Required fields These keys must exist in the final metadata object: - `title` - `description` - `url` - `image` The function checks only that the names exist. It does not currently validate that the values are non-empty strings. If any required key is missing, the function stops with: - `Missing required meta fields: ...` ## Defaults The package fills these defaults when the key is missing or `NULL`: - `locale = "en_US"` - `robots = "index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1"` - `twitter_card = "summary_large_image"` - `bing_site_verification = Sys.getenv("SHINYSEO_BING_SITE_VERIFICATION")` when present - `twitter_site = Sys.getenv("SHINYSEO_TWITTER_SITE")` when present - `twitter_creator = Sys.getenv("SHINYSEO_TWITTER_CREATOR")` when present - `schema_type = "WebApplication"` - `operating_system = "Any"` - `author_type = "Person"` - `publisher_type = "Organization"` - `in_language = locale` Important detail: - `schema` is enabled unless it is exactly `FALSE` - `schema = TRUE`, `schema = NULL`, or an omitted `schema` field all result in JSON-LD output - `schema = FALSE` suppresses JSON-LD - The helper does not emit a `` tag; set the document title in the app UI so it does not clash with any title Shiny already sets ## Output tags The function writes these tags into the returned head fragment: - `<link rel="canonical" href="...">` - `<meta name="description" ...>` - `<meta name="robots" ...>` - Open Graph tags used by Facebook, LinkedIn, Slack, X, and other platforms that read Open Graph: - `og:type = website` - `og:title` - `og:description` - `og:url` - `og:image` - `og:site_name` when `site_name` is present - `og:locale` when `locale` is present - `og:image:width` when `image_width` is present - `og:image:height` when `image_height` is present - `og:image:type` when `image_type` is present - `og:image:alt` when `image_alt` is present - Twitter Card tags: - `twitter:card` - `twitter:title` - `twitter:description` - `twitter:image` - `twitter:url` - `twitter:site` when `twitter_site` is present - `twitter:creator` when `twitter_creator` is present - `twitter:image:alt` when `twitter_image_alt` is present - Bing verification: - `msvalidate.01` when `bing_site_verification` is present - Google verification: - `google-site-verification` when `google_site_verification` is present - Yandex verification: - `yandex-verification` when `yandex_site_verification` is present - Baidu verification: - `baidu-site-verification` when `baidu_site_verification` is present - Naver verification: - `naver-site-verification` when `naver_site_verification` is present - Facebook domain verification: - `facebook-domain-verification` when `facebook_domain_verification` is present - Pinterest domain verification: - `p:domain_verify` when `pinterest_domain_verification` is present - Schema.org JSON-LD: - only when `schema` is not `FALSE` - always includes `@context`, `@type`, `name`, `description`, `url`, and `inLanguage` - conditionally includes `applicationCategory`, `operatingSystem`, `educationalUse`, `isAccessibleForFree`, `disclaimer`, `author`, and `publisher` ## Field map | Key | Used for | | --- | --- | | `title` | Open Graph title, Twitter title, schema name | | `description` | Meta description, Open Graph description, Twitter description, schema description | | `url` | Canonical URL, Open Graph URL, Twitter URL, schema URL | | `image` | Open Graph image, Twitter image | | `locale` | Open Graph locale, schema language default | | `robots` | Robots meta tag | | `twitter_card` | Twitter card type | | `twitter_site` | Twitter site handle; falls back to `SHINYSEO_TWITTER_SITE` | | `twitter_creator` | Twitter creator handle; falls back to `SHINYSEO_TWITTER_CREATOR` | | `twitter_image_alt` | Twitter image alt text | | `image_alt` | Open Graph image alt text | | `image_width` | Open Graph image width | | `image_height` | Open Graph image height | | `image_type` | Open Graph image MIME type | | `bing_site_verification` | Bing Webmaster verification; falls back to `SHINYSEO_BING_SITE_VERIFICATION` | | `google_site_verification` | Google Search Console verification | | `yandex_site_verification` | Yandex Webmaster verification | | `baidu_site_verification` | Baidu Webmaster verification | | `naver_site_verification` | Naver Webmaster verification | | `facebook_domain_verification` | Facebook domain verification | | `pinterest_domain_verification` | Pinterest domain verification | | `schema_type` | Schema.org `@type` | | `application_category` | Schema.org `applicationCategory` | | `operating_system` | Schema.org `operatingSystem` | | `educational_use` | Schema.org `educationalUse` | | `is_accessible_for_free` | Schema.org `isAccessibleForFree` | | `disclaimer` | Schema.org `disclaimer` | | `author_name` | Schema.org `author.name` | | `author_type` | Schema.org `author.@type` | | `publisher_name` | Schema.org `publisher.name` | | `publisher_type` | Schema.org `publisher.@type` | | `in_language` | Schema.org `inLanguage` | | `schema` | Turns JSON-LD on or off | ## Favicon and web app manifest fields These optional fields in `social_meta()` control browser icon and PWA behaviour: | Field | Tag produced | | --- | --- | | `favicon` | `<link rel="icon" type="..." href="...">` | | `favicon_type` | Overrides the MIME type inferred from `favicon`'s extension | | `favicon_png` | `<link rel="icon" type="image/png" sizes="..." href="...">` — PNG fallback alongside an SVG `favicon` | | `favicon_png_sizes` | Overrides the `sizes` attribute on `favicon_png` (defaults to `"32x32"`) | | `apple_touch_icon` | `<link rel="apple-touch-icon" href="...">` | | `manifest` | `<link rel="manifest" href="...">` | | `theme_color` | `<meta name="theme-color" content="...">` | | `short_name` | Used by `write_manifest()` for the manifest `short_name` field | MIME type is inferred automatically from the file extension of `favicon` (`.ico`, `.png`, `.svg`, `.webp`, `.gif`). Supply `favicon_type` to override. If `favicon` is an SVG, also set `favicon_png` to a PNG fallback. Chromium-based browsers don't render SVG favicons in the address bar and fall back to a generic globe icon without one. ### `write_manifest()` Generates `www/manifest.json` from the same metadata object. Call it once in `global.R`: ```r shinyseo::write_manifest(list( title = "My App", short_name = "MyApp", description = "A Shiny web app.", theme_color = "#1a73e8", favicon = "/favicon.png", apple_touch_icon = "/apple-touch-icon.png" )) ``` Then reference the manifest from the UI: ```r shinyseo::social_meta(list( ..., manifest = "/manifest.json" )) ``` Parameters: - `meta` — same YAML path or list as `social_meta()`. - `path` — directory to write into. Defaults to `"www"`. - `display` — PWA display mode: `"standalone"` (default), `"fullscreen"`, `"minimal-ui"`, or `"browser"`. - `start_url` — start URL in the manifest. Defaults to `"/"`. - `background_color` — splash screen background. Defaults to `theme_color` when set, otherwise `"#ffffff"`. ## `update_meta()` — reactive metadata updates `update_meta(session, title, description, url, image)` sends a custom message to the browser that updates the live DOM without a page reload. It is intended for multi-page or tabbed Shiny apps where the metadata should reflect the current view. ### Parameters - `session` — the Shiny `session` object passed to `server`. - `title` — replaces `document.title`, `og:title`, and `twitter:title`. - `description` — replaces `meta[name="description"]`, `og:description`, and `twitter:description`. - `url` — replaces `link[rel="canonical"]`, `og:url`, and `twitter:url`. - `image` — replaces `og:image` and `twitter:image`. All parameters except `session` are optional. Only the fields you supply are changed; omitted fields keep their current values. If no fields are supplied the function is a no-op. ### Requirements `social_meta()` must be present in the UI for the JavaScript handler to be registered. `update_meta()` alone has no effect. ### Example ```r server <- function(input, output, session) { observeEvent(input$tabs, { if (input$tabs == "about") { shinyseo::update_meta(session, title = "About – My App", description = "Learn more about the team behind the app." ) } }) } ``` ## Behavior notes - The function does not mutate the input list outside its local copy. - The helper returns plain Shiny tags, so it is safe to embed in `fluidPage()`, `navbarPage()`, or any other UI container that accepts head tags. - The package is intentionally small and generic. - If you need a simpler SEO-only configuration, you can still use the required four fields and leave the rest blank.