diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ebf5c13..1b4016ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## Next +- Add support for defining `fontSizes` [#310](https://github.com/Shopify/restyle/pull/310) by [kaltoft](https://github.com/kaltoft) + ## 2.4.3 - 2023-04-18 - Fixed: clean up the project configuration for RN0.71, aligning some packages to the right version [#271](https://github.com/Shopify/restyle/pull/271) by [kelset](https://github.com/kelset) & [tido64](https://github.com/tido64) diff --git a/README.md b/README.md index c2200b82..c95e4fd4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ +
+ +**_This fork introduces support for defining and referencing font sizes_** + +
+ ![Restyle Image](./Restyle.png)
diff --git a/documentation/docs/fundamentals/font-sizes.md b/documentation/docs/fundamentals/font-sizes.md new file mode 100644 index 00000000..a3395470 --- /dev/null +++ b/documentation/docs/fundamentals/font-sizes.md @@ -0,0 +1,26 @@ +--- +id: fontSizes +title: Font Sizes +--- + +When working with font sizes in a design system, a common pattern is to define a scale that includes a range of sizes, from smaller to larger text. See for example the [Type Scale Tool](https://typescale.com/). + +This scale should preferably not be directly included as values in the theme. Instead, the naming of font sizes in the theme object should be used to assign semantic meaning to the scale, as shown in this example: + +```ts +const theme = createTheme({ + fontSizes: { + title: 34, + h1: 28, + h2: 22, + h3: 20, + body: 16, + }, +}); +``` + +Defining semantic font sizes brings several advantages: + +- It’s clear which font sizes are used and in what contexts throughout the app. +- If changes are made to the font scale (e.g., adjusting the base size or ratios), you only need to update the semantic size mappings instead of manually changing every reference to specific sizes like `body` across the app. +- Themes can easily be swapped at runtime, enabling dynamic adjustments like switching to a larger font scale for accessibility. diff --git a/src/context.tsx b/src/context.tsx index be37f6b1..7278f1a3 100644 --- a/src/context.tsx +++ b/src/context.tsx @@ -4,6 +4,7 @@ import {BaseTheme} from 'types'; export const ThemeContext = React.createContext({ colors: {}, spacing: {}, + fontSizes: {}, }); export const ThemeProvider = ({ diff --git a/src/createText.ts b/src/createText.ts index 51a9eada..3a129424 100644 --- a/src/createText.ts +++ b/src/createText.ts @@ -13,6 +13,7 @@ import { ColorProps, OpacityProps, SpacingProps, + FontSizeProps, TextShadowProps, TypographyProps, VisibleProps, @@ -27,6 +28,7 @@ type BaseTextProps = ColorProps & OpacityProps & VisibleProps & TypographyProps & + FontSizeProps & SpacingProps & LayoutProps & TextShadowProps & diff --git a/src/restyleFunctions.ts b/src/restyleFunctions.ts index 0f698fee..77bfb441 100644 --- a/src/restyleFunctions.ts +++ b/src/restyleFunctions.ts @@ -54,7 +54,6 @@ const spacingPropertiesShorthand = { const typographyProperties = { fontFamily: true, - fontSize: true, fontStyle: true, fontWeight: true, includeFontPadding: true, @@ -199,11 +198,17 @@ export const spacingShorthand = getKeys(spacingPropertiesShorthand).map( }, ); -export const typography = getKeys(typographyProperties).map(property => { - return createRestyleFunction({ - property, - }); -}); +export const typography = [ + ...getKeys(typographyProperties).map(property => { + return createRestyleFunction({ + property, + }); + }), + createRestyleFunction({ + property: 'fontSize', + themeKey: 'fontSizes', + }), +]; export const layout = getKeys(layoutProperties).map(property => { return createRestyleFunction({ @@ -322,6 +327,10 @@ export type SpacingShorthandProps = { >; }; +export interface FontSizeProps { + fontSize?: ResponsiveValue; +} + export type TypographyProps = { [Key in keyof typeof typographyProperties]?: ResponsiveValue< TextStyle[Key], @@ -392,6 +401,7 @@ export type AllProps = BackgroundColorProps & OpacityProps & SpacingProps & SpacingShorthandProps & + FontSizeProps & TypographyProps & LayoutProps & PositionProps & diff --git a/src/test/TestButton.tsx b/src/test/TestButton.tsx index dab0005b..d73af46b 100644 --- a/src/test/TestButton.tsx +++ b/src/test/TestButton.tsx @@ -9,6 +9,12 @@ import composeRestyleFunctions from '../composeRestyleFunctions'; const theme = { colors: {}, spacing: {}, + fontSizes: { + xs: 14, + s: 16, + m: 20, + l: 24, + }, buttonVariants: { defaults: {}, }, diff --git a/src/test/TestContainer.tsx b/src/test/TestContainer.tsx index 958653f0..d51449f6 100644 --- a/src/test/TestContainer.tsx +++ b/src/test/TestContainer.tsx @@ -27,6 +27,12 @@ export const theme = { none: 0, m: 8, }, + fontSizes: { + xs: 14, + s: 16, + m: 20, + l: 24, + }, breakpoints: { phone: 320, tablet: 768, diff --git a/src/test/composeRestyleFunctions.test.ts b/src/test/composeRestyleFunctions.test.ts index 3b97a17d..88fa8786 100644 --- a/src/test/composeRestyleFunctions.test.ts +++ b/src/test/composeRestyleFunctions.test.ts @@ -5,6 +5,12 @@ const theme = { colors: { black: '#111111', }, + fontSizes: { + xs: 14, + s: 16, + m: 20, + l: 24, + }, spacing: { m: 16, }, @@ -20,20 +26,26 @@ describe('composeRestyleFunctions', () => { const restyleFunctions = [ createRestyleFunction({property: 'color', themeKey: 'colors'}), createRestyleFunction({property: 'margin', themeKey: 'spacing'}), + createRestyleFunction({property: 'fontSize', themeKey: 'fontSizes'}), ]; it('composes multiple restyleFunctions into one', () => { const {buildStyle} = composeRestyleFunctions(restyleFunctions); expect( - buildStyle({color: 'black', margin: 'm'}, {theme, dimensions}), + buildStyle( + {color: 'black', margin: 'm', fontSize: 'm'}, + {theme, dimensions}, + ), ).toStrictEqual({ color: '#111111', margin: 16, + fontSize: 20, }); }); it('combines all restyle function input properties into a list', () => { const {properties} = composeRestyleFunctions(restyleFunctions); - expect(properties).toStrictEqual(['color', 'margin']); + + expect(properties).toStrictEqual(['color', 'margin', 'fontSize']); }); }); diff --git a/src/test/createRestyleComponent.test.tsx b/src/test/createRestyleComponent.test.tsx index 9fe1abce..ce06708a 100644 --- a/src/test/createRestyleComponent.test.tsx +++ b/src/test/createRestyleComponent.test.tsx @@ -25,6 +25,12 @@ const theme = { spacing: { s: 8, }, + fontSizes: { + xs: 14, + s: 16, + m: 20, + l: 24, + }, breakpoints: { phone: 0, tablet: 376, diff --git a/src/test/createRestyleFunction.test.ts b/src/test/createRestyleFunction.test.ts index 33f43d6c..1f4ac8be 100644 --- a/src/test/createRestyleFunction.test.ts +++ b/src/test/createRestyleFunction.test.ts @@ -4,6 +4,7 @@ import {RNStyle} from '../types'; const theme = { colors: {}, spacing: {}, + fontSizes: {}, opacities: { invisible: 0, barelyVisible: 0.1, diff --git a/src/test/createText.test.tsx b/src/test/createText.test.tsx index 364834c9..476f1946 100644 --- a/src/test/createText.test.tsx +++ b/src/test/createText.test.tsx @@ -11,6 +11,15 @@ const palette = { grey: '#808080', }; +const fontSizes = { + xs: 12, + s: 14, + m: 18, + l: 20, + xl: 24, + xxl: 48, +}; + const theme = createTheme({ colors: { black: palette.black, @@ -20,17 +29,18 @@ const theme = createTheme({ s: 4, m: 8, }, + fontSizes, textVariants: { defaults: { color: 'black', }, title: { - fontSize: 48, + fontSize: 'xxl', fontWeight: 'bold', color: 'black', }, subtitle: { - fontSize: 16, + fontSize: 'm', color: 'grey', }, }, @@ -65,6 +75,21 @@ describe('createText', () => { ]); }); + it('uses a font size from the theme', () => { + const {root} = render( + + Some text + , + ); + + expect(root.findByType(RNText).props.style).toStrictEqual([ + { + color: palette.black, + fontSize: fontSizes.m, + }, + ]); + }); + it('uses a spacing value from the theme', () => { const {root} = render( @@ -102,7 +127,7 @@ describe('createText', () => { { const variant = createVariant({ themeKey: 'textVariants', defaults: { - fontSize: 10, + fontSize: 'xs', opacity: 0.5, }, }); expect(variant.func({}, {theme, dimensions})).toStrictEqual({ - fontSize: 10, + fontSize: 12, opacity: 0.5, }); }); @@ -101,7 +108,7 @@ describe('createVariant', () => { const variant = createVariant({ themeKey: 'boxVariants', defaults: { - fontSize: 10, + fontSize: 'xl', opacity: 0.5, }, }); @@ -117,7 +124,7 @@ describe('createVariant', () => { const variant = createVariant({ themeKey: 'boxVariants', defaults: { - fontSize: 10, + fontSize: 'xl', opacity: 0.5, }, }); @@ -150,7 +157,7 @@ describe('createVariant', () => { const variant = createVariant({ themeKey: 'textVariants', defaults: { - fontSize: 10, + fontSize: 'xl', opacity: 0.5, }, }); @@ -193,7 +200,7 @@ describe('createVariant', () => { {theme, dimensions: {width: 768, height: 300}}, ), ).toStrictEqual({ - fontSize: 28, + fontSize: 16, margin: 8, fontWeight: 'bold', color: '#EEEEEE', @@ -208,7 +215,7 @@ describe('createVariant', () => { {theme, dimensions: {width: 768, height: 1024}}, ), ).toStrictEqual({ - fontSize: 34, + fontSize: 20, margin: 8, fontWeight: 'bold', color: '#EEEEEE', @@ -223,7 +230,7 @@ describe('createVariant', () => { {theme, dimensions: {width: 375, height: 1024}}, ), ).toStrictEqual({ - fontSize: 22, + fontSize: 14, margin: 8, fontWeight: 'bold', color: '#111111', diff --git a/src/types.ts b/src/types.ts index 2384c9fd..9643a1c6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -38,6 +38,9 @@ export interface KnownBaseTheme { spacing: { [key: string]: number | string; }; + fontSizes: { + [key: string]: number; + }; breakpoints?: { [key: string]: Breakpoint; };