mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix(emails): re-export the wordmark onto its original canvas (#6651)
#6648 shrank the header wordmark by re-exporting the raster edge-to-edge at 168x80, replacing a 272x164 file whose mark was inset. Same URL, new proportions — so every email already delivered, which keeps the width=68 height=41 it was sent with and refetches that URL forever, now stretches the mark ~20%, and a client still holding the old bytes squashes it ~22% into the new box. That is the squished logo on staging. Re-export onto the original 272x164 canvas instead, with the outlines at their own aspect (the previous asset was itself 3.9% squashed) and the textBody fill. Old mail renders against its own numbers as before; the header takes a 43x26 box for 20.7px of ink. A test pins the canvas, since that shape — not the display size — is what old mail depends on.
This commit is contained in:
@@ -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,
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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 not shown.
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 4.4 KiB |
Reference in New Issue
Block a user