├── next.config.js
├── .gitignore
├── screen.png
├── now.json
├── components
├── FontSelector
│ ├── Section.js
│ ├── Container.js
│ ├── Label.js
│ ├── Select.js
│ ├── InputNumber.js
│ ├── SelectField.js
│ └── index.js
├── FontImporter.js
└── FontDemo.js
├── README.md
├── bin
└── update-fonts.js
├── data
└── fonts.js
├── pages
└── index.js
├── reducers
└── FontReducer.js
└── package.json
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | target: 'serverless'
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .next
2 | node_modules
3 | data/familyMetadataList.json
4 | package-lock.json
--------------------------------------------------------------------------------
/screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/border-radius/play-with-fonts/master/screen.png
--------------------------------------------------------------------------------
/now.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 2,
3 | "builds": [{
4 | "src": "next.config.js",
5 | "use": "@now/next"
6 | }]
7 | }
--------------------------------------------------------------------------------
/components/FontSelector/Section.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Section = styled.div`
4 | padding: 7px;
5 | `
6 |
7 | export default Section
8 |
--------------------------------------------------------------------------------
/components/FontSelector/Container.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Container = styled.div`
4 | padding: 15px 15px 5px;
5 | display: flex;
6 | `
7 |
8 | export default Container
9 |
--------------------------------------------------------------------------------
/components/FontSelector/Label.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Label = styled.div`
4 | padding-left: 14px;
5 | font: 12px/1.4 Arial, sans-serif;
6 | color: #666;
7 | `
8 |
9 | export default Label
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Play with fonts
2 |
3 | 
4 |
5 | #### How to play
6 |
7 | ```bash
8 | git clone https://github.com/border-radius/play-with-fonts
9 | cd play-with-fonts
10 | npm install
11 | npm run dev
12 | ```
13 |
14 | And open your browser at [localhost:3000](http://localhost:3000).
--------------------------------------------------------------------------------
/components/FontSelector/Select.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const Select = styled.select`
4 | padding: 4px 10px;
5 | min-width: 70px;
6 | border: 1px solid #ccc;
7 | background: #fff;
8 | font: 14px/1.4 Arial, sans-serif;
9 | color: #000;
10 | `
11 |
12 | export default Select
13 |
--------------------------------------------------------------------------------
/components/FontSelector/InputNumber.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 |
3 | const InputNumber = styled.input`
4 | padding: 4px 0 4px 15px;
5 | width: 70px;
6 | border: 1px solid #ccc;
7 | background: #fff;
8 | font: 14px/1.4 Arial, sans-serif;
9 | color: #000;
10 | `
11 |
12 | export default InputNumber
13 |
--------------------------------------------------------------------------------
/components/FontImporter.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 |
3 | const FontImporter = ({ context }) => {
4 | const { subset, family, font } = useContext(context)
5 |
6 | const familyUri = family.replace(/\s+/g, '+')
7 | const importUrl = `https://fonts.googleapis.com/css?family=${familyUri}:${font}&subset=${subset}`
8 |
9 | return
10 | }
11 |
12 | export default FontImporter
13 |
--------------------------------------------------------------------------------
/bin/update-fonts.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import axios from 'axios'
3 | import { promises as fs } from 'fs'
4 |
5 | const FONTS_URL = 'https://fonts.google.com/metadata/fonts'
6 | const FONTS_PATH = path.join(__dirname, '../data/familyMetadataList.json')
7 |
8 | async function updateFonts() {
9 | const { data } = await axios({ url: FONTS_URL, responseType: 'text' })
10 | return fs.writeFile(FONTS_PATH, data.slice(5), { encoding: 'utf8' })
11 | }
12 |
13 | ;(async () => await updateFonts())()
14 |
--------------------------------------------------------------------------------
/components/FontSelector/SelectField.js:
--------------------------------------------------------------------------------
1 | import Section from './Section'
2 | import Label from './Label'
3 | import Select from './Select'
4 |
5 | const SelectField = ({ title, list, value, onChange }) => (
6 |
7 |
8 |
15 |
16 | )
17 |
18 | export default SelectField
19 |
--------------------------------------------------------------------------------
/data/fonts.js:
--------------------------------------------------------------------------------
1 | import { familyMetadataList } from './familyMetadataList.json'
2 |
3 | const subsetsList = []
4 | const subsetFamilies = {}
5 | const familyFonts = {}
6 |
7 | familyMetadataList.forEach(({ family, subsets, fonts }) => {
8 | subsets.forEach(subset => {
9 | if (!subsetFamilies[subset]) {
10 | subsetFamilies[subset] = [family]
11 | subsetsList.push(subset)
12 | } else {
13 | subsetFamilies[subset].push(family)
14 | }
15 | })
16 |
17 | familyFonts[family] = Object.keys(fonts)
18 | })
19 |
20 | subsetsList.sort()
21 |
22 | export const getSubsetList = () => subsetsList
23 | export const getFamilyList = subset => subsetFamilies[subset] || []
24 | export const getFontList = family => familyFonts[family] || []
25 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import { createContext, useMemo } from 'react'
2 |
3 | import FontDemo from '../components/FontDemo'
4 | import FontSelector from '../components/FontSelector'
5 | import FontImporter from '../components/FontImporter'
6 | import FontReducer from '../reducers/FontReducer'
7 |
8 | const FontContext = createContext({})
9 |
10 | const App = () => {
11 | const [state, setState] = FontReducer()
12 |
13 | const contextValue = useMemo(() => ({ ...state, setState }), [
14 | state.subset,
15 | state.family,
16 | state.font,
17 | state.size,
18 | state.width,
19 | ])
20 |
21 | return (
22 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
30 | export default App
31 |
--------------------------------------------------------------------------------
/components/FontDemo.js:
--------------------------------------------------------------------------------
1 | import styled from 'styled-components'
2 | import { useContext, useState } from 'react'
3 |
4 | const defaultText = `Lorem ipsum, dolor sit amet consectetur adipisicing elit.
5 | Architecto quo labore nulla.
6 | Corporis possimus asperiores sit voluptates debitis voluptas, pariatur accusamus.
7 | Cupiditate ipsum tempora, natus earum ex hic nesciunt soluta!`.replace(
8 | /\n/g,
9 | ' '
10 | )
11 |
12 | const TextArea = ({ value, onChange, className }) => (
13 |
22 | )
23 |
24 | const Editor = styled(TextArea)`
25 | margin: 0 22px;
26 | width: ${props => props.width}px;
27 | height: 200px;
28 | font-family: '${props => props.family}';
29 | font-weight: ${props => props.font};
30 | font-size: ${props => props.size}px;
31 | border: none;
32 | `
33 |
34 | const FontDemo = ({ context }) => {
35 | const [text, setText] = useState(defaultText)
36 | const onChange = ({ target }) => setText(target.value)
37 |
38 | const { family, font, size, width } = useContext(context)
39 |
40 | return (
41 |
49 | )
50 | }
51 |
52 | export default FontDemo
53 |
--------------------------------------------------------------------------------
/reducers/FontReducer.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from 'react'
2 |
3 | import { getSubsetList, getFamilyList, getFontList } from '../data/fonts'
4 |
5 | const defaultSubset = 'latin'
6 | const defaultFamily = 'IBM Plex Mono'
7 | const defaultFont = '400'
8 | const defaultSize = 16
9 | const defaultWidth = 700
10 |
11 | const subsets = getSubsetList()
12 | const subset = defaultSubset
13 | const families = getFamilyList(subset)
14 | const family = defaultFamily
15 | const fonts = getFontList(family)
16 | const font = defaultFont
17 |
18 | export default () =>
19 | useReducer(
20 | (oldState, diff) => {
21 | const { subsets } = oldState
22 | const subset = diff.subset || oldState.subset
23 | const families = diff.subset ? getFamilyList(subset) : oldState.families
24 | const family = diff.family
25 | ? diff.family
26 | : families.indexOf(oldState.family) > -1
27 | ? oldState.family
28 | : families[0]
29 | const fonts =
30 | diff.subset || diff.family ? getFontList(family) : oldState.fonts
31 | const font = diff.font
32 | ? diff.font
33 | : fonts.indexOf(oldState.font) > -1
34 | ? oldState.font
35 | : fonts[0]
36 | const size = diff.size || oldState.size
37 | const width = diff.width || oldState.width
38 |
39 | return {
40 | subsets,
41 | subset,
42 | families,
43 | family,
44 | fonts,
45 | font,
46 | size,
47 | width,
48 | }
49 | },
50 | {
51 | subsets,
52 | subset,
53 | families,
54 | family,
55 | fonts,
56 | font,
57 | size: defaultSize,
58 | width: defaultWidth,
59 | }
60 | )
61 |
--------------------------------------------------------------------------------
/components/FontSelector/index.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react'
2 | import Container from './Container'
3 | import Section from './Section'
4 | import Label from './Label'
5 | import InputNumber from './InputNumber'
6 | import SelectField from './SelectField'
7 |
8 | const FontSelector = ({ context }) => {
9 | const {
10 | setState,
11 | subsets,
12 | subset,
13 | families,
14 | family,
15 | fonts,
16 | font,
17 | size,
18 | width,
19 | } = useContext(context)
20 |
21 | const subsetChange = ({ target }) => setState({ subset: target.value })
22 | const familyChange = ({ target }) => setState({ family: target.value })
23 | const fontChange = ({ target }) => setState({ font: target.value })
24 | const sizeChange = ({ target }) => setState({ size: target.value })
25 | const widthChange = ({ target }) => setState({ width: target.value })
26 |
27 | return (
28 |
29 |
35 |
41 |
47 |
51 |
55 |
56 | )
57 | }
58 |
59 | export default FontSelector
60 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "play-with-fonts",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "bin": {
7 | "play-with-fonts": "update-fonts.js"
8 | },
9 | "scripts": {
10 | "update-fonts": "babel-node --presets es2015 bin/update-fonts.js",
11 | "dev": "next",
12 | "build": "next build",
13 | "start": "next start",
14 | "fmt": "prettier --write --single-quote --jsx-single-quote --no-semi --trailing-comma es5 \"{pages,reducers,bin,data,components}/**/*.js\""
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/border-radius/play-with-fonts.git"
19 | },
20 | "keywords": [],
21 | "author": "",
22 | "license": "ISC",
23 | "bugs": {
24 | "url": "https://github.com/border-radius/play-with-fonts/issues"
25 | },
26 | "homepage": "https://github.com/border-radius/play-with-fonts#readme",
27 | "dependencies": {
28 | "babel-plugin-styled-components": "^1.10.0",
29 | "next": "^8.0.3",
30 | "react": "^16.8.5",
31 | "react-dom": "^16.8.5",
32 | "styled-components": "^4.2.0"
33 | },
34 | "babel": {
35 | "env": {
36 | "development": {
37 | "presets": [
38 | "next/babel"
39 | ],
40 | "plugins": [
41 | [
42 | "styled-components",
43 | {
44 | "ssr": true,
45 | "displayName": true
46 | }
47 | ]
48 | ]
49 | },
50 | "production": {
51 | "presets": [
52 | "next/babel"
53 | ],
54 | "plugins": [
55 | [
56 | "styled-components",
57 | {
58 | "ssr": true,
59 | "displayName": false
60 | }
61 | ]
62 | ]
63 | }
64 | }
65 | },
66 | "devDependencies": {
67 | "prettier": "^1.16.4"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------