<!-- ccr-slack-attribution --> _Requested by **Nicole Kramer** · [Slack thread](https://supabase.slack.com/archives/C0161K73J1J/p1786027145751449)_ ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Feature — a new legal page on the marketing site (`apps/www`). ## What is the current behavior? **Before:** the Program Addenda page at `/legal/partner-resources/program-addenda` lists exactly one addendum, the Integration Partner Addendum. There is no published Open Authorization (OAuth) addendum anywhere on the site. ## What is the new behavior? **After:** the Program Addenda page also lists the **Open Authorization Integration Addendum**, linking to a new page at `/legal/partner-resources/program-addenda/oauth-partner-addendum`. Formatting, breadcrumbs, version selector, and listing badge all match the existing Integration Partner Addendum. **How:** three files. - `apps/www/data/legal/partner-resources/oauth-partner-addendum/20260806-v1.mdx` — the addendum text, formatted to match `integration-partner-addendum/20260615-v1.1.mdx` (escaped section-number periods, `####` run-in headings for the lettered subsections, italic `_Label_` run-in labels for the enumerated data-protection clauses, explicit `[url](url)` links). - `apps/www/pages/legal/partner-resources/program-addenda/oauth-partner-addendum.tsx` — the page, mirroring `integration-partner-addendum.tsx` with a single-version `versions` array. - `apps/www/lib/addenda.ts` — adds a small `TITLE_OVERRIDES` map. The listing derives titles by capitalizing slug words, which turns `oauth-partner-addendum` into "Oauth Partner Addendum"; the override makes the listing link read the same as the page's `h1`. No other wiring was needed: the addenda listing is generated from the directory, so there is no hub entry, redirect, rewrite, sitemap entry, or `noindex` rule to add. ## Additional context Two things for the requester to confirm: - **The effective date is an assumption.** The addendum document itself contains no date. The listing and version label derive the effective date from the `YYYYMMDD` filename prefix, so this file is dated **August 6, 2026**, taken from the source document's own filename (`2026.08.06 - Supabase-OAuthAddendum-ONLINE.docx`). To change it, rename the file — no code change required. - **The legal text is a verbatim transcription.** Source wording, capitalization, and punctuation are preserved exactly as drafted, including anything that reads like a typo. Only markup was added; the plain text was diffed against the transcription and is character-identical. Please review the wording itself rather than assuming it was copy-edited. One wording choice that was not in the source document: the page subheader, "An addendum to the Master Partner Program Agreement governing OAuth integrations." It mirrors the one-line subheader style of the existing addendum page and is easy to reword. ## Also fixed here: a literal `(c)` rendered as `©` in legal headings While formatting the new addendum we hit a rendering bug that turned out to be **already live on supabase.com**, not new to this branch. The heading font, **Manrope**, ships a default-on standard `liga` feature that maps the glyph sequence `parenleft c parenright` to the copyright glyph. So a literal `(c)` anywhere inside an `h2`–`h6` on the marketing site paints as `©`. Body copy is unaffected because it uses Inter, whose subset has no such ligature — which is why this only ever shows up in headings. This branch adds a `legal-prose` utility (`font-variant-ligatures: no-common-ligatures`) in `apps/www/styles/globals.css` and applies it to two pages: - the new **Open Authorization Integration Addendum** page (heading `#### (c) Security.`), and - the **Master Partner Program Agreement** page, where the `#### (b) Such indemnity …` heading in section 17.1 contains `… ; or (c) replace the Covered Materials …` about 600 characters into the line. That page was **already published**, and rendered "or © replace the Covered Materials" in production. The MPPA change is one word — `className="prose"` → `className="prose legal-prose"`. **No legal text was modified**: no HTML entities, no zero-width characters, no rewording, no re-hyphenation. The DOM still holds `U+0028 U+0063 U+0029`; only the font's shaping is suppressed. Verified in Chromium against the real heading text and the same two font subsets `next/font` serves: the `(c)` run measures **15.36px** before the fix (a single `©` glyph) and **22.05px** after (three literal glyphs), against a 23.30px control for the `(b)` in the same heading. All 17 `.mdx` files under `apps/www/data/legal/` were swept for `(c)` and the other Manrope `liga` input sequences (`--`, `->`, `<-`, `(>)`, `<3`) on heading lines. The only two hits are the two pages fixed above; nothing else needs the utility today. (Headings do contain `ff`/`fi`/`fl`/`tt` — those ligatures are ordinary typography and are intentionally left alone.) **For future legal pages:** because the cause is the heading font's default ligature rather than anything about these documents, any new legal page whose source has `(c)` in a heading will need `legal-prose` on its prose container too. **One side effect worth flagging:** `no-common-ligatures` is blunt, so on those two pages it also suppresses the ordinary `fi`, `ff` and `tt` ligatures — a sweep of the legal `.mdx` files counts 107 such occurrences in headings (`fi` 83, `ff` 21, `tt` 3, `fl` 0), so the note above about leaving them alone holds for the rest of the site rather than for these two pages. That is a deliberate trade-off: correctness of the legal text beats typographic polish on two addendum pages. A narrower alternative exists — `font-feature-settings: "liga" 0` scoped to just the offending ligature, or overriding only the `parenleft_c_parenright` substitution — but it is more fragile and more subset-specific, so push back here if you would rather have that instead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01VtcJJGqw5jL1ESwhs8DGCu --------- Co-authored-by: Claude <noreply@anthropic.com>
supabase.com
Overview
Refer to the Development Guide to learn how to run this site locally.
To get started copy the example env file using cp .env.local.example .env.local.
Best practices
Images
- Resize images: All new images should be resized to only the maximum resolution needed when rendering on the frontend. Don't upload images larger than what will be displayed.
- Compress images: All new images should be compressed before committing. Use tools like Clop or ImageOptim to reduce file size without noticeable quality loss.
- Image locations: Store blog post images in
apps/www/public/images/blog/. Event images go inapps/www/public/images/events/.
OG image generation
Open Graph (OG) images for social sharing are handled differently across content types:
- Blog posts: Use static images via
imgSocialandimgThumbfields (see Blog posts section below) - Events: Use dynamic generation via Edge Function (with optional
og_imageoverride) - Customer stories: Use dynamic generation via Edge Function (no static option)
The og-images Edge Function (supabase/functions/og-images/) automatically generates OG images for events and customer stories. It's deployed via .github/workflows/og_images.yml when changes are made to the function code.
Development: In local development, the function runs at http://127.0.0.1:54321/functions/v1/og-images. Ensure Supabase is running locally (supabase start).
Content frontmatter image fields
Different content types use different image field conventions:
Blog posts
Blog posts support two image fields in their frontmatter:
imgSocial: Used for Open Graph and social media sharing (X, LinkedIn, etc.). These images should include text overlays since they appear standalone in social feeds without accompanying text.imgThumb: Used for internal thumbnails displayed on the blog listing pages and featured posts. These images don't need text overlays since they're always displayed alongside the post title and description.
This naming convention was introduced to replace the previously confusing thumb and og fields, which were often mixed up. The new names clearly indicate:
imgSocial: Purpose-built for social media sharing (needs text overlays)imgThumb: Optimized for site display (clean, no text overlays)
This separation allows you to optimize images for their specific use case while maintaining a clear, unambiguous naming convention.
Image path format
Always use relative paths (just the filename or subfolder/filename). The /images/blog/ prefix is added automatically by the code.
- ✅ Correct:
imgSocial: my-post/og.pngorimgSocial: og.png - ❌ Wrong:
imgSocial: /images/blog/my-post/og.png(creates double prefix)
Image fallback behavior
For site display (what visitors see):
- Priority 1:
imgThumb - Priority 2:
imgSocial(ifimgThumbis missing) - Priority 3:
/images/blog/blog-placeholder.png(if both missing)
For social sharing (Open Graph meta tags):
- Priority 1:
imgSocial - Priority 2:
imgThumb(ifimgSocialis missing) - Priority 3: No fallback (undefined)
What happens if fields are not provided?
- If only
imgThumbis provided: Site displays the image correctly, social sharing usesimgThumbas fallback - If only
imgSocialis provided: Social sharing uses it, site display uses it as fallback - If neither is provided: Site shows placeholder image, social sharing has no image
- Best practice: Provide both fields for optimal display and social sharing
Example
---
title: 'My Blog Post'
imgSocial: 2025-01-01-my-post/og.png # Relative path - with text overlay for social sharing
imgThumb: 2025-01-01-my-post/thumb.png # Relative path - without text, clean image
---
The images would be stored at:
apps/www/public/images/blog/2025-01-01-my-post/og.pngapps/www/public/images/blog/2025-01-01-my-post/thumb.png
Or if using the same image for both:
---
title: 'My Blog Post'
imgSocial: my-image.png # Stored at: apps/www/public/images/blog/my-image.png
imgThumb: my-image.png
---
Events
Events use different image fields to avoid confusion with their display patterns:
thumb: Used for event grid item thumbnails (small cards in listing)cover_url: Used for the featured event banner (large display on events page)og_image(optional): Used to override the dynamically generated OG image for social sharing
OG image generation
Events automatically generate Open Graph images using the og-images Supabase Edge Function. The function creates images dynamically based on:
- Event type (conference, hackathon, etc.)
- Title (or
meta_titleif provided) - Description (or
meta_descriptionif provided) - Date (formatted as "DD MMM YYYY" using the event's timezone)
- Duration (if provided)
If you need a custom OG image that differs from the auto-generated one, you can provide an og_image field in the frontmatter. This will override the dynamic generation.
Example:
---
title: 'Supabase Meetup'
thumb: /images/events/2025-01-meetup/thumbnail.png
cover_url: https://external-cdn.com/event-banner.jpg
og_image: /images/events/2025-01-meetup/custom-og.png # Optional override
---
Note: The og_image field is optional. If not provided, OG images are generated automatically via the Edge Function.
Go pages (/go/*)
/go/ is a system for building standalone campaign landing pages (lead generation, legal, thank-you flows). The name is intentionally generic — these pages are typically linked from ads, emails, or partner campaigns and are not part of the main site navigation.
Pages are defined as TypeScript objects (not MDX files) and validated against Zod schemas at build time.
Parts
| Location | Purpose |
|---|---|
apps/www/_go/ |
Page definitions. Each file exports a page object. index.tsx registers all pages. |
apps/www/app/go/[slug]/page.tsx |
App Router route — renders the page for a given slug, handles 404s and metadata. |
apps/www/components/Go/GoPageRenderer.tsx |
www-specific wrapper — adds the Supabase logo header and footer, registers custom section renderers. |
packages/marketing/src/go/ |
Framework-agnostic core: schemas, section components, templates, form server action. |
packages/marketing/src/crm/ |
CRM client abstraction (HubSpot + Customer.io) used by the form server action. |
Page structure
Each page specifies a template which determines its top-level layout:
lead-gen— hero + arbitrary sections (form, metrics, feature grid, tweets, social proof, etc.)thank-you— hero + sections + confetti animationlegal— hero + table-of-contents sidebar + markdown body
Pages are arrays of typed section objects. The SectionRenderer in packages/marketing dispatches each section to the right component based on its type field.
Custom renderers
The marketing package doesn't know about topTweets data or the Pages Router basePath, so the tweets section type has no default renderer. GoPageRenderer.tsx in www registers TweetsSection as a custom renderer for that type. This is the extension point for any section that requires www-specific dependencies.
Adding a new page
- Create a new file in
apps/www/_go/<category>/my-page.tsxexporting a page object. - Register it in
apps/www/_go/index.tsx. - The page will be available at
/go/<slug>automatically via static generation.
Customer Stories (Case Studies)
Customer stories are defined in MDX files (apps/www/_customers/*.mdx) and use a different approach:
- No
og_imagefield: Customer stories do NOT use static OG images - Dynamic OG generation: All customer story OG images are automatically generated using the
og-imagesSupabase Edge Function - The function creates images based on the customer
slugandtitle(ormeta_titleif provided)
Do not include an og_image field in customer story frontmatter. It will be ignored. OG images are always generated dynamically.
Example (in apps/www/_customers/company-abc.mdx):
---
name: Company ABC
title: Company ABC built their platform with Supabase
# DO NOT include og_image - it's generated automatically
logo: /images/customers/logos/company-abc.png
---
Legacy Case Studies (in data/CustomerStories.ts):
imgUrl: Path to the case study image in the source data- This gets mapped to
imgThumbwhen rendered viaBlogGridItemcomponent - Case studies only need one image for site display (no separate social sharing image)
Example (in data/CustomerStories.ts):
{
type: 'Customer Story',
title: 'Company ABC built their platform with Supabase',
description: '...',
organization: 'Company ABC',
imgUrl: 'images/customers/logos/company-abc.png', // Full path from public/
logo: '/images/customers/logos/company-abc.png',
url: '/customers/company-abc',
}