gluestack / gluestack-ui

React & React Native Components & Patterns (copy-paste components & patterns crafted with Tailwind CSS (NativeWind))
https://gluestack.io/
MIT License
2.72k stars 120 forks source link

[Android] Heading fontFamily #2009

Open borstessi opened 7 months ago

borstessi commented 7 months ago

Description

On Android the FontFamily of the Heading Component is not applied correctly

CodeSandbox/Snack link

No response

Steps to reproduce

If i install gluestack and eject theme and use expo font to load a font to apply it to the heading - the font doesnt get applied on android.

Works fine on ios.

Workaround with <Text fontFamily="$heading"></Text> work on android as well.

theme-provider.tsx
 const { theme } = useThemeColorMode();

  const [loaded, error] = useFonts({
    Manrope_400Regular,
    Manrope_500Medium,
    Manrope_800ExtraBold,
  });

  if (error) {
    throw new Error("Failed to load fonts");
  }

  if (!loaded) {
    return null;
  }

  return (
    <GluestackUIProvider config={config} colorMode={theme}>
      <PushNotificationsProvider>
        <AuthProvider>
          <SplashScreenProvider>
            <IntroProvider>
              <NavigationProvider />
            </IntroProvider>
          </SplashScreenProvider>
        </AuthProvider>
      </PushNotificationsProvider>

      <StatusBar style={theme === "dark" ? "light" : "dark"} />
    </GluestackUIProvider>
  );
};
config
import { AnimationResolver } from "@gluestack-style/animation-resolver";
import { MotionAnimationDriver } from "@gluestack-style/legend-motion-animation-driver";
import { createComponents, createConfig } from "@gluestack-style/react";
import * as componentsTheme from "./theme";

const colors = {
  primary: {
    light: "#188939",
    main: "#0F5824",
    dark: "#0B3D19",
    contrast: "#FFFFFF",
  },
  secondary: {
    light: "#ECF7DC",
    main: "#ECF7DC",
    dark: "#AFCC85",
    contrast: "#0F5824",
  },
  backgroundLight: {
    light: "#FFFFFF",
    main: "#F5F5F5",
    dark: "#D3D3D3",
    contrast: "#2C3931",
  },
  textLight: {
    light: "#4E6556",
    main: "#3C4E42",
    dark: "#2C3931",
    contrast: "#FFFFFF",
  },
  infoLight: {
    light: "#E0F4FF",
    main: "#8AC6E7",
    dark: "#135175",
    contrast: "#001A2A",
  },
  warningLight: {
    light: "#FFD057",
    main: "#BE8D10",
    dark: "#825E00",
    contrast: "#292210",
  },
  successLight: {
    light: "#94E592",
    main: "#466246",
    dark: "#324532",
    contrast: "#061A06",
  },
  errorLight: {
    light: "#FF9A9A",
    main: "#6F3131",
    dark: "#491E1E",
    contrast: "#2B0F0F",
  },
  primaryDark: {
    light: "#CDFF7E",
    main: "#ACED45",
    dark: "#88BB38",
    contrast: "#13311C",
  },
  secondaryDark: {
    light: "#000",
    main: "#111706",
    dark: "#0C1004",
    contrast: "#ACED45",
  },
  textDark: {
    light: "#8A8A8A",
    main: "#DBDBDB",
    dark: "#FFFFFF",
    contrast: "#fff",
  },
  backgroundDark: {
    light: "#232924",
    main: "#161D17",
    dark: "#0C0C0C",
    contrast: "#fff",
  },
  infoDark: {
    light: "#051925",
    main: "#135175",
    dark: "#186998",
    contrast: "#DEEEF7",
  },
  warningDark: {
    light: "#292210",
    main: "#825E00",
    dark: "#BE8D10",
    contrast: "#FFD057",
  },
  successDark: {
    light: "#081D08",
    main: "#324532",
    dark: "#466246",
    contrast: "#99D698",
  },
  errorDark: {
    light: "#2B0F0F",
    main: "#491E1E",
    dark: "#6F3131",
    contrast: "#FF9A9A",
  },
};

export const gluestackUIConfig = createConfig({
  aliases: {
    bg: "backgroundColor",
    bgColor: "backgroundColor",
    h: "height",
    w: "width",
    p: "padding",
    px: "paddingHorizontal",
    py: "paddingVertical",
    pt: "paddingTop",
    pb: "paddingBottom",
    pr: "paddingRight",
    pl: "paddingLeft",
    m: "margin",
    mx: "marginHorizontal",
    my: "marginVertical",
    mt: "marginTop",
    mb: "marginBottom",
    mr: "marginRight",
    ml: "marginLeft",
    rounded: "borderRadius",
  } as const,
  tokens: {
    colors: {
      primary0: colors.primary.contrast,
      primary50: colors.primary.light,
      primary100: colors.primary.light,
      primary200: colors.primary.light,
      primary300: colors.primary.light,
      primary400: colors.primary.main,
      primary500: colors.primary.main,
      primary600: colors.primary.main,
      primary700: colors.primary.dark,
      primary800: colors.primary.dark,
      primary900: colors.primary.dark,
      primary950: colors.primary.dark,
      primaryDark0: colors.primaryDark.contrast,
      primaryDark50: colors.primaryDark.light,
      primaryDark100: colors.primaryDark.light,
      primaryDark200: colors.primaryDark.light,
      primaryDark300: colors.primaryDark.light,
      primaryDark400: colors.primaryDark.main,
      primaryDark500: colors.primaryDark.main,
      primaryDark600: colors.primaryDark.main,
      primaryDark700: colors.primaryDark.dark,
      primaryDark800: colors.primaryDark.dark,
      primaryDark900: colors.primaryDark.dark,
      primaryDark950: colors.primaryDark.dark,
      secondary0: colors.secondary.contrast,
      secondary50: colors.secondary.light,
      secondary100: colors.secondary.light,
      secondary200: colors.secondary.light,
      secondary300: colors.secondary.light,
      secondary400: colors.secondary.main,
      secondary500: colors.secondary.main,
      secondary600: colors.secondary.main,
      secondary700: colors.secondary.dark,
      secondary800: colors.secondary.dark,
      secondary900: colors.secondary.dark,
      secondary950: colors.secondary.dark,
      secondaryDark0: colors.secondaryDark.contrast,
      secondaryDark50: colors.secondaryDark.light,
      secondaryDark100: colors.secondaryDark.light,
      secondaryDark200: colors.secondaryDark.light,
      secondaryDark300: colors.secondaryDark.light,
      secondaryDark400: colors.secondaryDark.main,
      secondaryDark500: colors.secondaryDark.main,
      secondaryDark600: colors.secondaryDark.main,
      secondaryDark700: colors.secondaryDark.dark,
      secondaryDark800: colors.secondaryDark.dark,
      secondaryDark900: colors.secondaryDark.dark,
      secondaryDark950: colors.secondaryDark.dark,
      textLight0: colors.textLight.contrast,
      textLight50: colors.textLight.light,
      textLight100: colors.textLight.light,
      textLight200: colors.textLight.light,
      textLight300: colors.textLight.light,
      textLight400: colors.textLight.main,
      textLight500: colors.textLight.main,
      textLight600: colors.textLight.main,
      textLight700: colors.textLight.dark,
      textLight800: colors.textLight.dark,
      textLight900: colors.textLight.dark,
      textLight950: colors.textLight.dark,
      textDark0: colors.textDark.contrast,
      textDark50: colors.textDark.light,
      textDark100: colors.textDark.light,
      textDark200: colors.textDark.light,
      textDark300: colors.textDark.light,
      textDark400: colors.textDark.main,
      textDark500: colors.textDark.main,
      textDark600: colors.textDark.main,
      textDark700: colors.textDark.dark,
      textDark800: colors.textDark.dark,
      textDark900: colors.textDark.dark,
      textDark950: colors.textDark.dark,
      borderDark0: "#FCFCFC",
      borderDark50: "#F5F5F5",
      borderDark100: "#E5E5E5",
      borderDark200: "#DBDBDB",
      borderDark300: "#D4D4D4",
      borderDark400: "#A3A3A3",
      borderDark500: "#8C8C8C",
      borderDark600: "#737373",
      borderDark700: "#525252",
      borderDark800: "#404040",
      borderDark900: "#262626",
      borderDark950: "#171717",
      borderLight0: "#FCFCFC",
      borderLight50: "#F5F5F5",
      borderLight100: "#E5E5E5",
      borderLight200: "#DBDBDB",
      borderLight300: "#D4D4D4",
      borderLight400: "#A3A3A3",
      borderLight500: "#8C8C8C",
      borderLight600: "#737373",
      borderLight700: "#525252",
      borderLight800: "#404040",
      borderLight900: "#262626",
      borderLight950: "#171717",
      backgroundDark0: "#FFFFFF",
      backgroundDark50: "#232924",
      backgroundDark100: "#232924",
      backgroundDark200: "#232924",
      backgroundDark300: "#232924",
      backgroundDark400: "#161D17",
      backgroundDark500: "#161D17",
      backgroundDark600: "#161D17",
      backgroundDark700: "#0C0C0C",
      backgroundDark800: "#0C0C0C",
      backgroundDark900: "#0C0C0C",
      backgroundDark950: "#0C0C0C",
      backgroundLight0: colors.backgroundLight.contrast,
      backgroundLight50: colors.backgroundLight.light,
      backgroundLight100: colors.backgroundLight.light,
      backgroundLight200: colors.backgroundLight.light,
      backgroundLight300: colors.backgroundLight.light,
      backgroundLight400: colors.backgroundLight.main,
      backgroundLight500: colors.backgroundLight.main,
      backgroundLight600: colors.backgroundLight.main,
      backgroundLight700: colors.backgroundLight.dark,
      backgroundLight800: colors.backgroundLight.dark,
      backgroundLight900: colors.backgroundLight.dark,
      backgroundLight950: colors.backgroundLight.dark,
      error0: colors.errorLight.contrast,
      error50: colors.errorLight.light,
      error100: colors.errorLight.light,
      error200: colors.errorLight.light,
      error300: colors.errorLight.light,
      error400: colors.errorLight.main,
      error500: colors.errorLight.main,
      error600: colors.errorLight.main,
      error700: colors.errorLight.dark,
      error800: colors.errorLight.dark,
      error900: colors.errorLight.dark,
      error950: colors.errorLight.dark,
      success0: colors.successLight.contrast,
      success50: colors.successLight.light,
      success100: colors.successLight.light,
      success200: colors.successLight.light,
      success300: colors.successLight.light,
      success400: colors.successLight.main,
      success500: colors.successLight.main,
      success600: colors.successLight.main,
      success700: colors.successLight.dark,
      success800: colors.successLight.dark,
      success900: colors.successLight.dark,
      success950: colors.successLight.dark,
      warning0: colors.warningLight.contrast,
      warning50: colors.warningLight.light,
      warning100: colors.warningLight.light,
      warning200: colors.warningLight.light,
      warning300: colors.warningLight.light,
      warning400: colors.warningLight.main,
      warning500: colors.warningLight.main,
      warning600: colors.warningLight.main,
      warning700: colors.warningLight.dark,
      warning800: colors.warningLight.dark,
      warning900: colors.warningLight.dark,
      info0: colors.infoLight.contrast,
      info50: colors.infoLight.light,
      info100: colors.infoLight.light,
      info200: colors.infoLight.light,
      info300: colors.infoLight.light,
      info400: colors.infoLight.main,
      info500: colors.infoLight.main,
      info600: colors.infoLight.main,
      info700: colors.infoLight.dark,
      info800: colors.infoLight.dark,
      info900: colors.infoLight.dark,
      errorDark0: colors.errorDark.contrast,
      errorDark50: colors.errorDark.light,
      errorDark100: colors.errorDark.light,
      errorDark200: colors.errorDark.light,
      errorDark300: colors.errorDark.light,
      errorDark400: colors.errorDark.main,
      errorDark500: colors.errorDark.main,
      errorDark600: colors.errorDark.main,
      errorDark700: colors.errorDark.dark,
      errorDark800: colors.errorDark.dark,
      errorDark900: colors.errorDark.dark,
      errorDark950: colors.errorLight.dark,
      successDark0: colors.successDark.contrast,
      successDark50: colors.successDark.light,
      successDark100: colors.successDark.light,
      successDark200: colors.successDark.light,
      successDark300: colors.successDark.light,
      successDark400: colors.successDark.main,
      successDark500: colors.successDark.main,
      successDark600: colors.successDark.main,
      successDark700: colors.successDark.dark,
      successDark800: colors.successDark.dark,
      successDark900: colors.successDark.dark,
      successDark950: colors.successDark.dark,
      warningDark0: colors.warningDark.contrast,
      warningDark50: colors.warningDark.light,
      warningDark100: colors.warningDark.light,
      warningDark200: colors.warningDark.light,
      warningDark300: colors.warningDark.light,
      warningDark400: colors.warningDark.main,
      warningDark500: colors.warningDark.main,
      warningDark600: colors.warningDark.main,
      warningDark700: colors.warningDark.dark,
      warningDark800: colors.warningDark.dark,
      warningDark900: colors.warningDark.dark,
      infoDark0: colors.infoDark.contrast,
      infoDark50: colors.infoDark.light,
      infoDark100: colors.infoDark.light,
      infoDark200: colors.infoDark.light,
      infoDark300: colors.infoDark.light,
      infoDark400: colors.infoDark.main,
      infoDark500: colors.infoDark.main,
      infoDark600: colors.infoDark.main,
      infoDark700: colors.infoDark.dark,
      infoDark800: colors.infoDark.dark,
      infoDark900: colors.infoDark.dark,
      backgroundLightError: "#491E1E",
      backgroundDarkError: "#FE8383",
      backgroundLightWarning: "#6A4B2E",
      backgroundDarkWarning: "#FFC691",
      backgroundLightSuccess: "#324532",
      backgroundDarkSuccess: "#99D698",
      backgroundLightInfo: "#135175",
      backgroundDarkInfo: "#DEEEF7",
      backgroundLightMuted: "#F6F6F7",
      backgroundDarkMuted: "#252526",
      white: "#FFFFFF",
      black: "#000000",
    },
    space: {
      px: "1px",
      "0": 0,
      "0.5": 2,
      "1": 4,
      "1.5": 6,
      "2": 8,
      "2.5": 10,
      "3": 12,
      "3.5": 14,
      "4": 16,
      "4.5": 18,
      "5": 20,
      "6": 24,
      "7": 28,
      "8": 32,
      "9": 36,
      "10": 40,
      "11": 44,
      "12": 48,
      "16": 64,
      "20": 80,
      "24": 96,
      "32": 128,
      "40": 160,
      "48": 192,
      "56": 224,
      "64": 256,
      "72": 288,
      "80": 320,
      "96": 384,
      "1/2": "50%",
      "1/3": "33.333%",
      "2/3": "66.666%",
      "1/4": "25%",
      "2/4": "50%",
      "3/4": "75%",
      "1/5": "20%",
      "2/5": "40%",
      "3/5": "60%",
      "4/5": "80%",
      "1/6": "16.666%",
      "2/6": "33.333%",
      "3/6": "50%",
      "4/6": "66.666%",
      "5/6": "83.333%",
      full: "100%",
    },
    borderWidths: {
      "0": 0,
      "1": 1,
      "2": 2,
      "4": 4,
      "8": 8,
    },
    radii: {
      none: 0,
      xs: 2,
      sm: 4,
      md: 6,
      lg: 8,
      xl: 12,
      "2xl": 16,
      "3xl": 24,
      full: 9999,
    },
    breakpoints: {
      base: 0,
      sm: 480,
      md: 768,
      lg: 992,
      xl: 1280,
    },
    mediaQueries: {
      base: "@media screen and (min-width: 0)",
      xs: "@media screen and (min-width: 400px)",
      sm: "@media screen and (min-width: 480px)",
      md: "@media screen and (min-width: 768px)",
      lg: "@media screen and (min-width: 992px)",
      xl: "@media screen and (min-width: 1280px)",
    },
    letterSpacings: {
      xs: -0.4,
      sm: -0.2,
      md: 0,
      lg: 0.2,
      xl: 0.4,
      "2xl": 1.6,
    },
    lineHeights: {
      "2xs": 16,
      xs: 18,
      sm: 20,
      md: 22,
      lg: 24,
      xl: 28,
      "2xl": 32,
      "3xl": 40,
      "4xl": 48,
      "5xl": 56,
      "6xl": 72,
      "7xl": 90,
    },
    fontWeights: {
      hairline: "300",
      thin: "400",
      light: "400",
      normal: "500",
      medium: "500",
      semibold: "800",
      bold: "800",
      extrabold: "800",
      black: "800",
      extraBlack: "800",
    },
    fonts: {
      heading: "Manrope_800ExtraBold",
      body: "Manrope_500Medium",
      mono: "Manrope_400Regular",
    },
    fontSizes: {
      "2xs": 10,
      xs: 10,
      sm: 12,
      md: 14,
      lg: 16,
      xl: 18,
      "2xl": 24,
      "3xl": 30,
      "4xl": 36,
      "5xl": 48,
      "6xl": 60,
      "7xl": 72,
      "8xl": 96,
      "9xl": 128,
    },
    opacity: {
      0: 0,
      5: 0.05,
      10: 0.1,
      20: 0.2,
      25: 0.25,
      30: 0.3,
      40: 0.4,
      50: 0.5,
      60: 0.6,
      70: 0.7,
      75: 0.75,
      80: 0.8,
      90: 0.9,
      95: 0.95,
      100: 1,
    },
  } as const,
  globalStyle: {
    variants: {
      hardShadow: {
        "1": {
          shadowColor: "$backgroundLight900",
          shadowOffset: {
            width: -2,
            height: 2,
          },
          shadowRadius: 8,
          shadowOpacity: 0.5,
          elevation: 10,
        },
        "2": {
          shadowColor: "$backgroundLight900",
          shadowOffset: {
            width: 0,
            height: 3,
          },
          shadowRadius: 8,
          shadowOpacity: 0.5,
          elevation: 10,
        },
        "3": {
          shadowColor: "$backgroundLight900",
          shadowOffset: {
            width: 2,
            height: 2,
          },
          shadowRadius: 8,
          shadowOpacity: 0.5,
          elevation: 10,
        },
        "4": {
          shadowColor: "$backgroundLight900",
          shadowOffset: {
            width: 0,
            height: -3,
          },
          shadowRadius: 8,
          shadowOpacity: 0.5,
          elevation: 10,
        },
        // this 5th version is only for toast shadow
        // temporary
        "5": {
          shadowColor: "$backgroundLight900",
          shadowOffset: {
            width: 0,
            height: 3,
          },
          shadowRadius: 8,
          shadowOpacity: 0.2,
          elevation: 10,
        },
      },
      softShadow: {
        "1": {
          shadowColor: "$primary500",
          shadowOffset: {
            width: 0,
            height: 4,
          },
          shadowRadius: 10,
          shadowOpacity: 0.5,
          _android: {
            shadowColor: "$primary500",
            elevation: 10,
            shadowOpacity: 0.5,
          },
          _dark: {
            shadowColor: "$black",
            shadowOffset: {
              width: 0,
              height: 4,
            },
            shadowRadius: 10,
            shadowOpacity: 0.5,
            _android: {
              shadowColor: "$black",
              elevation: 10,
              shadowOpacity: 0.5,
            },
          },
        },
        "2": {
          shadowColor: "$backgroundLight500",
          shadowOffset: {
            width: 0,
            height: 4,
          },
          shadowRadius: 10,
          shadowOpacity: 0.3,
          _android: {
            shadowColor: "$backgroundLight500",
            elevation: 10,
            shadowOpacity: 0.3,
          },
          _dark: {
            shadowColor: "$backgroundDark500",
            shadowOffset: {
              width: 0,
              height: 4,
            },
            shadowRadius: 10,
            shadowOpacity: 0.5,
            _android: {
              shadowColor: "$backgroundDark500",
              elevation: 10,
              shadowOpacity: 0.5,
            },
          },
        },
        "3": {
          shadowColor: "$textLight500",
          shadowOffset: {
            width: 0,
            height: 4,
          },
          shadowRadius: 12,
          shadowOpacity: 0.15,
          _android: {
            shadowColor: "$textLight500",
            elevation: 4,
            shadowOpacity: 0.15,
          },
        },
        "4": {
          shadowColor: "$backgroundLight900",
          shadowOffset: {
            width: 0,
            height: 0,
          },
          shadowRadius: 40,
          shadowOpacity: 0.1,
          elevation: 10,
          _android: {
            shadowColor: "$backgroundLight500",
            elevation: 20,
            shadowOpacity: 0.2,
          },
        },
      },
    },
  },
  plugins: [new AnimationResolver(MotionAnimationDriver)],
});

type Config = typeof gluestackUIConfig; // Assuming `config` is defined elsewhere

type Components = typeof componentsConfig;

export const componentsConfig = createComponents(componentsTheme);

export type { UIConfig, UIComponents } from "@gluestack-ui/themed";

export interface IConfig {}
export interface IComponents {}

declare module "@gluestack-ui/themed" {
  interface UIConfig extends Omit<Config, keyof IConfig>, IConfig {}
  interface UIComponents
    extends Omit<Components, keyof IComponents>,
      IComponents {}
}

// Extend the internal styled config
declare module "@gluestack-style/react" {
  interface ICustomConfig extends Config {}
}

export const config = {
  ...gluestackUIConfig,
  components: componentsConfig,
};

gluestack-ui Version

1.1.18

Platform

Other Platform

No response

Additional Information

No response

borstessi commented 7 months ago

it is somehow related to the fontWeight, if i apply fontWeight 400 the font is loaded correctly

Viraj-10 commented 7 months ago

Hey @borstessi, Thanks for reporting issue. We will have a look.

tedcu commented 7 months ago

Just here to report that we have the same issue. Applying fontWeight: 400 on config fixes the issue.

jonra1993 commented 7 months ago

I have a similar issue. I am working in Android and Expo. Despite I have imported the fonts using UseFont in the expo and changed the tokens the fonts are not applied to the global theme.

import { config as defaultConfig } from "@gluestack-ui/config";
import { createConfig } from "@gluestack-ui/themed";

export const themeConfig = createConfig({
  ...defaultConfig,  
  tokens: {
    ...defaultConfig.tokens,
    fontSizes: {
      ...defaultConfig.tokens.fontSizes,
      newFontSize: 90,
    },
    fonts: {
      heading: 'Futura-Bold',
      body: 'Futura-Medium',
      mono: 'Futura-Book',
    },
    colors: {
      ...defaultConfig.tokens.colors,
      primary0: "#E5F1FB",
      primary50: "#e2ebff",
      primary100: "#b2c3ff",
      primary200: "#809bff",
      primary300: "#4e72fe",
      primary400: "#204afd",
      primary500: "#0b32e4",
      primary600: "#0426b2",
      primary700: "#001b80",
      primary800: "#001871",
      primary900: "#00051f",
      primary950: "#000711",
      brandPrimary: '#001871',
      brandSecondary: '#007aff',
      brandInfo: '#C0E7F7',
      brandSuccess: '#4DD298',
      brandDanger: '#F58BA0',
      brandWarning: '#FA8202',
      brandDark: '#000',
      brandLight: '#a9a9a9',
    }
  }  
});

When I do this the font is not applied,

<Text style={{ alignSelf: "center", marginTop: 500}}>Styling! :D Hooray!</Text>

The only way it works is forcing the font in each component like this.

<Text style={{fontFamily: "Futura-Bold", alignSelf: "center", marginTop: 500}}>Styling! :D Hooray!</Text>

I am using

    "@gluestack-style/react": "^1.0.52",
    "@gluestack-ui/config": "^1.1.16",
    "@gluestack-ui/themed": "^1.1.22",
    "expo": "~50.0.14",
    "expo-font": "~11.10.3",
pgill-rbi commented 6 months ago

Same issue, setting fontWeight to 400 seems to fix I guess the fontWeight's don't actually do anything

teckbeng-payboy commented 4 months ago

Hi hi can anyone show where you add the fontweight: 400 code? I dded in my config file but the font on android still doesnt change

perminder-klair commented 4 months ago

I am having same issue, loading any custom font from google or local. font colors/sizes does not works. any solution or bug fix?

doranakan commented 4 months ago

I have the same issue, in my project I have both expo-google-fonts and local ones: on iOS everything's ok, on Android it only works but with several issue by adding the FontResolver so it seems it isn't a real solution

DavidAmyot commented 3 months ago

@Viraj-10 I believe this is still present in gluestack-ui v2.

If in my tailwind config I have custom: ["Poppins_400Regular"], and I use a heading with <Heading className="font-custom">{title}</Heading>, it will not work unless I add the font-normal or font-medium classes such as <Heading className="font-normal font-custom">{title}</Heading> and now it'll work, but again that's just for an hardcoded font with a predefined weight.

In the tailwind config, custom: ["Poppins", "sans-serif"] or custom: ["Poppins"] will not work.

I've been having issues with gluestack-ui with the fonts since the very beginning, even in v1. It'd be nice if someone could look into this and have a good in-depth documentation and one of the example apps using a custom font as the default font for everything.