Skip to content

Commit

Permalink
Calculation can be based on fontSize or capHeight
Browse files Browse the repository at this point in the history
  • Loading branch information
jesstelford committed Jul 8, 2020
1 parent c9be751 commit b2f49f2
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 43 deletions.
6 changes: 6 additions & 0 deletions .changeset/shaggy-lies-suffer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'capsize': minor
'capsize-site': minor
---

Calculation can be based on fontSize or capHeight
27 changes: 21 additions & 6 deletions packages/capsize/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ interface FontMetrics {
interface CapsizeOptions {
leading?: number;
gap?: number;
capHeight: number;
capHeight?: number;
fontSize?: number;
fontMetrics: FontMetrics;
}

export default function createCss({
leading,
gap,
fontSize,
capHeight,
fontMetrics,
}: CapsizeOptions) {
Expand All @@ -26,8 +28,21 @@ export default function createCss({
);
}

if (typeof capHeight !== 'undefined' && typeof fontSize !== 'undefined') {
throw new Error(
'Only a single calculation basis can be provided. Please pass either `capHeight` OR `fontSize`.',
);
}

const capHeightRatio = fontMetrics.capHeight / fontMetrics.unitsPerEm;
const capSize = capHeight / capHeightRatio;

if (typeof fontSize !== 'undefined') {
capHeight = fontSize * capHeightRatio;
}

if (typeof capHeight !== 'undefined') {
fontSize = capHeight / capHeightRatio;
}

const absoluteDescent = Math.abs(fontMetrics.descent);

Expand All @@ -37,7 +52,7 @@ export default function createCss({
const contentArea = fontMetrics.ascent + absoluteDescent;
const lineHeight = contentArea + fontMetrics.lineGap;
const lineHeightScale = lineHeight / fontMetrics.unitsPerEm;
const lineHeightNormal = lineHeightScale * capSize;
const lineHeightNormal = lineHeightScale * fontSize;

const hasSpecifiedLineHeight =
typeof leading !== 'undefined' || typeof gap !== 'undefined';
Expand All @@ -52,19 +67,19 @@ export default function createCss({

// Basekick
const descenderTransformOffsetForLeading = hasSpecifiedLineHeight
? offset / 2 / capSize
? offset / 2 / fontSize
: 0;
const descenderTransform = descentRatio - descenderTransformOffsetForLeading;

// Top Crop
const distanceTopOffsetForLeading = hasSpecifiedLineHeight
? offset / capSize
? offset / fontSize
: 0;
const distanceTop =
ascentRatio - capHeightRatio + descentRatio - distanceTopOffsetForLeading;

return {
fontSize: `${capSize}px`,
fontSize: `${fontSize}px`,
...(hasSpecifiedLineHeight && { lineHeight: `${specifiedLineHeight}px` }),
transform: `translateY(${descenderTransform}em)`,
paddingTop: '0.05px',
Expand Down
33 changes: 32 additions & 1 deletion site/src/components/AppStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,35 @@ interface Font {
}

type LineHeightStyle = 'gap' | 'leading';
type CalculationBasis = 'capheight' | 'fontsize';

interface AppState {
capHeight: number;
fontSize: number;
leading: number;
lineGap: number;
gridStep: number;
lineHeightStyle: LineHeightStyle;
calculationBasis: CalculationBasis;
metrics: FontMetrics;
selectedFont: Font;
focusedField: 'grid' | 'capheight' | 'leading' | 'linegap' | null;
focusedField:
| 'grid'
| 'capheight'
| 'fontsize'
| 'leading'
| 'linegap'
| null;
scaleLeading: boolean;
}

type Action =
| { type: 'UPDATE_CAPHEIGHT'; capHeight: number; leading: number }
| { type: 'UPDATE_FONTSIZE'; fontSize: number }
| { type: 'UPDATE_LEADING'; value: number }
| { type: 'UPDATE_LINEGAP'; value: number }
| { type: 'UPDATE_LINEHEIGHT_STYLE'; value: LineHeightStyle }
| { type: 'UPDATE_CALCULATIONBASIS'; value: CalculationBasis }
| { type: 'UPDATE_GRID_STEP'; value: number }
| { type: 'FIELD_FOCUS'; value: AppState['focusedField'] }
| { type: 'FIELD_BLUR' }
Expand All @@ -65,6 +76,16 @@ function reducer(state: AppState, action: Action): AppState {
};
}

case 'UPDATE_FONTSIZE': {
return {
...state,
fontSize: action.fontSize,
leading: state.scaleLeading
? Math.round((state.leading / state.fontSize) * action.fontSize)
: state.leading,
};
}

case 'UPDATE_LEADING': {
return {
...state,
Expand Down Expand Up @@ -93,6 +114,14 @@ function reducer(state: AppState, action: Action): AppState {
};
}

case 'UPDATE_CALCULATIONBASIS': {
return {
...state,
calculationBasis: action.value,
focusedField: action.value === 'capheight' ? 'capHeight' : 'fontSize',
};
}

case 'UPDATE_FONT': {
return {
...state,
Expand Down Expand Up @@ -139,10 +168,12 @@ const initialFontSize = 48;
const intialState: AppState = {
metrics: robotoMetrics,
capHeight: initialFontSize,
fontSize: initialFontSize,
leading: Math.round(initialFontSize * 1.5),
lineGap: 24,
gridStep: 4,
lineHeightStyle: 'gap',
calculationBasis: 'capheight',
selectedFont: roboto,
focusedField: null,
scaleLeading: true,
Expand Down
120 changes: 97 additions & 23 deletions site/src/components/CapSizeSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,11 @@ const CapSizeSelector = () => {
const {
leading,
capHeight,
fontSize,
scaleLeading,
lineGap,
lineHeightStyle,
calculationBasis,
gridStep,
} = state;

Expand All @@ -185,7 +187,7 @@ const CapSizeSelector = () => {
min={0}
max={10}
value={gridStep}
onChange={(newStep) =>
onChange={newStep =>
dispatch({ type: 'UPDATE_GRID_STEP', value: newStep })
}
onFocus={() => dispatch({ type: 'FIELD_FOCUS', value: 'grid' })}
Expand Down Expand Up @@ -226,23 +228,95 @@ const CapSizeSelector = () => {
</Box>

<Box>
<Setting
name="capHeight"
label="Cap Height"
gridStep={useGrid ? gridStep : undefined}
min={10}
max={200}
value={capHeight}
onChange={(newValue) =>
dispatch({
type: 'UPDATE_CAPHEIGHT',
capHeight: newValue,
leading,
})
}
onFocus={() => dispatch({ type: 'FIELD_FOCUS', value: 'capheight' })}
onBlur={() => dispatch({ type: 'FIELD_BLUR' })}
/>
<Stack isInline alignItems="center" spacing={5}>
<Box w={[130, 150]} flexShrink={0}>
<SettingLabel
id="calculationBasisType"
htmlFor="calculationBasisType"
>
Calculation basis
</SettingLabel>
</Box>

<Box w="100%">
<RadioButtonGroup
id="calculationBasisType"
value={calculationBasis}
onChange={style =>
dispatch({
type: 'UPDATE_CALCULATIONBASIS',
value: style as typeof calculationBasis,
})
}
isInline
>
<CustomRadio
isChecked={calculationBasis === 'capheight'}
value="capheight"
onFocus={() =>
dispatch({ type: 'FIELD_FOCUS', value: 'capheight' })
}
onBlur={() => dispatch({ type: 'FIELD_BLUR' })}
>
Cap Height
</CustomRadio>
<CustomRadio
isChecked={calculationBasis === 'fontsize'}
value="fontsize"
onFocus={() =>
dispatch({ type: 'FIELD_FOCUS', value: 'fontSize' })
}
onBlur={() => dispatch({ type: 'FIELD_BLUR' })}
>
Font Size
</CustomRadio>
</RadioButtonGroup>
</Box>
</Stack>
</Box>

<Box pos="relative" h={16} margin={-2} overflow="hidden">
<Mask hide={calculationBasis === 'fontsize'}>
<Setting
name="capHeight"
label="Cap Height"
gridStep={useGrid ? gridStep : undefined}
min={10}
max={200}
value={capHeight}
active={calculationBasis === 'capheight'}
onChange={newValue =>
dispatch({
type: 'UPDATE_CAPHEIGHT',
capHeight: newValue,
leading,
})
}
onFocus={() =>
dispatch({ type: 'FIELD_FOCUS', value: 'capheight' })
}
onBlur={() => dispatch({ type: 'FIELD_BLUR' })}
/>
</Mask>

<Mask hide={calculationBasis === 'capheight'}>
<Setting
name="fontSize"
label="Font Size"
min={1}
max={200}
value={fontSize}
active={calculationBasis === 'fontsize'}
onChange={newValue =>
dispatch({
type: 'UPDATE_FONTSIZE',
fontSize: newValue,
})
}
onFocus={() => dispatch({ type: 'FIELD_FOCUS', value: 'fontsize' })}
onBlur={() => dispatch({ type: 'FIELD_BLUR' })}
/>
</Mask>
</Box>

<Box>
Expand All @@ -257,7 +331,7 @@ const CapSizeSelector = () => {
<RadioButtonGroup
id="lineHeightType"
value={lineHeightStyle}
onChange={(style) =>
onChange={style =>
dispatch({
type: 'UPDATE_LINEHEIGHT_STYLE',
value: style as typeof lineHeightStyle,
Expand Down Expand Up @@ -296,10 +370,10 @@ const CapSizeSelector = () => {
name="leading"
label="Leading"
gridStep={useGrid ? gridStep : undefined}
min={capHeight}
max={capHeight * 2}
min={calculationBasis === 'capheight' ? capHeight : fontSize}
max={(calculationBasis === 'capheight' ? capHeight : fontSize) * 2}
value={leading}
onChange={(newValue) =>
onChange={newValue =>
dispatch({
type: 'UPDATE_LEADING',
value: newValue,
Expand Down Expand Up @@ -335,7 +409,7 @@ const CapSizeSelector = () => {
label="Line Gap"
gridStep={useGrid ? gridStep : undefined}
value={lineGap}
onChange={(newValue) =>
onChange={newValue =>
dispatch({
type: 'UPDATE_LINEGAP',
value: newValue,
Expand Down
13 changes: 11 additions & 2 deletions site/src/components/OutputCSS.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,20 @@ const editorTheme = ({
const OutputCSS = () => {
const { state } = useAppState();

const { leading, capHeight, metrics, lineHeightStyle, lineGap } = state;
const {
leading,
capHeight,
fontSize,
calculationBasis,
metrics,
lineHeightStyle,
lineGap,
} = state;
const { colors } = useTheme();

const capsizeStyles = capsize({
capHeight,
capHeight: calculationBasis === 'capheight' ? capHeight : undefined,
fontSize: calculationBasis === 'fontsize' ? fontSize : undefined,
leading: lineHeightStyle === 'leading' ? leading : undefined,
gap: lineHeightStyle === 'gap' ? lineGap : undefined,
fontMetrics: metrics,
Expand Down
Loading

0 comments on commit b2f49f2

Please sign in to comment.