Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions contents/founders/registering-trademarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Those first crucial months present bigger problems to worry about - like finding
A word mark is basically just the name of the brand (as written in a specific font, if applicable). In our case, it's the word 'PostHog.' A logo mark is typically pictorial - in our case, the hedgehog logo either alone or combined with a stylized word. The word mark is the most versatile, so that should be your first trademark priority.

<div class="text-center my-8">
<img src="/brand/posthog-logo.svg" class="w-full" />
<img src="/brand/posthog-logo.svg" alt="PostHog" class="w-full" />
</div>

When you decide to register your trademark, you don't need to spend a fortune doing it. With a tactical combination of using the right type of lawyer and doing it yourself, you should be able to get trademarks filed in the US, UK, EU and China for ~$3,000 _in total_.
Expand All @@ -45,4 +45,3 @@ We’ve currently secured our brand name ('PostHog') and word mark in the four t
The phrase 'open source' might denote a public codebase, but a brand name can definitely be trademarked. For example, we want people to [deploy our open source edition](https://github.com/PostHog/posthog) wherever they please, but we don't want them to slap the 'PostHog' name on random products. Besides, if you’re [raising money](https://posthog.com/blog/open-source-business-models) for your startup in the future, IP is a box that investors will want you to check as part of their diligence.

<NewsletterForm />

1 change: 1 addition & 0 deletions contents/handbook/brand/assets.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Each logo below is rendered live from our [`@posthog/brand`](https://github.com/
- On **light backgrounds, prefer the standard logo**, and only reach for the **dark logo** (solid black) when a single-color mark is required.
- Use the **print (4-color)** version only for print or other limited-palette contexts where the gradients can't be reproduced.
- Use the **logomark** on its own only at small sizes — favicons, app icons — where the full lockup won't fit and the overlapping gradients would get too busy.
- Use a **square logomark** for avatars and integrations that require a square image. Its transparent square canvas keeps the logomark's proportions intact — never stretch the standard logomark to fill a square. Use the standard gradient version on light backgrounds, or the light solid-white version on dark backgrounds.
- Use the **stacked** logo for portrait or square placements where the standard (landscape) lockup is too wide.

_Never_ modify the colors in the logomark (for example, don't recolor the hedgehog's face white on a dark background — use the light logo instead).
Expand Down
4 changes: 2 additions & 2 deletions contents/handbook/brand/visual-identity.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Find logos on our [brand assets](/handbook/brand/assets) page.
| Stacked logo | When horizontal space is limited but the full name is important. |
| 4-color logo | Print version, when gradients can't be printed. Only use the 4-color logo for this case. |

Download all variants at [/handbook/company/brand-assets.md](/handbook/company/brand-assets). SVG is always preferred.
Download all variants from [brand assets](/handbook/brand/assets). SVG is always preferred.

### Rules

Expand Down Expand Up @@ -264,4 +264,4 @@ The same light/dark rule applies: if a visualization appears on posthog.com, it

**Annotations and context**

A chart without context can mislead. When publishing a chart – especially in a blog post – annotate significant moments: a feature launch, a spike, a known data anomaly. Readers should understand *why* something happened, not just *that* it happened.
A chart without context can mislead. When publishing a chart – especially in a blog post – annotate significant moments: a feature launch, a spike, a known data anomaly. Readers should understand *why* something happened, not just *that* it happened.
212 changes: 0 additions & 212 deletions contents/handbook/company/brand-assets.md

This file was deleted.

2 changes: 1 addition & 1 deletion contents/handbook/marketing/events.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Our brand is a reflection of us and [how we’re experienced by others](/blog/br

**Words:** Naming products is hard. Same goes for naming events and writing their descriptions. As a prerequisite, read our primer on [writing for developers](/founders/writing-for-developers). Try your best to come up with event names that communicate the 'what?' and will attract the 'who?' And then again ask yourself, "would I attend this?"

**Pictures:** Every event is improved with a flyer or poster that showcases the essence of the experience. We keep a comprehensive list of brand assets and guidelines on the [brand assets page](/handbook/company/brand-assets). Share your assets and we’ll give feedback. Depending on the scale and timing of the event, our team may be able to help with branding as well.
**Pictures:** Every event is improved with a flyer or poster that showcases the essence of the experience. We keep a comprehensive list of brand assets and guidelines on the [brand assets page](/handbook/brand/assets). Share your assets and we’ll give feedback. Depending on the scale and timing of the event, our team may be able to help with branding as well.

### Event recaps

Expand Down
2 changes: 1 addition & 1 deletion contents/handbook/marketing/ownership.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Read [art and branding requests](/handbook/brand/art-requests) first!
<details>
<summary>I need a font, logo, etc.</summary>

See [Logos, brand, hedgehogs](/handbook/company/brand-assets)
See [Logos, brand, hedgehogs](/handbook/brand/assets)
</details>

<details>
Expand Down
4 changes: 2 additions & 2 deletions contents/handbook/marketing/speaker-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ A few principles for building out slides:
* If a slide doesn't support the one true thing you identified in step 3, cut it.
* Speaking of speaker notes, you will save yourself time and head space if you always have notes.

For feedback on design or help with navigating the [PostHog brand assets](https://posthog.com/handbook/company/brand-assets) ([Hoggies](https://www.figma.com/design/I0VKEEjbkKUDSVzFus2Lpu/Hoggies?node-id=2226-55&t=1sj1GezTKuCfaybF-1) included), stop by [\#team-graphics](https://posthog.slack.com/archives/C0AU440KS6P)
For feedback on design or help with navigating the [PostHog brand assets](/handbook/brand/assets) ([Hoggies](https://www.figma.com/design/I0VKEEjbkKUDSVzFus2Lpu/Hoggies?node-id=2226-55&t=1sj1GezTKuCfaybF-1) included), stop by [\#team-graphics](https://posthog.slack.com/archives/C0AU440KS6P)

## **6. Practice out loud. Twice minimum.**

Expand Down Expand Up @@ -130,4 +130,4 @@ If you receive a question that you believe is off-topic or unfitting for the set
* Feb 2026 - <TeamMember name="Michael Matloka"/> - [10 learning from launching an agentic AI product at scale](https://www.figma.com/slides/zMNBsNihNxKyS6z7sSwiRK/10-learnings-from-launching-an-agentic-AI-product-at-scale?t=6hlWStUDlHsITOKo-6)
* Nov 2025 - <TeamMember name="James Hawkins"/> - [How to build a cult](https://drive.google.com/file/d/1BMgl2y817m5t43D6NCr9hstnGO1kKxJy/view)
* Oct 2025 - <TeamMember name="Joshua Snyder"/> - [Code that fixes itself](https://docs.google.com/presentation/d/1sYsmTPugdttQqshPa6yIKQcUu5Rje3-fPMbRGtyrfZg/edit?slide=id.p#slide=id.p)
* Oct 2024 - <TeamMember name="Michael Matloka"/> - [Parsing at the speed of light](http://figma.com/slides/y0rMgEaWbYe0nSpEhWkyY6/Parsing-at-the-speed-of-metal?node-id=1-536&t=uiCktez3IWxAKI8D-0)
* Oct 2024 - <TeamMember name="Michael Matloka"/> - [Parsing at the speed of light](http://figma.com/slides/y0rMgEaWbYe0nSpEhWkyY6/Parsing-at-the-speed-of-metal?node-id=1-536&t=uiCktez3IWxAKI8D-0)
3 changes: 1 addition & 2 deletions contents/library/ops/registering-trademarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Those first crucial months present bigger problems to worry about - like finding
A word mark is basically just the name of the brand (as written in a specific font, if applicable). In our case, it's the word 'PostHog.' A logo mark is typically pictorial - in our case, the hedgehog logo either alone or combined with a stylized word. The word mark is the most versatile, so that should be your first trademark priority.

<div class="text-center my-8">
<img src="/brand/posthog-logo.svg" class="w-full" />
<img src="/brand/posthog-logo.svg" alt="PostHog" class="w-full" />
</div>

When you decide to register your trademark, you don't need to spend a fortune doing it. With a tactical combination of using the right type of lawyer and doing it yourself, you should be able to get trademarks filed in the US, UK, EU and China for ~$3,000 _in total_.
Expand All @@ -44,4 +44,3 @@ We’ve currently secured our brand name ('PostHog') and word mark in the four t
The phrase 'open source' might denote a public codebase, but a brand name can definitely be trademarked. For example, we want people to [deploy our open source edition](https://github.com/PostHog/posthog) wherever they please, but we don't want them to slap the 'PostHog' name on random products. Besides, if you’re [raising money](https://posthog.com/blog/open-source-business-models) for your startup in the future, IP is a box that investors will want you to check as part of their diligence.

_Enjoyed this? Subscribe to our [newsletter](https://newsletter.posthog.com/subscribe) to hear more from us twice a month!_

2 changes: 1 addition & 1 deletion contents/media-contents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ You can find biographies and social links for our founders, [James](/community/p

## PostHog logos

We've created brand assets for you. You don't have to negotiate legal agreements for them. You can find them on our [brand assets](/handbook/company/brand-assets) page.
We've created brand assets for you. You don't have to negotiate legal agreements for them. You can find them on our [brand assets](/handbook/brand/assets) page.

## Founder headshots

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ First, head to the [‘My Apps’ section of your Zapier account](https://zapier

Next, we need to create a Zapier bot in Notion, which will perform the action of updating a database with information whenever an action is defined in PostHog. You’ll need to be a Notion admin to do this.

Start by heading to Notion’s Settings page, then click the Integrations tab and select ‘Develop your own integrations’ to start creating your bot. Here you can follow the instructions to give your bot a name, as well as customize it in other ways. You can even grab the PostHog logo from [our brand assets](/handbook/company/brand-assets) and use it as the icon for your bot!
Start by heading to Notion’s Settings page, then click the Integrations tab and select ‘Develop your own integrations’ to start creating your bot. Here you can follow the instructions to give your bot a name, as well as customize it in other ways. You can even grab the PostHog logo from [our brand assets](/handbook/brand/assets) and use it as the icon for your bot!

![Notion Integrations Page](https://res.cloudinary.com/dmukukwp6/image/upload/v1710055416/posthog.com/contents/images/tutorials/notion/create-integration.png)

Expand Down Expand Up @@ -61,4 +61,4 @@ If the action is specific to a page or on-page selector, this information is als

As an output, we want the workflow to populate the information to a Notion page — so we’ll use Notion’s ‘Write to database’ output. With these options created you can tailor how the information contained in the event is written to your Notion database. Instituting best practices, such as requiring action creators to include descriptions for their actions, will enable you to track all tracked actions within a simple Notion table.

<NewsletterForm />
<NewsletterForm />
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
],
"private": true,
"scripts": {
"prebuild-move": "pnpm generate-brand-assets",
"build-move": "gatsby build && bash ./src/scripts/move-index.sh",
"prebuild": "pnpm generate-brand-assets",
"build": "gatsby build",
"prebuild:minimal": "pnpm generate-brand-assets",
"build:minimal": "GATSBY_MINIMAL=true gatsby build",
"prestart": "pnpm generate-brand-assets",
"start": "./bin/start",
"serve": "gatsby serve",
"format": "prettier --write \"**/*.{html,js,ts,tsx,json,yml,css,scss}\"",
Expand All @@ -29,6 +33,7 @@
"test-redirects": "jest scripts",
"test:bundle": "node --test scripts/bundle/check-eager-graph.test.mjs",
"check-links-post-build": "node scripts/check-links-post-build.js",
"generate-brand-assets": "node scripts/generate-brand-assets.mjs",
"storybook": "start-storybook -s ./static -p 6006",
"build-storybook": "build-storybook",
"postinstall": "patch-package",
Expand Down Expand Up @@ -59,7 +64,7 @@
"@mdx-js/react": "^1.6.22",
"@mdxeditor/editor": "^3.32.3",
"@popperjs/core": "^2.11.2",
"@posthog/brand": "^0.8.0",
"@posthog/brand": "0.8.0",
"@posthog/hedgehog-mode": "^0.0.52",
"@posthog/icons": "0.36.6",
"@radix-ui/react-accordion": "^1.2.3",
Expand Down Expand Up @@ -271,6 +276,7 @@
"puppeteer": "^13.0.1",
"react-dev-utils": "^11.0.4",
"sass-loader": "^12.2.0",
"sharp": "0.34.5",
"storybook-addon-gatsby": "0.0.2",
"storybook-dark-mode": "^1.0.8",
"style-loader": "^3.3.0",
Expand Down
5 changes: 4 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

134 changes: 134 additions & 0 deletions scripts/generate-brand-assets.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import React from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import sharp from 'sharp'
import { Logo } from '@posthog/brand/logo'

// Materialize stable /brand URLs from the canonical package before Gatsby copies static/ to public/.
// package.json lifecycle hooks run this automatically for the standard start and build commands.
const outputDirectory = fileURLToPath(new URL('../static/brand/', import.meta.url))

const scaleDimensions = ([width, height], scale) => [width * scale, height * scale]
const padDimensions = ([width, height], padding) => [width + padding * 2, height + padding * 2]

const getNativeDimensions = (props) => {
const markup = renderToStaticMarkup(React.createElement(Logo, props))
const viewBox = markup.match(/\bviewBox="([^"]+)"/)?.[1]
const dimensions = viewBox?.trim().split(/\s+/).map(Number)

if (dimensions?.length !== 4 || dimensions.some((value) => !Number.isFinite(value))) {
throw new Error(`Unable to read logo viewBox from: ${markup.slice(0, 200)}`)
}

return dimensions.slice(2)
}

const createAsset = ({ name, props, pngScale = 1, padding = 5, paddedSvg = true }) => {
const svg = getNativeDimensions(props)
const png = scaleDimensions(svg, pngScale)
const padded = padDimensions(png, padding)

return {
name,
props,
svg,
png,
png2x: scaleDimensions(png, 2),
padded,
padded2x: scaleDimensions(padded, 2),
paddedSvg,
}
}

const assets = [
createAsset({
name: 'posthog-logo',
props: {},
}),
createAsset({
name: 'posthog-logo-black',
props: { variant: 'mono', color: '#111' },
}),
createAsset({
name: 'posthog-logo-white',
props: { variant: 'mono', color: '#FAFAFA' },
}),
createAsset({
name: 'posthog-logomark',
props: { layout: 'logomark' },
}),
createAsset({
name: 'posthog-logo-stacked',
props: { layout: 'stacked' },
pngScale: 5,
padding: 20,
paddedSvg: false,
}),
]

const replaceRootSvgAttributes = (markup, attributes) =>
markup.replace(/^<svg\b([^>]*)>/, (_match, currentAttributes) => {
const cleanedAttributes = currentAttributes.replace(
/\s(?:aria-hidden|height|role|width|x|y)=(?:"[^"]*"|'[^']*')/g,
''
)
const serializedAttributes = Object.entries(attributes)
.map(([name, value]) => ` ${name}="${value}"`)
.join('')
return `<svg${cleanedAttributes}${serializedAttributes}>`
})

const renderLogo = (props, [width, height]) =>
replaceRootSvgAttributes(renderToStaticMarkup(React.createElement(Logo, props)), { width, height })

const renderPaddedLogo = (props, [contentWidth, contentHeight], [width, height]) => {
const x = (width - contentWidth) / 2
const y = (height - contentHeight) / 2
const innerLogo = replaceRootSvgAttributes(renderToStaticMarkup(React.createElement(Logo, props)), {
x,
y,
width: contentWidth,
height: contentHeight,
})

return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">${innerLogo}</svg>`
}

const writeSvg = (filename, svg) => fs.writeFile(path.join(outputDirectory, filename), `${svg}\n`)

const writePng = (filename, svg, [width, height]) =>
sharp(Buffer.from(svg), { density: 288 })
.resize(width, height, { fit: 'fill' })
.png({ compressionLevel: 9, palette: true })
.toFile(path.join(outputDirectory, filename))

await fs.mkdir(outputDirectory, { recursive: true })

for (const asset of assets) {
const svg = renderLogo(asset.props, asset.svg)
const pngSvg = renderLogo(asset.props, asset.png)
const png2xSvg = renderLogo(asset.props, asset.png2x)
const paddedSvg = renderPaddedLogo(asset.props, asset.png, asset.padded)
const padded2xSvg = renderPaddedLogo(asset.props, asset.png2x, asset.padded2x)

await Promise.all([
writeSvg(`${asset.name}.svg`, svg),
writePng(`${asset.name}.png`, pngSvg, asset.png),
writePng(`${asset.name}@2x.png`, png2xSvg, asset.png2x),
writePng(`${asset.name}-padded.png`, paddedSvg, asset.padded),
writePng(`${asset.name}-padded@2x.png`, padded2xSvg, asset.padded2x),
...(asset.paddedSvg ? [writeSvg(`${asset.name}-padded.svg`, paddedSvg)] : []),
])
}

// Image-only integrations often force avatars into square boxes. Keep the official
// logomark aspect ratio intact and center it on a transparent square canvas instead of stretching it.
const squareLogomarkProps = { layout: 'logomark' }
await writeSvg(
'posthog-logomark-square.svg',
renderPaddedLogo(squareLogomarkProps, getNativeDimensions(squareLogomarkProps), [60, 60])
)

console.log(`Generated ${assets.length} public logo variants in ${path.relative(process.cwd(), outputDirectory)}`)
9 changes: 4 additions & 5 deletions src/components/About/v2/Letterhead.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react'
import Link from 'components/Link'
import { IconXNotTwitter } from 'components/OSIcons'
import Logo from 'components/Logo'
import CloudinaryImage from 'components/CloudinaryImage'
import { useApp } from '../../../context/App'
import { DebugContainerQuery } from 'components/DebugContainerQuery'
Expand All @@ -12,10 +11,10 @@ export const Letterhead = () => {
return (
<div className="not-prose border-b border-primary py-4 flex flex-col gap-2 @sm:flex-row items-center justify-between">
<div>
<Logo
className="inline-block"
variant={siteSettings.theme === 'dark' ? 'mono' : 'gradient'}
color={siteSettings.theme === 'dark' ? 'white' : undefined}
<img
className="inline-block w-auto h-auto max-w-full"
src={siteSettings.theme === 'dark' ? '/brand/posthog-logo-white.svg' : '/brand/posthog-logo.svg'}
alt="PostHog"
/>
</div>
<div className="@sm:hidden uppercase text-xs tracking-wider text-center text-secondary pt-2">
Expand Down
Loading
Loading