A React component to help with typography by replacing characters and inserting elements to attach custom styling to.
- Widon't (Widow Control): Prevent the last word from appearing alone on the last line with either a non-breaking space or CSS.
- Smart Text Replacement: Replace the easy to type characters with the typographically correct characters for quotes, ellipsis and dashes.
- Text Wrapping: Some typefaces could use extra help with special characters or they deserve special treatment. Wrap various characters in elements to add custom styles. It can wrap ordinal indicator, all-capital words, quotes, parenthesis and ampersands.
All the TypeScript definitions are included.
npm install @seamusleahy/react-typography-helper"
// or
yarn add @seamusleahy/react-typography-helper"
import React from 'react';
import TypographyHelper from 'react-typography-helper';
export const MyArticle = ({title, body}) => (
<article>
<h1><TypographyHelper text={title} widontNonBreakingSpace /></h1>
<div dangerouslySetInnerHTML={{__html: body}} />
</article>
);
The text to display and transform with the type helper.
<TypographyHelper text="The text to transform" />
// Output: The text to transform
Default value: false
Replaces the space between the last two words with a non-breaking space to prevent widowing of the last word.
<TypographyHelper text="The text to transform" widontNonBreakingSpace />
// Output: The text to transform
Default value: false
false
: disabledtrue
: will wrap in a<span class="widont">
- A
ReactElement
(aka, component), will wrap in a clone of theReactElement
Wraps the last two-words in an element for you to add CSS styles to prevent wrapping. This is useful when wanting to use Media Queries to conditionally prevent wrapping.
<TypographyHelper text="The text to transform" wrapWidont />
// Output: The text <span class="widont">to transform</span>
@media (min-width: 400px) {
.widont {
white-space: nowrap;
}
}
If you use a CSS-in-JS library:
const Widont = styled.span`
@media (min-width: 400px) {
white-space: nowrap;
}`;
<TypographyHelper text="The text to transform" wrapWidont={Widont} />
// Output: The text <span class="css-1pybesw">to transform</span>
Default value: Infinity
This allows you to set a length for when the last word is long enough that it doesn’t require widow control. This setting is for both wrapWidont
and widontNonBreakingSpace
.
Default value: Infinity
This allows you to set a length when for when the last two words are long enough that it doesn’t require widow control. This setting is for both wrapWidont
and widontNonBreakingSpace
.
Default value: false
This replaces '
and "
with right and left quo=te marks.
<TypographyHelper text={`"'Em shouted, 'free the flour!'"`} smartQuotes />
// Output: “’Em shouted, ‘free the flour!’”
Cases | Input | Output |
---|---|---|
Double Quotes | Andrew said, "I am tired" | Andrew said, “I am tired” |
Single Quotes | Tawny said, 'just trying my best' | Tawny said, ‘just trying my best’ |
Apostrophe | I shouldn't have more | I shouldn’t have more |
Leading Apostrophe (’til, ’tis, ’tude, ’twas, ’twere, ’em) |
'Twas the night before | ’Twas the night before |
Note: The double quotes are always paired together when replacing with a left and right double quote marks, whereas, single quotes are determined by the characters before and after it.
Default value: false
Replaces three periods (...
) with an ellipsis (…
).
<TypographyHelper text="Today..." smartEllipsis />
// Output: Today…
Note: This will ignore a sequence of four or more periods.
Default value: false
Replaces two-dashes (--
) with an en-dash (–
) and three-dashes (---
) with an em-dash (—).
<TypographyHelper text="Where--what---why" smartDashes />
// Output: Where–what—why
Default value: false
false
: disabledtrue
: will wrap in a<span class="amp">
- A
ReactElement
, will wrap in a clone of theReactElement
Wraps ampersands (&
) in an element for the purpose of custom styling.
<TypographyHelper text="Flour & Yeast" wrapAmpersand />
// Output: Flour <span class="amp">&</span> Yeast
.amp {
letter-spacing: -0.1em;
}
If you use a CSS-in-JS library:
const Amp = styled.span`
letter-spacing: -0.1em;
`;
<TypographyHelper text="Flour & Yeast" wrapAmpersand={Amp} />
// Output: Flour <span class="css-ork2sq">&</span> Yeast
Default value: false
false
: disabledtrue
: will wrap in a<span class="caps">
- A
ReactElement
, will wrap in a clone of theReactElement
Wrap all capital-letter words in an element for the purpose of custom styling.
<TypographyHelper text="Highlights of XYZ" wrapMultipileCapitals />
// Output: Highlights of <span class="caps">XYZ</span>
If you use a CSS-in-JS library:
const Caps = styled.span`
letter-spacing: -0.1em;
`;
<TypographyHelper text="Flour & Yeast" wrapAmpersand={Caps} />
// Output: Highlights of <span class="css-ork2sq">XYZ</span>
Default value: 2
Configure the minimum length an all capital-letter word has to be in order to be wrapped with wrapMultipileCapitals
.
Default value: false
false
: disabledtrue
: will wrap in a<span class="ords">
- A
ReactElement
, will wrap in a clone of theReactElement
Wrap the ordinal indicator (-st, -nd, -rd, -th) in an element for the purpose of custom styling.
<TypographyHelper text="The 23rd of May" wrapOrdinalIndicator />
// Output: The 23<span class="ords">rd</span> of May
If you use a CSS-in-JS library:
const OrdinalIndicator = styled.span`
letter-spacing: -0.1em;
`;
<TypographyHelpertext="The 23rd of May" wrapOrdinalIndicator={OrdinalIndicator} />
// Output:The 23<span class="css-ork2sq">rd</span> of May
Default value: false
false
: disabledtrue
: will wrap in:{ leftDouble: <span className="left-double-quote" />, rightDouble: <span className="right-double-quote" />, leftSingle: <span className="left-single-quote" />, rightSingle: <span className="right-single-quote" />, double: <span className="double-quote" />, single: <span className="single-quote" />, apostrophe: <span className="apostrophe" />, }
- A
ReactElement
will wrap each quote character in a clone of theReactElement
- A
WrapQuoteElements
will wrap each corresponding quote character in a clone of the correspondingReactElement
:{ leftDouble: ReactElement, rightDouble: ReactElement, leftSingle: ReactElement, rightSingle: ReactElement, double: ReactElement, single: ReactElement, apostrophe: ReactElement, }
<TypographyHelper text="“’Em shouted, ‘free the flour!’”" wrapQuotes />
// Output: <span class="left-double-quote">“</span><span class="apostrophe">’</span>Em shouted, <span class="left-single-quote">‘</span>free the flour!<span class="right-single-quote">’</span><span class="right-double-quote">”</span>
Using a single element:
<TypographyHelper text="“’Em shouted, ‘free the flour!’”" wrapQuotes={<i />} />
// Output: <i>“</i><i>’</i>Em shouted, <i>‘</i>free the flour!<i>’</i><i>”</i>
Using a WrapQuoteElements
:
<TypographyHelper
text="“’Em shouted, ‘free the flour!’”"
wrapQuotes={{
leftDouble: <span className="ldq" />,
rightDouble: <span className="rdq" />,
leftSingle:<span className="lsq" />,
rightSingle:<span className="rsq" />,
double: <span className="dq" />,
single: <span className="sq" />,
apostrophe: <span className="a" />,
}}
/>
// Output: <span class="ldq">“</span><span class="a">’</span>Em shouted, <span class="lsq">‘</span>free the flour!<span class="rsq">’</span><span class="rdq">”</span>
Note: apostrophe
and rightSingle
both match the same character so the characters around James’ around it are used to inform which of the two it is. It isn’t perfect. Words that end in an apostrophe are matched as rightSingle
: James’ bicycle.
Default value: false
false
: disabledtrue
: will wrap in:{ leftParanthesis: <span className="left-paren" />, rightParanthesis: <span className="right-paren" />, leftSquareParanthesis: <span className="left-square-paren" />, rightSquareParanthesis: <span className="right-square-paren" />, }
- A
ReactElement
will wrap each quote character in a clone of theReactElement
- A
WrapParanthesisElements
will wrap each corresponding quote character in a clone of the correspondingReactElement
:{ leftParanthesis: ReactElement, rightParanthesis: ReactElement, leftSquareParanthesis: ReactElement, rightSquareParanthesis: ReactElement, }
<TypographyHelper text="(Yes) [No]" wrapParanthesis />
// Output: <span class="left-paren">(</span>Yes<span class="right-paren">)</span> <span class="left-square-paren">[</span>No<span class="right-square-paren">]</span>
Using a single element:
<TypographyHelper text="(Yes) [No]" wrapParanthesis={<b />} />
// Output: <b>(</b>Yes<b>)</b> <b>[</b>No<b>]</b>
Using a WrapParanthesisElements
:
<TypographyHelper
text="(Yes) [No]"
wrapParanthesis={{
leftParanthesis: <span className="lp" />,
rightParanthesis: <span className="rp" />,
leftSquareParanthesis: <span className="lsp" />,
rightSquareParanthesis: <span className="rsp" />,
}}
/>
// Output: <span class="lp">(</span>Yes<span class="rp">)</span> <span class="lsp">[</span>No<span class="rsp">]</span>
Default value: true
When true
, the component will only update the output when the text
has changed - another way to put it is that it caches the results. It is leveraging React’s shouldComponentUpdate
life cycle hook to accomplish this.
This assumes the formatting props are not changing during run-time. This potentially could cause issues when formatting props are being changed during development while using a hot-module-reloading; in those cases, the formatting would not change until after a hard refresh.