diff --git a/apps/svelte-sandbox/src/main.js b/apps/svelte-sandbox/src/main.js
index 43dda413..0bb4056d 100644
--- a/apps/svelte-sandbox/src/main.js
+++ b/apps/svelte-sandbox/src/main.js
@@ -1,8 +1,18 @@
-import { theme, resetCss } from '@morfeo/web';
+import { theme, resetCss, loadFont } from '@morfeo/web';
import { enableMorfeoDevTool } from '@morfeo/dev-tools';
import App from './App.svelte';
import { lightTheme } from './theme';
+loadFont({
+ importFontFace: true,
+ urls: [
+ {
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap',
+ },
+ ],
+ name: 'regular',
+ family: 'Roboto',
+});
enableMorfeoDevTool();
resetCss();
theme.set(lightTheme);
diff --git a/apps/web-sandbox/src/App.tsx b/apps/web-sandbox/src/App.tsx
index a48b9475..ea95b32b 100644
--- a/apps/web-sandbox/src/App.tsx
+++ b/apps/web-sandbox/src/App.tsx
@@ -33,12 +33,14 @@ function App() {
const { containerStyle, blockStyle, codeStyle } = useStyles({
containerStyle: {
bg: 'secondary',
- width: '100vw' as any,
+ width: '100%' as any,
display: 'flex',
- height: '100vh' as any,
+ minHeight: '100vh' as any,
alignItems: 'center',
justifyContent: 'space-evenly',
transition: 'light',
+ pt: 's',
+ pb: 's'
},
blockStyle: {
flex: 1,
diff --git a/apps/web-sandbox/src/index.tsx b/apps/web-sandbox/src/index.tsx
index 843afe90..79ae6b73 100644
--- a/apps/web-sandbox/src/index.tsx
+++ b/apps/web-sandbox/src/index.tsx
@@ -1,13 +1,20 @@
import React from 'react';
-import { theme, resetCss } from '@morfeo/react';
+import { theme, resetCss, loadFont } from '@morfeo/react';
import ReactDOM from 'react-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { lightTheme } from './theme';
import { enableMorfeoDevTool } from '@morfeo/dev-tools';
+loadFont({
+ importFontFace: true,
+ urls: [{
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap'
+ }],
+ name: 'regular',
+ family: 'Roboto',
+})
enableMorfeoDevTool();
-
resetCss();
theme.set(lightTheme as any);
diff --git a/apps/web-sandbox/src/theme.ts b/apps/web-sandbox/src/theme.ts
index 6bd31967..11007b1f 100644
--- a/apps/web-sandbox/src/theme.ts
+++ b/apps/web-sandbox/src/theme.ts
@@ -33,6 +33,9 @@ export const lightTheme = {
warning: '#f39c12',
transparent: 'transparent',
},
+ fontFamilies: {
+ regular: 'Roboto',
+ },
gradients,
radii: {
m: '10px',
@@ -42,6 +45,15 @@ export const lightTheme = {
s: '40px',
m: '100px',
},
+ lineHeights: {
+ s: 1.7,
+ },
+ letterSpacings: {
+ s: 1.6,
+ },
+ fontSizes: {
+ s: '14px',
+ },
sizes: {
s: '10px',
m: '100px',
@@ -137,7 +149,12 @@ export const lightTheme = {
},
Typography: {
tag: 'p',
- style: {},
+ style: {
+ fontFamily: 'regular',
+ fontSize: 's',
+ lineHeight: 's',
+ letterSpacing: 's',
+ },
variants: {
h1: {
tag: 'h1',
diff --git a/packages/fonts/LICENSE b/packages/fonts/LICENSE
new file mode 100644
index 00000000..13491c21
--- /dev/null
+++ b/packages/fonts/LICENSE
@@ -0,0 +1,25 @@
+
+The MIT License (MIT)
+
+Copyright (c) 2021 Mauro Erta.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/packages/fonts/README.md b/packages/fonts/README.md
new file mode 100644
index 00000000..0664ecf7
--- /dev/null
+++ b/packages/fonts/README.md
@@ -0,0 +1,123 @@
+
+
@morfeo/fonts
+
+
+**@morfeo/fonts** provide 2 helpers to easily load a font on your document style and your mofeo theme both.
+
+**@morfeo/fonts** is part of the [@morfeo](https://github.com/VLK-STUDIO/morfeo) eco-system, a set of **framework-agnostic** tools that help you to create beautiful design systems for your web and mobile apps.
+
+---
+
+
+
+---
+
+## Table of contents
+
+#### [Installation](#installation-1)
+
+#### [APIS](#apis-1)
+
+- [mountFont](#mountFont)
+ - [@import fontFace](#@import)
+ - [define fontFace](#@fontFace)
+ - [references](#@references)
+
+---
+
+## Installation
+
+with [npm](https://www.npmjs.com/package/@morfeo/fonts):
+
+```bash
+npm install @morfeo/fonts
+```
+
+or [yarn](https://yarn.pm/@morfeo/fonts):
+
+```bash
+ yarn add @morfeo/fonts
+```
+
+**note:** @morfeo/fonts is also included on **@morfeo/web**
+
+---
+
+## Apis
+
+### mountFont
+
+The `mountFont` Api mount a font on the document head
+
+#### @import
+
+Setting the prop `importFontFace` to `true` the style will be resolve to `@import ...`.
+
+It is usefull if you want to import a css file with the fontFace definitions.
+
+It is also the case of Google Font.
+
+```typescript
+import { mountFont } from '@morfeo/fonts';
+
+mountFont({
+ urls: [
+ {
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap'
+ },
+ ],
+ name: 'bold',
+ family: 'Roboto',
+ importFontFace: true,
+})
+```
+
+#### @fontFace
+
+By not defining or setting to `true` the `importFontFace` prop the style will resolve on `@fontFace ...`.
+It allows to create `@fontFace` definition from scratch
+
+```typescript
+import { mountFont } from '@morfeo/fonts';
+
+mountFont({
+ urls: [
+ {
+ url: 'src/fonts/Roboto-bold.woff',
+ format: 'woff'
+ },
+ {
+ url: 'src/fonts/Roboto-bold.woff2',
+ format: 'woff2'
+ },
+ ],
+ name: 'bold',
+ family: 'Roboto',
+ weight: 'bold'
+})
+```
+
+#### References
+
+```typescript
+export type FontType = 'woff' | 'woff2' | 'trueType' | 'embedded-opentype';
+
+export type FontUrl = {
+ url: string;
+ format?: FontType;
+};
+
+export type MountFontParams = {
+ name: string;
+ urls: FontUrl[];
+ family: string;
+ importFontFace?: boolean;
+ weight?: string;
+};
+```
+
diff --git a/packages/fonts/package.json b/packages/fonts/package.json
new file mode 100644
index 00000000..0d4bb90a
--- /dev/null
+++ b/packages/fonts/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "@morfeo/fonts",
+ "author": {
+ "name": "Andrea Simone Porceddu",
+ "email": "andrea@vlkstudio.com"
+ },
+ "private": false,
+ "version": "0.1.5",
+ "license": "MIT",
+ "main": "build/index.js",
+ "module": "build/index.js",
+ "types": "build/index",
+ "typings": "build/index",
+ "keywords": [
+ "design",
+ "system",
+ "font",
+ "load"
+ ],
+ "scripts": {
+ "build": "rimraf build && tsc",
+ "watch": "tsc -w"
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "build"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/VLK-STUDIO/morfeo"
+ },
+ "homepage": "https://github.com/VLK-STUDIO/morfeo/tree/main/packages/fonts",
+ "bugs": {
+ "url": "https://github.com/VLK-STUDIO/morfeo/issues"
+ }
+}
diff --git a/packages/fonts/src/index.ts b/packages/fonts/src/index.ts
new file mode 100644
index 00000000..5b976cb3
--- /dev/null
+++ b/packages/fonts/src/index.ts
@@ -0,0 +1 @@
+export * from './mountFont';
diff --git a/packages/fonts/src/mountFont.ts b/packages/fonts/src/mountFont.ts
new file mode 100644
index 00000000..eee1df3c
--- /dev/null
+++ b/packages/fonts/src/mountFont.ts
@@ -0,0 +1,103 @@
+export type FontType = 'woff' | 'woff2' | 'trueType' | 'embedded-opentype';
+
+export type FontUrl = {
+ url: string;
+ format?: FontType;
+};
+
+export type MountFontParams = {
+ name: string;
+ urls: FontUrl[];
+ family: string;
+ /**
+ * set to true if you are providing a css url with predefined fontFace(example: Google Font)
+ * Please note: it support **one font family** per css import
+ */
+ importFontFace?: boolean;
+ weight?: string;
+};
+
+export function unmountFont(name: string) {
+ const currentFontStyle = document.getElementById(`font-${name}`);
+
+ if (currentFontStyle) {
+ currentFontStyle.remove();
+ }
+}
+
+/**
+ * Mount a fontFace on document head style
+ *
+ * ---
+ *
+ * ### mount a google font
+ *
+ * ```ts
+ * mountFont({
+ * urls: [{
+ * url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap'
+ * }],
+ * importFontFace: true,
+ * fontFamily: 'Roboto',
+ * name: 'bold',
+ * })
+ * ```
+ *
+ * ---
+ *
+ * ### mount a local font
+ *
+ * ```ts
+ * mountFont({
+ * urls: [
+ * {
+ * url: './fonts/Roboto-bold.woff',
+ * format: 'woff'
+ * },
+ * {
+ * url: './fonts/Roboto-bold.woff2',
+ * format: 'woff2'
+ * },
+ * ],
+ * importFontFace: true,
+ * fontFamily: 'Roboto',
+ * name: 'bold',
+ * })
+ * ```
+ */
+
+export function mountFont(font: MountFontParams) {
+ let newFontFaceContent = '';
+
+ if (font.importFontFace) {
+ newFontFaceContent += `
+ @import ${font.urls.reduce((_, value) => `url(${value.url})`, '')}
+ `;
+ }
+
+ if (!font.importFontFace) {
+ newFontFaceContent += `
+ @fontFace {
+ font-family: ${font.family};
+ ${font.weight ? `font-weight: ${font.weight};` : ''}
+ url: ${font.urls.reduce(
+ (acc, value) =>
+ `${acc ? acc + '\n' : acc} url('${value.url}') ${
+ value.format ? `format('${value.format}')` : ''
+ }`,
+ '',
+ )}
+ }
+ `;
+ }
+
+ const newFontStyle = `
+
+ `;
+
+ unmountFont(font.name);
+
+ document.head.innerHTML += newFontStyle;
+}
diff --git a/packages/fonts/tests/mountFont.test.ts b/packages/fonts/tests/mountFont.test.ts
new file mode 100644
index 00000000..4c47b1d1
--- /dev/null
+++ b/packages/fonts/tests/mountFont.test.ts
@@ -0,0 +1,66 @@
+import { mountFont, MountFontParams } from '../src';
+
+const newImportedFontFace: MountFontParams = {
+ importFontFace: true,
+ urls: [
+ {
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap',
+ },
+ ],
+ name: 'bold',
+ family: 'Roboto',
+};
+
+const newFontFace: MountFontParams = {
+ urls: [
+ {
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap',
+ },
+ ],
+ name: 'bold',
+ family: 'Roboto',
+};
+
+const newFontWithFormat: MountFontParams = {
+ urls: [
+ {
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap',
+ format: 'woff',
+ },
+ {
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap',
+ format: 'woff2',
+ },
+ ],
+ name: 'bold',
+ family: 'Roboto',
+};
+
+describe('mountFont', () => {
+ describe('new fontFace', () => {
+ test('should add a style with the fontFace definition', () => {
+ mountFont(newFontFace);
+ const boldStyle = document.getElementById('font-bold');
+ expect(boldStyle?.textContent).toContain('@fontFace');
+ });
+
+ test('should set a style that contain font-weight', () => {
+ mountFont({ ...newFontFace, weight: 'bold' });
+ const boldStyle = document.getElementById('font-bold');
+ expect(boldStyle?.textContent).toContain('font-weight: bold');
+ });
+
+ test('should set a style that contain format', () => {
+ mountFont(newFontWithFormat);
+ const boldStyle = document.getElementById('font-bold');
+ expect(boldStyle?.textContent).toContain("format('woff')");
+ });
+ });
+ describe('imported fontFace', () => {
+ test('should add a style with the fontface import', () => {
+ mountFont(newImportedFontFace);
+ const boldStyle = document.getElementById('font-bold');
+ expect(boldStyle?.textContent).toContain('@import');
+ });
+ });
+});
diff --git a/packages/fonts/tsconfig.json b/packages/fonts/tsconfig.json
new file mode 100644
index 00000000..e3f631ae
--- /dev/null
+++ b/packages/fonts/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "compilerOptions": {
+ "outDir": "./build",
+ "declaration": true
+ },
+ "include": ["./src"],
+ "exclude": ["./node_modules", "./src/**/*.test.ts", "./src/**/*.spec.ts"]
+}
diff --git a/packages/web/package.json b/packages/web/package.json
index b492ce07..e2636ad5 100644
--- a/packages/web/package.json
+++ b/packages/web/package.json
@@ -21,7 +21,8 @@
},
"dependencies": {
"@morfeo/core": "^0.1.5",
- "@morfeo/jss": "^0.1.5"
+ "@morfeo/jss": "^0.1.5",
+ "@morfeo/fonts": "^0.1.5"
},
"peerDependencies": {
"csstype": "^3.0.8"
diff --git a/packages/web/src/index.ts b/packages/web/src/index.ts
index cd70245a..fc169591 100644
--- a/packages/web/src/index.ts
+++ b/packages/web/src/index.ts
@@ -18,3 +18,5 @@ export * from './types';
export * from '@morfeo/core';
/** re-export of @morfeo/jss */
export * from '@morfeo/jss';
+/** re-export of @morfeo/fonts */
+export * from '@morfeo/fonts';
diff --git a/packages/web/src/utils/index.ts b/packages/web/src/utils/index.ts
index b8494d6b..beca1935 100644
--- a/packages/web/src/utils/index.ts
+++ b/packages/web/src/utils/index.ts
@@ -1 +1,2 @@
export * from './resetCss';
+export * from './loadFont';
diff --git a/packages/web/src/utils/loadFont.ts b/packages/web/src/utils/loadFont.ts
new file mode 100644
index 00000000..e2018b57
--- /dev/null
+++ b/packages/web/src/utils/loadFont.ts
@@ -0,0 +1,49 @@
+import { theme, Font } from '@morfeo/core';
+import { MountFontParams, mountFont } from '@morfeo/fonts';
+
+export type LoadFontParams = Omit & { name: Font };
+/**
+ * Load a fontFace on document head style and add it to the morfeo theme
+ *
+ * ---
+ *
+ * ### load a google font
+ *
+ * ```ts
+ * loadFont({
+ * urls: [{
+ * url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap'
+ * }],
+ * importFontFace: true,
+ * fontFamily: 'Roboto',
+ * name: 'bold',
+ * })
+ * ```
+ *
+ * ---
+ *
+ * ### load a local font
+ *
+ * ```ts
+ * loadFont({
+ * urls: [
+ * {
+ * url: './fonts/Roboto-bold.woff',
+ * format: 'woff'
+ * },
+ * {
+ * url: './fonts/Roboto-bold.woff2',
+ * format: 'woff2'
+ * },
+ * ],
+ * importFontFace: true,
+ * fontFamily: 'Roboto',
+ * name: 'bold',
+ * })
+ * ```
+ */
+
+export function loadFont(font: LoadFontParams) {
+ mountFont(font);
+ theme.setValue('fonts', font.name, font.family);
+}
diff --git a/packages/web/src/utils/resetCss.ts b/packages/web/src/utils/resetCss.ts
index b748cb2e..3b5dc39a 100644
--- a/packages/web/src/utils/resetCss.ts
+++ b/packages/web/src/utils/resetCss.ts
@@ -18,6 +18,7 @@ time, mark, audio, video {
font-size: 100%;
font: inherit;
vertical-align: baseline;
+ font-smooth: auto;
}
:focus {
diff --git a/packages/web/tests/loadFont.test.ts b/packages/web/tests/loadFont.test.ts
new file mode 100644
index 00000000..19248057
--- /dev/null
+++ b/packages/web/tests/loadFont.test.ts
@@ -0,0 +1,43 @@
+import { loadFont, LoadFontParams } from '../src/utils';
+import { theme } from '@morfeo/core';
+
+const newImportedFontFace: LoadFontParams = {
+ importFontFace: true,
+ urls: [
+ {
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap',
+ },
+ ],
+ name: 'regular',
+ family: 'Roboto',
+};
+
+const newFontFace: LoadFontParams = {
+ urls: [
+ {
+ url: 'https://fonts.googleapis.com/css2?family=Roboto:wght@700&display=swap',
+ },
+ ],
+ name: 'bold',
+ family: 'Roboto',
+};
+
+beforeEach(() => {
+ theme.reset();
+});
+describe('loadFont', () => {
+ describe('new fontFace', () => {
+ test('should add a font on theme', () => {
+ loadFont(newFontFace);
+ const themeFont = theme.getValue('fonts', newFontFace.name);
+ expect(themeFont).toBe(newFontFace.family);
+ });
+ });
+ describe('imported fontFace', () => {
+ test('should add a font on theme', () => {
+ loadFont(newImportedFontFace);
+ const themeFont = theme.getValue('fonts', newImportedFontFace.name);
+ expect(themeFont).toBe(newImportedFontFace.family);
+ });
+ });
+});