diff --git a/.changeset/slow-wolves-notice.md b/.changeset/slow-wolves-notice.md new file mode 100644 index 0000000..1a5e276 --- /dev/null +++ b/.changeset/slow-wolves-notice.md @@ -0,0 +1,5 @@ +--- +'@tokens-studio/sd-transforms': patch +--- + +Refined CSS font name processing: Enhanced escapeApostrophes for accurate apostrophe handling in font names. Updated quoteWrapWhitespacedFont for smart quoting and escaping in multi-word fonts. Ensures better CSS font family compatibility. diff --git a/src/css/transformTypography.ts b/src/css/transformTypography.ts index d23429e..4981c4b 100644 --- a/src/css/transformTypography.ts +++ b/src/css/transformTypography.ts @@ -16,8 +16,17 @@ export function isCommaSeparated(value: string): boolean { return value.includes(','); } +export function escapeApostrophes(value: string): string { + return value.replace(/'/g, "\\'"); +} + function quoteWrapWhitespacedFont(fontString: string) { - return hasWhiteSpace(fontString) && !isAlreadyQuoted(fontString) ? `'${fontString}'` : fontString; + let fontName = fontString.trim(); + const isQuoted = isAlreadyQuoted(fontName); + if (!isQuoted) { + fontName = escapeApostrophes(fontName); + } + return hasWhiteSpace(fontName) && !isQuoted ? `'${fontName}'` : fontName; } export function processFontFamily(fontFamily: string | undefined) { diff --git a/test/integration/sd-transforms.test.ts b/test/integration/sd-transforms.test.ts index 9dd666e..2c3c4e1 100644 --- a/test/integration/sd-transforms.test.ts +++ b/test/integration/sd-transforms.test.ts @@ -71,7 +71,7 @@ describe('sd-transforms smoke tests', () => { --sdFontWeightsBodyRegular: 400; --sdFontSizesH6: 16px; --sdFontSizesBody: 16px; - --sdHeading6: 700 16px/1 Arial; + --sdHeading6: 700 16px/1 'Arial Black', 'Suisse Int\\'l', sans-serif; --sdShadowBlur: 10px; --sdShadow: inset 0 4px 10px 0 rgba(0,0,0,0.4); --sdBorderWidth: 5px; @@ -115,7 +115,7 @@ describe('sd-transforms smoke tests', () => { --sd-font-weights-body-regular: 400; --sd-font-sizes-h6: 16px; --sd-font-sizes-body: 16px; - --sd-heading-6: 700 16px/1 Arial; + --sd-heading-6: 700 16px/1 'Arial Black', 'Suisse Int\\'l', sans-serif; --sd-shadow-blur: 10px; --sd-shadow: inset 0 4px 10px 0 rgba(0,0,0,0.4); --sd-border-width: 5px; diff --git a/test/integration/tokens/sd-transforms.tokens.json b/test/integration/tokens/sd-transforms.tokens.json index 70c9d4b..c0fdf71 100644 --- a/test/integration/tokens/sd-transforms.tokens.json +++ b/test/integration/tokens/sd-transforms.tokens.json @@ -146,7 +146,7 @@ "value": { "fontSize": "{fontSizes.h6}", "fontWeight": "700", - "fontFamily": "Arial", + "fontFamily": "Arial Black, Suisse Int'l, sans-serif", "lineHeight": "1" }, "type": "typography" diff --git a/test/spec/css/transformFontFamilies.spec.ts b/test/spec/css/transformFontFamilies.spec.ts index 7b81557..975c8b3 100644 --- a/test/spec/css/transformFontFamilies.spec.ts +++ b/test/spec/css/transformFontFamilies.spec.ts @@ -1,5 +1,5 @@ import { expect } from '@esm-bundle/chai'; -import { processFontFamily } from '../../../src/css/transformTypography.js'; +import { processFontFamily, escapeApostrophes } from '../../../src/css/transformTypography.js'; describe('process font family', () => { it('transforms font-family to have single quotes around multi-word font-families', () => { @@ -9,8 +9,16 @@ describe('process font family', () => { expect(processFontFamily('Arial Black, Times New Roman, Foo, sans-serif')).to.equal( `'Arial Black', 'Times New Roman', Foo, sans-serif`, ); - expect(processFontFamily(`'Arial Black', Times New Roman, Foo, sans-serif`)).to.equal( - `'Arial Black', 'Times New Roman', Foo, sans-serif`, - ); + expect( + processFontFamily(`'Arial Black', Times New Roman, Suisse Int'l, Foo, sans-serif`), + ).to.equal(`'Arial Black', 'Times New Roman', 'Suisse Int\\'l', Foo, sans-serif`); + }); +}); + +describe('escape apostrophes', () => { + it('should escape single apostrophes in strings', () => { + expect(escapeApostrophes("Suisse Int'l")).to.equal("Suisse Int\\'l"); + expect(escapeApostrophes("Font's Example")).to.equal("Font\\'s Example"); + expect(escapeApostrophes('NoEscape')).to.equal('NoEscape'); }); });