Skip to content

Commit

Permalink
✨ (title) try to fit chart title into a single line (#3173)
Browse files Browse the repository at this point in the history
> [!IMPORTANT]
> Wait for #3211 to be merged.

### Current state

- When exporting to static charts, we adjust the font size to fit the chart title into a single line if possible
    - The font size is decreased by not more than 20% using 0.5px steps
- But we don't currently do this on the web

### Changes

- We re-introduce this technique for charts rendered in an interactive environment (we had removed this for interactive charts in the redesign)
- I think reducing the font size by 20% is quite a lot, so I opted for 15% instead (the old grapher also used 85% of the original font size as a threshold)
- With 85%, the font size gets at most reduced to...
    - for narrow charts: 18px * 0.85 = 15.3px (subtitle font size = 12px)
    - for medium charts: 20px * 0.85 = 17px (subtitle font size = 13px)
    - for larger charts: 24px * 0.85 = 20.4px (subtitle font size = 14px)

The SVG tester complains because we used to decrease by no more than 20% but now use 15%. Some charts look "worse" because of that, but these thresholds are arbitrary, and we have to draw the line somewhere...
  • Loading branch information
sophiamersmann authored Mar 1, 2024
1 parent 9c4daeb commit 6979a0b
Showing 1 changed file with 43 additions and 50 deletions.
93 changes: 43 additions & 50 deletions packages/@ourworldindata/grapher/src/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,54 @@ export class Header<
return this.logo ? this.logo.height : 0
}

@computed get titleFontSize(): number {
if (this.useBaseFontSize) {
return (22 / BASE_FONT_SIZE) * this.baseFontSize
}
return this.manager.isNarrow ? 18 : this.manager.isMedium ? 20 : 24
@computed get titleFontWeight(): number {
return 600
}

@computed get titleLineHeight(): number {
return this.manager.isSmall ? 1.1 : 1.2
}

@computed get title(): TextWrap {
const { logoWidth } = this
return new TextWrap({
maxWidth: this.maxWidth - logoWidth - 24,
fontWeight: 600,
lineHeight: this.titleLineHeight,
fontSize: this.titleFontSize,
text: this.titleText,
})
const makeTitle = (fontSize: number): TextWrap =>
new TextWrap({
text: this.titleText,
maxWidth: this.maxWidth - this.logoWidth - 24,
fontWeight: this.titleFontWeight,
lineHeight: this.titleLineHeight,
fontSize,
})

const initialFontSize = this.useBaseFontSize
? (22 / BASE_FONT_SIZE) * this.baseFontSize
: this.manager.isNarrow
? 18
: this.manager.isMedium
? 20
: 24

let title = makeTitle(initialFontSize)

// if the title is already a single line, no need to decrease font size
if (title.lines.length <= 1) return title

const originalLineCount = title.lines.length
// decrease the initial font size by no more than 15% using 0.5px steps
const potentialFontSizes = range(
initialFontSize,
initialFontSize * 0.85,
-0.5
)
// try to fit the title into a single line if possible-- but not if it would make the text too small
for (const fontSize of potentialFontSizes) {
title = makeTitle(fontSize)
const currentLineCount = title.lines.length
if (currentLineCount <= 1 || currentLineCount < originalLineCount)
break
}
// return the title at the new font size: either it now fits into a single line, or
// its size has been reduced so the multi-line title doesn't take up quite that much space
return title
}

@computed get subtitleMarginTop(): number {
Expand Down Expand Up @@ -238,43 +266,8 @@ interface StaticHeaderProps extends HeaderProps {

@observer
export class StaticHeader extends Header<StaticHeaderProps> {
@computed get title(): TextWrap {
const { logoWidth, titleText } = this

const makeTitle = (fontSize: number): TextWrap =>
new TextWrap({
text: titleText,
maxWidth: this.maxWidth - logoWidth - 24,
fontSize,
fontWeight: 600,
lineHeight: this.manager.isStaticAndSmall ? 1.1 : 1.2,
})

// try to fit the title into a single line if possible-- but not if it would make the text too small
const initialFontSize = this.useBaseFontSize
? (22 / BASE_FONT_SIZE) * this.baseFontSize
: 24
let title = makeTitle(initialFontSize)

// if the title is already a single line, no need to decrease font size
if (title.lines.length <= 1) return title

const originalLineCount = title.lines.length
// decrease the initial font size by no more than 20% using 0.5px steps
const potentialFontSizes = range(
initialFontSize,
initialFontSize * 0.8,
-0.5
)
for (const fontSize of potentialFontSizes) {
title = makeTitle(fontSize)
const currentLineCount = title.lines.length
if (currentLineCount <= 1 || currentLineCount < originalLineCount)
break
}
// return the title at the new font size: either it now fits into a single line, or
// its size has been reduced so the multi-line title doesn't take up quite that much space
return title
@computed get titleLineHeight(): number {
return this.manager.isStaticAndSmall ? 1.1 : 1.2
}

@computed get subtitleLineHeight(): number {
Expand Down

0 comments on commit 6979a0b

Please sign in to comment.