├── site ├── .nvmrc ├── gatsby-ssr.js ├── gatsby-browser.js ├── static │ ├── _redirects │ ├── card.png │ ├── etsy.jpg │ └── topoink.png ├── netlify.toml ├── postcss.config.js ├── raw │ ├── 1x │ │ ├── twitter.png │ │ └── twitter-100.jpg │ └── SVG │ │ ├── circlewhitecircle.svg │ │ ├── mask.svg │ │ ├── serious mouth.svg │ │ ├── capback.svg │ │ ├── bgcircle.svg │ │ ├── sad mouth.svg │ │ ├── longhair.svg │ │ ├── longhairback.svg │ │ ├── beanieback.svg │ │ ├── sadmouth.svg │ │ ├── vue.svg │ │ ├── gatsby.svg │ │ ├── longhairfront.svg │ │ ├── seriouseyebrows.svg │ │ ├── happy.svg │ │ ├── content.svg │ │ ├── lashes.svg │ │ ├── grin.svg │ │ ├── knockedout.svg │ │ ├── hairshort.svg │ │ ├── simpleyes.svg │ │ ├── angry eyebrows.svg │ │ ├── oooooo.svg │ │ ├── normal eyebrows.svg │ │ ├── leftloweredeyebrow.svg │ │ ├── bobback.svg │ │ ├── dressfront.svg │ │ ├── lefteyebrowlowered.svg │ │ ├── opensmile.svg │ │ ├── concernedeyebrows.svg │ │ ├── sad.svg │ │ ├── graphql.svg │ │ ├── tongue.svg │ │ ├── longhairfront_1.svg │ │ ├── wink.svg │ │ ├── stubble.svg │ │ ├── dressback.svg │ │ ├── tinyglasses.svg │ │ ├── afro_front.svg │ │ ├── roundglasses.svg │ │ ├── normal eyes.svg │ │ ├── redwood.svg │ │ ├── lips.svg │ │ ├── tanktop.svg │ │ ├── breastsfront.svg │ │ ├── shorthair.svg │ │ ├── buzzcut.svg │ │ ├── hearteyes.svg │ │ ├── afro_back.svg │ │ ├── react.svg │ │ ├── shades.svg │ │ ├── vneck.svg │ │ ├── capfront.svg │ │ ├── lefttwitcheye.svg │ │ ├── twitch.svg │ │ ├── squinteyes.svg │ │ ├── baldinghair.svg │ │ ├── breasts.svg │ │ ├── eyeswithlashes.svg │ │ └── shirt.svg ├── src │ ├── images │ │ └── favicon.png │ ├── index.css │ ├── components │ │ ├── SlotMachine.tsx │ │ ├── SEO.tsx │ │ └── Hero.tsx │ ├── pages │ │ ├── 404.tsx │ │ └── index.tsx │ └── utils │ │ └── getRandomOptions.ts ├── tailwind.config.js ├── tsconfig.json ├── gatsby-config.js ├── package.json ├── .gitignore └── functions │ └── svg │ └── svg.js ├── tsconfig.json ├── demo └── demo.gif ├── core ├── .gitignore ├── src │ ├── components │ │ ├── hats │ │ │ └── types.ts │ │ ├── eyes │ │ │ ├── types.ts │ │ │ ├── HappyEyes.tsx │ │ │ ├── SimpleEyes.tsx │ │ │ ├── ContentEyes.tsx │ │ │ ├── Lashes.tsx │ │ │ ├── DizzyEyes.tsx │ │ │ ├── Wink.tsx │ │ │ ├── NormalEyes.tsx │ │ │ ├── HeartEyes.tsx │ │ │ ├── LeftTwitchEyes.tsx │ │ │ └── SquintEyes.tsx │ │ ├── facialHair │ │ │ ├── types.ts │ │ │ └── Stubble.tsx │ │ ├── mouths │ │ │ ├── types.ts │ │ │ ├── SeriousMouth.tsx │ │ │ ├── Grin.tsx │ │ │ ├── OpenMouth.tsx │ │ │ ├── SmileOpen.tsx │ │ │ ├── Sad.tsx │ │ │ ├── Tongue.tsx │ │ │ └── Lips.tsx │ │ ├── hair │ │ │ ├── types.ts │ │ │ ├── LongHair.tsx │ │ │ ├── ShortHair.tsx │ │ │ └── BuzzCut.tsx │ │ ├── bodies │ │ │ ├── types.ts │ │ │ └── Chest.tsx │ │ ├── clothing │ │ │ ├── types.ts │ │ │ ├── TankTop.tsx │ │ │ ├── Dress.tsx │ │ │ └── VNeck.tsx │ │ ├── Mask.tsx │ │ ├── BgCircle.tsx │ │ ├── clothingGraphic │ │ │ ├── Vue.tsx │ │ │ ├── Gatsby.tsx │ │ │ ├── GraphQL.tsx │ │ │ └── Redwood.tsx │ │ ├── eyebrows │ │ │ ├── SeriousEyebrows.tsx │ │ │ ├── AngryEyebrows.tsx │ │ │ ├── ConcernedEyebrows.tsx │ │ │ ├── Normal.tsx │ │ │ └── LeftLoweredEyebrows.tsx │ │ ├── accessories │ │ │ ├── TinyGlasses.tsx │ │ │ ├── RoundGlasses.tsx │ │ │ └── Shades.tsx │ │ └── FaceMask.tsx │ ├── utils │ │ └── Noop.tsx │ ├── index.tsx │ ├── themeContext.tsx │ └── theme.ts ├── tsconfig.json ├── package.json ├── .github │ └── workflows │ │ └── main.yml ├── LICENSE └── exportParts.js ├── prettier.config.js ├── LICENSE └── .gitignore /site/.nvmrc: -------------------------------------------------------------------------------- 1 | v16 -------------------------------------------------------------------------------- /site/gatsby-ssr.js: -------------------------------------------------------------------------------- 1 | import './src/index.css' 2 | -------------------------------------------------------------------------------- /site/gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import './src/index.css' 2 | -------------------------------------------------------------------------------- /site/static/_redirects: -------------------------------------------------------------------------------- 1 | /svg /example.svg 200 2 | /* /index.html 200 -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "." 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /demo/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertBroersma/beanheads/HEAD/demo/demo.gif -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.zip 3 | .DS_Store 4 | node_modules 5 | .cache 6 | dist 7 | parts -------------------------------------------------------------------------------- /core/src/components/hats/types.ts: -------------------------------------------------------------------------------- 1 | export interface HatProps { 2 | scale?: number 3 | } 4 | -------------------------------------------------------------------------------- /core/src/components/eyes/types.ts: -------------------------------------------------------------------------------- 1 | export interface EyeProps { 2 | withLashes?: boolean 3 | } 4 | -------------------------------------------------------------------------------- /core/src/utils/Noop.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const Noop = () => <> 4 | -------------------------------------------------------------------------------- /site/netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | functions = "functions" 3 | 4 | [dev] 5 | command = "yarn dev" -------------------------------------------------------------------------------- /site/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | plugins: [require('tailwindcss')], 3 | }) 4 | -------------------------------------------------------------------------------- /site/static/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertBroersma/beanheads/HEAD/site/static/card.png -------------------------------------------------------------------------------- /site/static/etsy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertBroersma/beanheads/HEAD/site/static/etsy.jpg -------------------------------------------------------------------------------- /site/raw/1x/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertBroersma/beanheads/HEAD/site/raw/1x/twitter.png -------------------------------------------------------------------------------- /site/static/topoink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertBroersma/beanheads/HEAD/site/static/topoink.png -------------------------------------------------------------------------------- /site/raw/1x/twitter-100.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertBroersma/beanheads/HEAD/site/raw/1x/twitter-100.jpg -------------------------------------------------------------------------------- /site/src/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RobertBroersma/beanheads/HEAD/site/src/images/favicon.png -------------------------------------------------------------------------------- /core/src/components/facialHair/types.ts: -------------------------------------------------------------------------------- 1 | import { colors } from '../../theme' 2 | 3 | export interface FacialHairProps { 4 | color: keyof typeof colors.hair 5 | } 6 | -------------------------------------------------------------------------------- /core/src/components/mouths/types.ts: -------------------------------------------------------------------------------- 1 | import { colors } from '../../theme' 2 | 3 | export interface MouthProps { 4 | lipColor?: keyof typeof colors.lipColors 5 | } 6 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tabWidth: 2, 3 | singleQuote: true, 4 | trailingComma: 'all', 5 | semi: false, 6 | arrowParens: 'avoid', 7 | } 8 | -------------------------------------------------------------------------------- /core/src/components/hair/types.ts: -------------------------------------------------------------------------------- 1 | import { colors } from '../../theme' 2 | 3 | export interface HairProps { 4 | hairColor: keyof typeof colors.hair 5 | hasHat?: boolean 6 | } 7 | -------------------------------------------------------------------------------- /core/src/components/bodies/types.ts: -------------------------------------------------------------------------------- 1 | import { colors } from '../../theme' 2 | 3 | export interface BodyProps { 4 | clothingColor: keyof typeof colors.clothing 5 | braStraps: boolean 6 | } 7 | -------------------------------------------------------------------------------- /site/raw/SVG/circlewhitecircle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/src/components/clothing/types.ts: -------------------------------------------------------------------------------- 1 | import { colors } from '../../theme' 2 | 3 | export interface ClothingProps { 4 | color: keyof typeof colors.clothing 5 | graphic?: React.ComponentType 6 | } 7 | -------------------------------------------------------------------------------- /site/raw/SVG/mask.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /site/raw/SVG/serious mouth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /core/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as theme from './theme' 2 | 3 | export { theme } 4 | export * from './components/Avatar' 5 | export { Avatar as BeanHead } from './components/Avatar' 6 | 7 | export { ThemeContext } from './themeContext' 8 | 9 | export { Noop } from './utils/Noop' 10 | -------------------------------------------------------------------------------- /core/src/themeContext.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | import { colors } from './theme' 3 | 4 | export const ThemeContext = React.createContext({ 5 | colors, 6 | skin: colors.skin.light, 7 | }) 8 | 9 | export const useTheme = () => useContext(ThemeContext) 10 | -------------------------------------------------------------------------------- /site/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: ['./src/**/*.js', './src/**/*.jsx', './src/**/*.ts', './src/**/*.tsx'], 3 | theme: { 4 | extend: {}, 5 | fontFamily: { 6 | sans: ['Jost', 'sans-serif'], 7 | }, 8 | }, 9 | variants: {}, 10 | plugins: [], 11 | } 12 | -------------------------------------------------------------------------------- /site/raw/SVG/capback.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /site/raw/SVG/bgcircle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/src/components/Mask.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const Mask = ({ id }: { id: string }) => { 4 | return ( 5 | 6 | 10 | 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /site/raw/SVG/sad mouth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /site/raw/SVG/longhair.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /site/raw/SVG/longhairback.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /site/src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Jost:wght@400;600&display=swap'); 2 | 3 | @tailwind base; 4 | 5 | @tailwind components; 6 | 7 | @tailwind utilities; 8 | 9 | body { 10 | @apply font-sans; 11 | } 12 | 13 | .underline-blue { 14 | text-decoration-color: #63b3ed; 15 | } 16 | 17 | .underline-blue:hover { 18 | color: #4299e1; 19 | text-decoration-color: #4299e1; 20 | } 21 | -------------------------------------------------------------------------------- /site/raw/SVG/beanieback.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /site/raw/SVG/sadmouth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /site/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "esnext", 5 | "jsx": "preserve", 6 | "lib": ["dom", "esnext"], 7 | "strict": true, 8 | "noEmit": true, 9 | "isolatedModules": true, 10 | "esModuleInterop": true, 11 | "noUnusedLocals": false, 12 | "strictNullChecks": true, 13 | "allowJs": true 14 | }, 15 | "exclude": ["node_modules", "public", ".cache"] 16 | } 17 | -------------------------------------------------------------------------------- /core/src/components/BgCircle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../themeContext' 3 | import { colors } from '../theme' 4 | 5 | export interface BgCircleProps { 6 | circleColor: keyof typeof colors.bgColors 7 | } 8 | 9 | export const BgCircle = ({ circleColor }: BgCircleProps) => { 10 | const { colors } = useTheme() 11 | 12 | const color = colors.bgColors[circleColor] 13 | 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /core/src/components/mouths/SeriousMouth.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const SeriousMouth = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /site/raw/SVG/vue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /site/raw/SVG/gatsby.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /site/raw/SVG/longhairfront.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /site/raw/SVG/seriouseyebrows.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /site/raw/SVG/happy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/components/clothingGraphic/Vue.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const Vue = () => { 4 | return ( 5 | <> 6 | 12 | 16 | 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /site/raw/SVG/content.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /site/raw/SVG/lashes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types"], 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "lib": ["dom", "esnext"], 6 | "importHelpers": true, 7 | "declaration": true, 8 | "sourceMap": true, 9 | "rootDir": "./src", 10 | "strict": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "noImplicitReturns": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "moduleResolution": "node", 16 | "baseUrl": "./", 17 | "paths": { 18 | "*": ["src/*", "node_modules/*"] 19 | }, 20 | "jsx": "react", 21 | "esModuleInterop": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /site/src/components/SlotMachine.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback, useEffect } from 'react' 2 | import { BeanHead } from 'beanheads' 3 | 4 | export function SlotMachine({ 5 | interval = 300, 6 | pause = false, 7 | }: { 8 | interval?: number 9 | pause?: boolean 10 | }) { 11 | const [rerender, set] = useState(0) 12 | 13 | const forceRerender = useCallback(() => { 14 | set(a => a + 1) 15 | }, []) 16 | 17 | useEffect(() => { 18 | const timeout = setTimeout( 19 | forceRerender, 20 | rerender % 15 === 0 && pause ? 2000 : interval, 21 | ) 22 | 23 | return () => clearTimeout(timeout) 24 | }, [rerender, interval]) 25 | 26 | return 27 | } 28 | -------------------------------------------------------------------------------- /site/raw/SVG/grin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /site/raw/SVG/knockedout.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /site/raw/SVG/hairshort.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /site/raw/SVG/simpleyes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /site/raw/SVG/angry eyebrows.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/components/eyebrows/SeriousEyebrows.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const SeriousEyebrows = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /site/raw/SVG/oooooo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /site/src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'gatsby' 3 | import { BeanHead } from 'beanheads' 4 | import { SEO } from '../components/SEO' 5 | 6 | function NotFound() { 7 | return ( 8 | <> 9 | 10 |
11 |
12 | 18 |
19 |

Page Not Found

20 | 21 | Go Home 22 | 23 |
24 | 25 | ) 26 | } 27 | 28 | export default NotFound 29 | -------------------------------------------------------------------------------- /site/raw/SVG/normal eyebrows.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/components/eyes/HappyEyes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const HappyEyes = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 17 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /core/src/components/eyes/SimpleEyes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const SimpleEyes = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 10 | 14 | 15 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /site/raw/SVG/leftloweredeyebrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/components/eyes/ContentEyes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const ContentEyes = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 17 | 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /site/gatsby-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteMetadata: { 3 | title: 'Bean Heads', 4 | titleTemplate: '%s · Easily generate avatars for your projects', 5 | description: 6 | 'Combine expressions, clothing, hair styles and colors into billions of different unique characters.', 7 | url: 'https://beanheads.robertbroersma.com', 8 | // TODO: 9 | image: '/card.png', // Path to your image you placed in the 'static' folder 10 | twitterUsername: '@robertbrosma', 11 | }, 12 | plugins: [ 13 | 'gatsby-plugin-typescript', 14 | 'gatsby-plugin-postcss', 15 | // { 16 | // resolve: 'gatsby-plugin-manifest', 17 | // options: { 18 | // icon: 'src/images/favicon.png', 19 | // }, 20 | // }, 21 | 'gatsby-plugin-react-helmet', 22 | { 23 | resolve: 'gatsby-plugin-google-analytics', 24 | options: { 25 | trackingId: 'UA-176816069-1', 26 | }, 27 | }, 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /core/src/components/eyes/Lashes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const RightLash = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | 13 | ) 14 | } 15 | 16 | export const LeftLash = () => { 17 | const { colors } = useTheme() 18 | 19 | return ( 20 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /core/src/components/eyebrows/AngryEyebrows.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const AngryEyebrows = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.3.3", 3 | "license": "MIT", 4 | "main": "dist/index.js", 5 | "typings": "dist/index.d.ts", 6 | "files": [ 7 | "dist", 8 | "src" 9 | ], 10 | "engines": { 11 | "node": ">=10" 12 | }, 13 | "scripts": { 14 | "start": "tsdx watch", 15 | "build": "tsdx build", 16 | "test": "tsdx test --passWithNoTests", 17 | "lint": "tsdx lint", 18 | "prepare": "tsdx build" 19 | }, 20 | "peerDependencies": { 21 | "react": ">=16" 22 | }, 23 | "husky": { 24 | "hooks": { 25 | "pre-commit": "tsdx lint" 26 | } 27 | }, 28 | "name": "beanheads", 29 | "author": "Robert Broersma", 30 | "module": "dist/core.esm.js", 31 | "devDependencies": { 32 | "@types/react": "^16.9.41", 33 | "@types/react-dom": "^16.9.8", 34 | "husky": "^4.2.5", 35 | "react": "^16.13.1", 36 | "react-dom": "^16.13.1", 37 | "tsdx": "^0.13.2", 38 | "tslib": "^2.0.0", 39 | "typescript": "^3.9.6" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /site/raw/SVG/bobback.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /site/raw/SVG/dressfront.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /site/raw/SVG/lefteyebrowlowered.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /site/raw/SVG/opensmile.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /site/raw/SVG/concernedeyebrows.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /site/raw/SVG/sad.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | 7 | steps: 8 | - name: Begin CI... 9 | uses: actions/checkout@v2 10 | 11 | - name: Use Node 12 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: 12.x 15 | 16 | - name: Use cached node_modules 17 | uses: actions/cache@v1 18 | with: 19 | path: node_modules 20 | key: nodeModules-${{ hashFiles('**/yarn.lock') }} 21 | restore-keys: | 22 | nodeModules- 23 | 24 | - name: Install dependencies 25 | run: yarn install --frozen-lockfile 26 | env: 27 | CI: true 28 | 29 | - name: Lint 30 | run: yarn lint 31 | env: 32 | CI: true 33 | 34 | - name: Test 35 | run: yarn test --ci --coverage --maxWorkers=2 36 | env: 37 | CI: true 38 | 39 | - name: Build 40 | run: yarn build 41 | env: 42 | CI: true 43 | -------------------------------------------------------------------------------- /site/raw/SVG/graphql.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /core/src/components/eyebrows/ConcernedEyebrows.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const ConcernedEyebrows = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /core/src/components/clothingGraphic/Gatsby.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const GatsbyGraphic = () => { 4 | return ( 5 | <> 6 | 12 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /core/src/components/eyebrows/Normal.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const NormalEyebrows = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /core/src/components/mouths/Grin.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const Grin = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 16 | 26 | 36 | 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /site/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "characters", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "beanheads": "^0.3.3", 8 | "gatsby": "^2.23.7", 9 | "gatsby-plugin-google-analytics": "^2.3.13", 10 | "gatsby-plugin-postcss": "^2.3.9", 11 | "gatsby-plugin-react-helmet": "^3.3.9", 12 | "gatsby-plugin-typescript": "^2.4.6", 13 | "prism-react-renderer": "^1.1.1", 14 | "query-string": "^6.13.1", 15 | "react": "^16.13.1", 16 | "react-dom": "^16.13.1", 17 | "react-feather": "^2.0.8", 18 | "react-helmet": "^6.1.0", 19 | "react-intersection-observer": "^8.26.2", 20 | "react-syntax-highlighter": "^12.2.1", 21 | "seedrandom": "^3.0.5" 22 | }, 23 | "scripts": { 24 | "dev": "gatsby develop", 25 | "build": "gatsby build", 26 | "serve": "gatsby serve", 27 | "clean": "gatsby clean" 28 | }, 29 | "devDependencies": { 30 | "@types/react-helmet": "^6.0.0", 31 | "@types/react-syntax-highlighter": "^11.0.4", 32 | "tailwindcss": "^1.4.6", 33 | "typescript": "^3.9.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/components/eyebrows/LeftLoweredEyebrows.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const LeftLoweredEyebrows = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 17 | 18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Robert Broersma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /core/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Robert Broersma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /site/raw/SVG/tongue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /core/src/components/mouths/OpenMouth.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const OpenMouth = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 10 | 14 | 18 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /site/raw/SVG/longhairfront_1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /core/src/components/mouths/SmileOpen.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const SmileOpen = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 17 | 24 | 28 | 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /core/src/components/mouths/Sad.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const SadMouth = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 16 | 20 | 24 | 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /core/src/components/eyes/DizzyEyes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const DizzyEyes = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 20 | 31 | 42 | 53 | 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /site/raw/SVG/wink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /site/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | junit.xml 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | examples/biz-website/public/ 30 | examples/blog/public/ 31 | integration-tests/gatsby-cli/execution-folder/* 32 | *.un~ 33 | dist 34 | bin/published.js 35 | 36 | # Build Path of Test Fixtures 37 | test/**/public 38 | .gatsby-context.js 39 | .DS_Store 40 | public/ 41 | node_modules/ 42 | e2e-tests/gatsby-pnp/ 43 | .cache/ 44 | .netlify 45 | 46 | # IDE specific 47 | .idea/ 48 | .vscode/ 49 | *.sw* 50 | 51 | # misc 52 | .serverless/ 53 | .eslintcache 54 | 55 | # lock files 56 | yarn.lock 57 | package-lock.json 58 | # ignore any lock file other than main lock file 59 | !/yarn.lock 60 | 61 | # starters are special; we want to persist the lock files 62 | !starters/*/package-lock.json 63 | !themes/gatsby-starter-blog-theme/package-lock.json 64 | !themes/gatsby-starter-notes-theme/package-lock.json 65 | !themes/gatsby-starter-theme/package-lock.json 66 | !themes/gatsby-starter-theme-workspace/package-lock.json -------------------------------------------------------------------------------- /site/raw/SVG/stubble.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /core/src/components/mouths/Tongue.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const Tongue = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 20 | 24 | 31 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | junit.xml 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | examples/biz-website/public/ 30 | examples/blog/public/ 31 | integration-tests/gatsby-cli/execution-folder/* 32 | *.un~ 33 | dist 34 | bin/published.js 35 | 36 | # Build Path of Test Fixtures 37 | test/**/public 38 | .gatsby-context.js 39 | .DS_Store 40 | public/ 41 | node_modules/ 42 | e2e-tests/gatsby-pnp/ 43 | .cache/ 44 | .netlify 45 | 46 | # IDE specific 47 | .idea/ 48 | .vscode/ 49 | *.sw* 50 | 51 | # misc 52 | .serverless/ 53 | .eslintcache 54 | 55 | # lock files 56 | yarn.lock 57 | package-lock.json 58 | # ignore any lock file other than main lock file 59 | !/yarn.lock 60 | 61 | # starters are special; we want to persist the lock files 62 | !starters/*/package-lock.json 63 | !themes/gatsby-starter-blog-theme/package-lock.json 64 | !themes/gatsby-starter-notes-theme/package-lock.json 65 | !themes/gatsby-starter-theme/package-lock.json 66 | !themes/gatsby-starter-theme-workspace/package-lock.json 67 | 68 | *.log 69 | .DS_Store 70 | node_modules 71 | .cache 72 | dist 73 | -------------------------------------------------------------------------------- /core/src/components/facialHair/Stubble.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const StubbleBeard = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /site/raw/SVG/dressback.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /site/raw/SVG/tinyglasses.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /site/src/utils/getRandomOptions.ts: -------------------------------------------------------------------------------- 1 | import { 2 | theme, 3 | eyesMap, 4 | eyebrowsMap, 5 | mouthsMap, 6 | hairMap, 7 | facialHairMap, 8 | clothingMap, 9 | accessoryMap, 10 | graphicsMap, 11 | hatMap, 12 | bodyMap, 13 | } from 'beanheads' 14 | 15 | function selectRandomKey(object: T) { 16 | return (Object.keys(object) as Array)[ 17 | Math.floor(Math.random() * Object.keys(object).length) 18 | ] 19 | } 20 | 21 | export function getRandomOptions() { 22 | const skinTone = selectRandomKey(theme.colors.skin) 23 | const eyes = selectRandomKey(eyesMap) 24 | const eyebrows = selectRandomKey(eyebrowsMap) 25 | const mouth = selectRandomKey(mouthsMap) 26 | const hair = selectRandomKey(hairMap) 27 | const facialHair = selectRandomKey(facialHairMap) 28 | const clothing = selectRandomKey(clothingMap) 29 | const accessory = selectRandomKey(accessoryMap) 30 | const graphic = selectRandomKey(graphicsMap) 31 | const hat = selectRandomKey(hatMap) 32 | const body = selectRandomKey(bodyMap) 33 | 34 | const hairColor = selectRandomKey(theme.colors.hair) 35 | const clothingColor = selectRandomKey(theme.colors.clothing) 36 | const circleColor = selectRandomKey(theme.colors.bgColors) 37 | const lipColor = selectRandomKey(theme.colors.lipColors) 38 | const hatColor = selectRandomKey(theme.colors.clothing) 39 | const faceMaskColor = selectRandomKey(theme.colors.clothing) 40 | 41 | const mask = true 42 | const faceMask = false 43 | const lashes = Math.random() > 0.5 44 | 45 | return { 46 | skinTone, 47 | eyes, 48 | eyebrows, 49 | mouth, 50 | hair, 51 | facialHair, 52 | clothing, 53 | accessory, 54 | graphic, 55 | hat, 56 | body, 57 | hairColor, 58 | clothingColor, 59 | circleColor, 60 | lipColor, 61 | hatColor, 62 | faceMaskColor, 63 | mask, 64 | faceMask, 65 | lashes, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /site/raw/SVG/afro_front.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /site/raw/SVG/roundglasses.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /site/raw/SVG/normal eyes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /core/src/components/eyes/Wink.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { EyeProps } from './types' 4 | import { LeftLash } from './Lashes' 5 | 6 | export const WinkEyes = ({ withLashes }: EyeProps) => { 7 | const { colors, skin } = useTheme() 8 | 9 | return ( 10 | <> 11 | 15 | 22 | 23 | 34 | 38 | 39 | {withLashes && } 40 | 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /core/src/components/clothingGraphic/GraphQL.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const GraphQLGraphic = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 14 | 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /site/src/components/SEO.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Helmet } from 'react-helmet' 3 | import { useLocation } from '@reach/router' 4 | import { useStaticQuery, graphql } from 'gatsby' 5 | 6 | interface SEOProps { 7 | title?: string 8 | description?: string 9 | image?: string 10 | } 11 | 12 | export const SEO = ({ title, description, image }: SEOProps) => { 13 | const { pathname } = useLocation() 14 | const { site } = useStaticQuery(query) 15 | const { 16 | defaultTitle, 17 | titleTemplate, 18 | defaultDescription, 19 | siteUrl, 20 | defaultImage, 21 | twitterUsername, 22 | } = site.siteMetadata 23 | 24 | const seo = { 25 | title: title || defaultTitle, 26 | description: description || defaultDescription, 27 | image: `${siteUrl}${image || defaultImage}`, 28 | url: `${siteUrl}${pathname}`, 29 | } 30 | 31 | return ( 32 | 71 | ) 72 | } 73 | 74 | const query = graphql` 75 | query SEO { 76 | site { 77 | siteMetadata { 78 | defaultTitle: title 79 | titleTemplate 80 | defaultDescription: description 81 | siteUrl: url 82 | defaultImage: image 83 | twitterUsername 84 | } 85 | } 86 | } 87 | ` 88 | -------------------------------------------------------------------------------- /core/src/components/hair/LongHair.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { HairProps } from './types' 4 | 5 | export const Back = ({ hairColor }: HairProps) => { 6 | const { colors } = useTheme() 7 | 8 | const { base } = colors.hair[hairColor] 9 | 10 | return ( 11 | <> 12 | 19 | 20 | ) 21 | } 22 | 23 | export const Front = ({ hairColor }: HairProps) => { 24 | const { colors, skin } = useTheme() 25 | 26 | const { base } = colors.hair[hairColor] 27 | 28 | return ( 29 | <> 30 | 34 | 38 | 42 | 46 | 53 | 54 | ) 55 | } 56 | 57 | export const hatScale = 1.12 58 | -------------------------------------------------------------------------------- /site/raw/SVG/redwood.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /site/raw/SVG/lips.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /core/src/theme.ts: -------------------------------------------------------------------------------- 1 | export const colors = { 2 | skin: { 3 | light: { 4 | base: '#fdd2b2', 5 | shadow: '#f3ab98', 6 | }, 7 | yellow: { 8 | base: '#FBE8B3', 9 | shadow: '#EDD494', 10 | }, 11 | brown: { 12 | base: '#D8985D', 13 | shadow: '#C6854E', 14 | }, 15 | dark: { 16 | base: '#A56941', 17 | shadow: '#8D5638', 18 | }, 19 | red: { 20 | base: '#CC734C', 21 | shadow: '#B56241', 22 | }, 23 | black: { 24 | base: '#754437', 25 | shadow: '#6B3D34', 26 | }, 27 | }, 28 | hair: { 29 | blonde: { 30 | base: '#FEDC58', 31 | shadow: '#EDBF2E', 32 | }, 33 | orange: { 34 | base: '#D96E27', 35 | shadow: '#C65C22', 36 | }, 37 | black: { 38 | base: '#592d3d', 39 | shadow: '#592d3d', 40 | }, 41 | white: { 42 | base: '#ffffff', 43 | shadow: '#E2E2E2', 44 | }, 45 | brown: { 46 | base: '#A56941', 47 | shadow: '#8D5638', 48 | }, 49 | blue: { 50 | base: '#85c5e5', 51 | shadow: '#67B7D6', 52 | }, 53 | pink: { 54 | base: '#D69AC7', 55 | shadow: '#C683B4', 56 | }, 57 | }, 58 | lipColors: { 59 | red: { 60 | base: '#DD3E3E', 61 | shadow: '#C43333', 62 | }, 63 | purple: { 64 | base: '#B256A1', 65 | shadow: '#9C4490', 66 | }, 67 | pink: { 68 | base: '#D69AC7', 69 | shadow: '#C683B4', 70 | }, 71 | turqoise: { 72 | base: '#5CCBF1', 73 | shadow: '#49B5CD', 74 | }, 75 | green: { 76 | base: '#4AB749', 77 | shadow: '#3CA047', 78 | }, 79 | }, 80 | clothing: { 81 | white: { 82 | base: '#FFFFFF', 83 | shadow: '#E2E2E2', 84 | }, 85 | blue: { 86 | base: '#85c5e5', 87 | shadow: '#67B7D6', 88 | }, 89 | black: { 90 | base: '#633749', 91 | shadow: '#5E3244', 92 | }, 93 | green: { 94 | base: '#89D86F', 95 | shadow: '#7DC462', 96 | }, 97 | red: { 98 | base: '#D67070', 99 | shadow: '#C46565', 100 | }, 101 | }, 102 | bgColors: { 103 | blue: '#85c5e5', 104 | // green: '#89D86F', 105 | // red: '#ED9191', 106 | }, 107 | outline: '#592d3d', 108 | white: '#ffffff', 109 | tongue: '#f28195', 110 | } 111 | -------------------------------------------------------------------------------- /core/src/components/clothingGraphic/Redwood.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { colors } from '../../theme' 3 | 4 | export const RedwoodGraphic = () => { 5 | return ( 6 | <> 7 | 8 | 9 | 14 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /core/exportParts.js: -------------------------------------------------------------------------------- 1 | const React = require('react') 2 | const RDS = require('react-dom/server') 3 | const fs = require('fs') 4 | const path = require('path') 5 | 6 | const { 7 | theme, 8 | eyesMap, 9 | eyebrowsMap, 10 | mouthsMap, 11 | hairMap, 12 | facialHairMap, 13 | clothingMap, 14 | accessoryMap, 15 | graphicsMap, 16 | hatMap, 17 | bodyMap, 18 | ThemeContext, 19 | Noop, 20 | } = require('./dist') 21 | 22 | const allMaps = { 23 | eyesMap, 24 | eyebrowsMap, 25 | mouthsMap, 26 | hairMap, 27 | facialHairMap, 28 | clothingMap, 29 | accessoryMap, 30 | graphicsMap, 31 | hatMap, 32 | bodyMap, 33 | } 34 | 35 | const skinTone = theme.colors.skin.light 36 | const hairColor = 'blonde' 37 | const clothingColor = 'blue' 38 | const circleColor = 'blue' 39 | const lipColor = 'red' 40 | const hatColor = 'blue' 41 | 42 | // const mask = true 43 | // const lashes = rng() > 0.5 44 | 45 | async function renderPart(Part, mapKey, key) { 46 | if (Part === Noop) return 47 | 48 | try { 49 | const partString = RDS.renderToString( 50 | React.createElement( 51 | ThemeContext.Provider, 52 | { value: { colors: theme.colors, skin: skinTone } }, 53 | React.createElement( 54 | 'svg', 55 | { 56 | xmlns: 'http://www.w3.org/2000/svg', 57 | viewBox: '0 0 1000 990', 58 | }, 59 | React.createElement(Part, { 60 | skinTone, 61 | hairColor, 62 | clothingColor, 63 | circleColor, 64 | lipColor, 65 | hatColor, 66 | }), 67 | ), 68 | ), 69 | ) 70 | 71 | await fs.promises.mkdir(path.join(__dirname, 'parts', mapKey), { 72 | recursive: true, 73 | }) 74 | 75 | await fs.promises.writeFile( 76 | path.join(__dirname, 'parts', mapKey, `${key}.svg`), 77 | partString, 78 | ) 79 | } catch (e) { 80 | console.error(e) 81 | } 82 | 83 | console.log(`${key} written`) 84 | } 85 | 86 | async function exportParts() { 87 | for (let [mapKey, map] of Object.entries(allMaps)) { 88 | for (let [key, Part] of Object.entries(map)) { 89 | if (Part.Front) { 90 | renderPart(Part.Front, mapKey, `${key}-front`) 91 | renderPart(Part.Back, mapKey, `${key}-back`) 92 | } else { 93 | renderPart(Part, mapKey, key) 94 | } 95 | } 96 | } 97 | } 98 | 99 | exportParts() 100 | -------------------------------------------------------------------------------- /site/raw/SVG/tanktop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /site/raw/SVG/breastsfront.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /site/raw/SVG/shorthair.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /core/src/components/accessories/TinyGlasses.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const TinyGlasses = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 16 | 21 | 30 | 34 | 38 | 45 | 50 | 59 | 63 | 67 | 74 | 78 | 79 | ) 80 | } 81 | -------------------------------------------------------------------------------- /core/src/components/eyes/NormalEyes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { EyeProps } from './types' 4 | import { LeftLash, RightLash } from './Lashes' 5 | 6 | export const NormalEyes = ({ withLashes }: EyeProps) => { 7 | const { skin, colors } = useTheme() 8 | 9 | return ( 10 | <> 11 | 15 | 22 | 23 | 27 | 34 | 35 | 36 | {withLashes && ( 37 | 38 | 39 | 40 | 41 | )} 42 | 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /site/raw/SVG/buzzcut.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /site/raw/SVG/hearteyes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /site/raw/SVG/afro_back.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /core/src/components/accessories/RoundGlasses.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const RoundGlasses = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 10 | 14 | 19 | 28 | 37 | 44 | 48 | 52 | 53 | 58 | 67 | 71 | 75 | 76 | ) 77 | } 78 | -------------------------------------------------------------------------------- /core/src/components/bodies/Chest.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { Noop } from '../../utils/Noop' 4 | 5 | export const Front = Noop 6 | 7 | export const Back = () => { 8 | const { skin, colors } = useTheme() 9 | return ( 10 | <> 11 | 15 | 19 | 23 | 27 | 31 | 35 | 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /core/src/components/mouths/Lips.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { MouthProps } from './types' 4 | 5 | export const Lips = ({ lipColor }: MouthProps) => { 6 | const { colors } = useTheme() 7 | 8 | const { base, shadow } = colors.lipColors[lipColor || 'red'] 9 | 10 | return ( 11 | <> 12 | {/* */} 16 | 20 | 24 | 28 | 32 | 39 | 43 | 47 | 48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /site/src/components/Hero.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { GitHub, Twitter, Coffee } from 'react-feather' 3 | 4 | export function Hero() { 5 | return ( 6 |
7 |
8 |
9 | hero 14 |
15 |
16 |

17 | Randomly Generated Characters for Your Apps & Games 18 |

19 |

20 | Combine expressions, clothing, hair styles and colors into billions 21 | of different unique characters. Embed them on your website, use them 22 | in your favourite design software, or import them from the React 23 | library! 24 |

25 |

26 | Free for personal and commercial use. Consider buying me a coffee 27 | and follow me on Twitter! 28 |

29 | 59 |
60 | ) 61 | } 62 | -------------------------------------------------------------------------------- /site/raw/SVG/react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /core/src/components/clothing/TankTop.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { ClothingProps } from './types' 4 | import { Noop } from '../../utils/Noop' 5 | 6 | export const TankTop = ({ color, graphic: Graphic = Noop }: ClothingProps) => { 7 | const { colors } = useTheme() 8 | 9 | const { base, shadow } = colors.clothing[color] 10 | 11 | return ( 12 | <> 13 | 17 | 21 | 25 | 29 | 36 | 40 | 47 | 54 | 55 | 56 | 57 | 58 | ) 59 | } 60 | -------------------------------------------------------------------------------- /site/functions/svg/svg.js: -------------------------------------------------------------------------------- 1 | const React = require('react') 2 | const RDS = require('react-dom/server') 3 | const seedrandom = require('seedrandom') 4 | 5 | const { 6 | BeanHead, 7 | theme, 8 | eyesMap, 9 | eyebrowsMap, 10 | mouthsMap, 11 | hairMap, 12 | facialHairMap, 13 | clothingMap, 14 | accessoryMap, 15 | graphicsMap, 16 | hatMap, 17 | bodyMap, 18 | } = require('beanheads') 19 | 20 | function getRandomOptions(rng) { 21 | function selectRandomKey(object) { 22 | return Object.keys(object)[Math.floor(rng() * Object.keys(object).length)] 23 | } 24 | 25 | const skinTone = selectRandomKey(theme.colors.skin) 26 | const eyes = selectRandomKey(eyesMap) 27 | const eyebrows = selectRandomKey(eyebrowsMap) 28 | const mouth = selectRandomKey(mouthsMap) 29 | const hair = selectRandomKey(hairMap) 30 | const facialHair = selectRandomKey(facialHairMap) 31 | const clothing = selectRandomKey(clothingMap) 32 | const accessory = selectRandomKey(accessoryMap) 33 | const graphic = selectRandomKey(graphicsMap) 34 | const hat = selectRandomKey(hatMap) 35 | const body = selectRandomKey(bodyMap) 36 | 37 | const hairColor = selectRandomKey(theme.colors.hair) 38 | const clothingColor = selectRandomKey(theme.colors.clothing) 39 | const circleColor = selectRandomKey(theme.colors.bgColors) 40 | const lipColor = selectRandomKey(theme.colors.lipColors) 41 | const hatColor = selectRandomKey(theme.colors.clothing) 42 | const faceMaskColor = selectRandomKey(theme.colors.clothing) 43 | 44 | const mask = true 45 | const faceMask = false 46 | const lashes = rng() > 0.5 47 | 48 | return { 49 | skinTone, 50 | eyes, 51 | eyebrows, 52 | mouth, 53 | hair, 54 | facialHair, 55 | clothing, 56 | accessory, 57 | graphic, 58 | hat, 59 | body, 60 | hairColor, 61 | clothingColor, 62 | circleColor, 63 | lipColor, 64 | hatColor, 65 | faceMaskColor, 66 | mask, 67 | faceMask, 68 | lashes, 69 | } 70 | } 71 | 72 | // Docs on event and context https://www.netlify.com/docs/functions/#the-handler-method 73 | exports.handler = async event => { 74 | try { 75 | let { seed, ...props } = event.queryStringParameters 76 | 77 | for (let [key, value] of Object.entries(props)) { 78 | if (value === 'false') { 79 | props[key] = false 80 | } 81 | 82 | if (value === 'true') { 83 | props[key] = true 84 | } 85 | } 86 | 87 | const rng = seed ? seedrandom(seed) : Math.random 88 | 89 | const mergedProps = { 90 | ...getRandomOptions(rng), 91 | ...props, 92 | } 93 | 94 | const avatarString = RDS.renderToString( 95 | React.createElement(BeanHead, mergedProps), 96 | ) 97 | 98 | return { 99 | statusCode: 200, 100 | body: avatarString, 101 | headers: { 102 | 'Content-Type': 'image/svg+xml', 103 | }, 104 | } 105 | } catch (err) { 106 | return { statusCode: 500, body: err.toString() } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /site/raw/SVG/shades.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /core/src/components/hair/ShortHair.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { HairProps } from './types' 4 | 5 | export const Back = () => <> 6 | 7 | export const Front = ({ hairColor }: HairProps) => { 8 | const { colors, skin } = useTheme() 9 | 10 | const { base, shadow } = colors.hair[hairColor] 11 | 12 | return ( 13 | <> 14 | 18 | 22 | 26 | 30 | 34 | 41 | 45 | 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /core/src/components/hair/BuzzCut.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { HairProps } from './types' 4 | import { Noop } from '../../utils/Noop' 5 | 6 | export const Back = Noop 7 | 8 | export const Front = ({ hairColor }: HairProps) => { 9 | const { colors } = useTheme() 10 | 11 | const { base, shadow } = colors.hair[hairColor] 12 | 13 | return ( 14 | <> 15 | 19 | 23 | 30 | 34 | 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /core/src/components/eyes/HeartEyes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { EyeProps } from './types' 4 | import { LeftLash, RightLash } from './Lashes' 5 | 6 | export const HeartEyes = ({ withLashes }: EyeProps) => { 7 | const { colors, skin } = useTheme() 8 | 9 | return ( 10 | <> 11 | 15 | 22 | 26 | 30 | 37 | 41 | 42 | {withLashes && ( 43 | <> 44 | 45 | 46 | 47 | )} 48 | 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /site/raw/SVG/vneck.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /site/raw/SVG/capfront.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /core/src/components/accessories/Shades.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | 4 | export const Shades = () => { 5 | const { colors } = useTheme() 6 | 7 | return ( 8 | <> 9 | 13 | 18 | 23 | 28 | 35 | 39 | 43 | 48 | 52 | 56 | 63 | 70 | 80 | 81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /site/raw/SVG/lefttwitcheye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /site/raw/SVG/twitch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /core/src/components/clothing/Dress.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { ClothingProps } from './types' 4 | 5 | export const Front = ({ color }: ClothingProps) => { 6 | const { colors, skin } = useTheme() 7 | 8 | const { base } = colors.clothing[color] 9 | 10 | return ( 11 | <> 12 | 19 | 23 | 30 | 34 | 35 | ) 36 | } 37 | 38 | export const Back = ({ color }: ClothingProps) => { 39 | const { colors } = useTheme() 40 | 41 | const { base, shadow } = colors.clothing[color] 42 | 43 | return ( 44 | <> 45 | 49 | 53 | 57 | 61 | 68 | 72 | 76 | 77 | ) 78 | } 79 | 80 | export const braStraps = false 81 | -------------------------------------------------------------------------------- /site/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState, useCallback, useMemo } from 'react' 2 | import qs from 'query-string' 3 | import { Link } from 'gatsby' 4 | import { BeanHead } from 'beanheads' 5 | import { useInView } from 'react-intersection-observer' 6 | 7 | import { Hero } from '../components/Hero' 8 | import { SEO } from '../components/SEO' 9 | import { getRandomOptions } from '../utils/getRandomOptions' 10 | 11 | const RandomAvatar = () => { 12 | const options = getRandomOptions() 13 | 14 | const queryString = qs.stringify(options) 15 | 16 | return ( 17 | 18 | 19 | 20 | ) 21 | } 22 | 23 | const RandomAvatars = React.memo( 24 | ({ onReachEnd, page }: { onReachEnd(page: number): void; page: number }) => { 25 | const [ref, inView] = useInView() 26 | 27 | useEffect(() => { 28 | if (inView) { 29 | onReachEnd(page) 30 | } 31 | }, [inView]) 32 | 33 | return useMemo( 34 | () => ( 35 | <> 36 |
37 | 38 |
39 |
40 | 41 |
42 |
43 | 44 |
45 |
46 | 47 |
48 |
49 | 50 |
51 |
52 | 53 |
54 |
55 | 56 |
57 |
58 | 59 |
60 |
61 | 62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 | 71 |
72 | 73 | ), 74 | [], 75 | ) 76 | }, 77 | ) 78 | 79 | const Home = () => { 80 | const onReachEnd = useCallback(page => { 81 | setPages(current => { 82 | if (page === current.length) { 83 | return [ 84 | ...current, 85 | , 90 | ] 91 | } 92 | 93 | return current 94 | }) 95 | }, []) 96 | 97 | const [pages, setPages] = useState([ 98 | , 99 | ]) 100 | 101 | if (typeof window === 'undefined') { 102 | return 103 | } 104 | 105 | return ( 106 | <> 107 | 108 | 109 |
110 |
111 |

112 | More Bean Heads 113 |

114 |

115 | Click on any character to edit, save or embed! 116 |

117 |
118 | {pages} 119 |
120 |
121 |
122 | 123 | ) 124 | } 125 | 126 | export default Home 127 | -------------------------------------------------------------------------------- /site/raw/SVG/squinteyes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /site/raw/SVG/baldinghair.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /site/raw/SVG/breasts.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /site/raw/SVG/eyeswithlashes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /site/raw/SVG/shirt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /core/src/components/FaceMask.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../themeContext' 3 | import { colors as themeColors } from '../theme' 4 | 5 | interface FaceMaskProps { 6 | color: keyof typeof themeColors.clothing 7 | } 8 | 9 | export const FaceMask = ({ color }: FaceMaskProps) => { 10 | const { colors, skin } = useTheme() 11 | const { base, shadow } = colors.clothing[color] 12 | 13 | return ( 14 | <> 15 | 19 | 26 | 30 | 34 | 38 | 42 | 49 | 56 | 63 | 64 | ) 65 | } 66 | -------------------------------------------------------------------------------- /core/src/components/eyes/LeftTwitchEyes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { EyeProps } from './types' 4 | import { LeftLash, RightLash } from './Lashes' 5 | 6 | export const LeftTwitchEyes = ({ withLashes }: EyeProps) => { 7 | const { skin, colors } = useTheme() 8 | 9 | return ( 10 | <> 11 | 15 | 19 | 20 | 27 | 34 | 38 | 45 | 46 | 50 | 54 | 55 | {withLashes && ( 56 | 57 | 58 | 59 | 60 | )} 61 | 62 | ) 63 | } 64 | -------------------------------------------------------------------------------- /core/src/components/clothing/VNeck.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { ClothingProps } from './types' 4 | import { Noop } from '../../utils/Noop' 5 | 6 | export const VNeck = ({ color, graphic: Graphic = Noop }: ClothingProps) => { 7 | const { colors, skin } = useTheme() 8 | 9 | const { base, shadow } = colors.clothing[color] 10 | 11 | return ( 12 | <> 13 | 17 | 25 | 29 | 33 | 37 | 41 | 45 | 49 | 57 | 65 | 69 | 73 | 80 | 84 | 91 | 99 | 103 | 110 | 111 | 112 | 113 | 114 | ) 115 | } 116 | -------------------------------------------------------------------------------- /core/src/components/eyes/SquintEyes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useTheme } from '../../themeContext' 3 | import { EyeProps } from './types' 4 | import { LeftLash, RightLash } from './Lashes' 5 | 6 | export const SquintEyes = ({ withLashes }: EyeProps) => { 7 | const { colors, skin } = useTheme() 8 | 9 | return ( 10 | <> 11 | 15 | 19 | 20 | 27 | 34 | 41 | 45 | 49 | 50 | 57 | 64 | 71 | 72 | {withLashes && ( 73 | <> 74 | 75 | 76 | 77 | )} 78 | 79 | ) 80 | } 81 | --------------------------------------------------------------------------------