Skip to content
Merged
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
34 changes: 18 additions & 16 deletions apps/sim/lib/branding/wordmark.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import { readFileSync } from 'node:fs'
import path from 'node:path'
import { describe, expect, it } from 'vitest'
import { EMAIL_WORDMARK_SIZE, WORDMARK_PATHS, WORDMARK_VIEW_BOX } from '@/lib/branding/wordmark'

/** Email clients do no responsive image selection, so the asset carries retina detail itself. */
const MIN_RETINA_SCALE = 2
import {
EMAIL_WORDMARK_CANVAS,
EMAIL_WORDMARK_SIZE,
WORDMARK_PATHS,
WORDMARK_VIEW_BOX,
} from '@/lib/branding/wordmark'

const WORDMARK_PNG = path.join(
import.meta.dirname,
Expand All @@ -27,27 +29,27 @@ function readPngSize(file: string): { width: number; height: number } {
}

describe('email wordmark asset', () => {
it('out-resolves the box the email header renders it in', () => {
const { width, height } = readPngSize(WORDMARK_PNG)
expect(width).toBeGreaterThanOrEqual(EMAIL_WORDMARK_SIZE.width * MIN_RETINA_SCALE)
expect(height).toBeGreaterThanOrEqual(EMAIL_WORDMARK_SIZE.height * MIN_RETINA_SCALE)
it('keeps the canvas every email already sent was measured against', () => {
expect(readPngSize(WORDMARK_PNG)).toEqual({
width: EMAIL_WORDMARK_CANVAS.width,
height: EMAIL_WORDMARK_CANVAS.height,
})
})

it('is committed at the aspect ratio the header displays it at', () => {
const { width, height } = readPngSize(WORDMARK_PNG)
const assetAspect = width / height
it('renders undistorted in the box the header displays it in', () => {
const boxAspect = EMAIL_WORDMARK_SIZE.width / EMAIL_WORDMARK_SIZE.height
expect(Math.abs(assetAspect - boxAspect) / boxAspect).toBeLessThan(0.01)
const canvasAspect = EMAIL_WORDMARK_CANVAS.width / EMAIL_WORDMARK_CANVAS.height
expect(Math.abs(boxAspect - canvasAspect) / canvasAspect).toBeLessThan(0.01)
})

it('renders the mark at the brand outlines aspect ratio, within a rounding pixel', () => {
const boxAspect = EMAIL_WORDMARK_SIZE.width / EMAIL_WORDMARK_SIZE.height
const viewBoxAspect = WORDMARK_VIEW_BOX.width / WORDMARK_VIEW_BOX.height
expect(Math.abs(boxAspect - viewBoxAspect) / viewBoxAspect).toBeLessThan(0.01)
it('out-resolves that box, since email clients do no responsive selection', () => {
expect(EMAIL_WORDMARK_CANVAS.width).toBeGreaterThanOrEqual(EMAIL_WORDMARK_SIZE.width * 2)
expect(EMAIL_WORDMARK_CANVAS.height).toBeGreaterThanOrEqual(EMAIL_WORDMARK_SIZE.height * 2)
})

it('carries every glyph of the logotype', () => {
expect(WORDMARK_PATHS).toHaveLength(4)
for (const d of WORDMARK_PATHS) expect(d.startsWith('M')).toBe(true)
expect(WORDMARK_VIEW_BOX.width).toBeGreaterThan(WORDMARK_VIEW_BOX.height)
})
})
26 changes: 19 additions & 7 deletions apps/sim/lib/branding/wordmark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,29 @@ export const WORDMARK_PATHS: readonly string[] = [
'M179.43 212H149.16V49.76C153.76 51.91 158.88 53.12 164.29 53.12C169.7 53.12 174.83 51.91 179.43 49.76V212Z',
]

/**
* Canvas of `public/brand/color/email/wordmark.png`, which the header renders
* instead of these paths because email clients strip inline SVG.
*
* **These proportions are frozen.** A sent email is immutable — it keeps the
* `width`/`height` it was sent with and refetches this URL forever — so an
* export whose canvas is shaped differently would stretch the mark in every
* message already delivered. Re-export onto this canvas (the outlines centered
* at their own aspect, filled with the email palette's `textBody`) and the same
* file keeps serving old and new mail alike.
*/
export const EMAIL_WORDMARK_CANVAS = { width: 272, height: 164 } as const

/**
* Display box for the email header wordmark.
*
* The landing navbar sizes the mark at its neighbouring text plus 2px above and
* below (18px ink against 14px chip labels). Email body copy is 16px, so the
* mark stands 20px tall; the width follows the view box. Both dimensions are
* pinned because email clients do no responsive image selection.
* mark wants to stand ~20px tall. The canvas insets the outlines, so a 26px box
* renders 20.7px of ink; the width follows {@link EMAIL_WORDMARK_CANVAS}. Both
* dimensions are pinned because email clients do no responsive image selection.
*
* The header renders `public/brand/color/email/wordmark.png` rather than these
* paths, because email clients strip inline SVG. That file is these outlines
* filled with the email palette's `textBody`, rasterized at 4x so the mark
* stays crisp on retina and this box can be retuned without re-exporting it.
* Retuning this box is safe on its own — the canvas is 6x the box, so the mark
* stays crisp, and older mail keeps rendering against its own numbers.
*/
export const EMAIL_WORDMARK_SIZE = { width: 42, height: 20 } as const
export const EMAIL_WORDMARK_SIZE = { width: 43, height: 26 } as const
Binary file modified apps/sim/public/brand/color/email/wordmark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading