diff --git a/.eslintrc.json b/.eslintrc.json index fac5da5..4a62ec9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -20,7 +20,9 @@ "react/jsx-filename-extension": [1, { "extensions": [".js", ".ts", "tsx"] }], "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-member-accessibility": "off", - "@typescript-eslint/member-delimiter-style": "off" + "@typescript-eslint/member-delimiter-style": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/ban-ts-ignore": "off" }, "overrides": [ { diff --git a/.gitignore b/.gitignore index 20fccdd..fd07293 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ yarn-error.log* .env.development.local .env.test.local .env.production.local + +.now \ No newline at end of file diff --git a/.storybook/logo.png b/.storybook/logo.png index 9c5b9ac..0b09ba2 100644 Binary files a/.storybook/logo.png and b/.storybook/logo.png differ diff --git a/.storybook/main.js b/.storybook/main.js index d8754b5..44cf475 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -8,7 +8,17 @@ module.exports = { // Make whatever fine-grained changes you need baseConfig.module.rules.push({ test: /\.scss$/, - use: ['style-loader', 'css-loader', 'sass-loader'], + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + importLoaders: 1, + modules: true, + }, + }, + 'sass-loader', + ], include: path.resolve(__dirname, '../'), }) diff --git a/.storybook/preview-body.html b/.storybook/preview-body.html new file mode 100644 index 0000000..c2938b6 --- /dev/null +++ b/.storybook/preview-body.html @@ -0,0 +1 @@ + diff --git a/.storybook/styles.css b/.storybook/styles.css index b7739c2..5d020a0 100644 --- a/.storybook/styles.css +++ b/.storybook/styles.css @@ -1,4 +1,12 @@ .sb-show-main { margin: 0; background: inherit; + transition: background 0.2s ease; +} + +.sb-show-main.light-mode { + background: #fff; +} +.sb-show-main.dark-mode { + background: #191a1f; } diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index a4ff0b3..a8717b6 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -26,6 +26,7 @@ module.exports = ({ config, mode }) => { config.resolve.alias['components'] = path.join(__dirname, '../components') config.resolve.alias['pages'] = path.join(__dirname, '../pages') config.resolve.alias['styles'] = path.join(__dirname, '../styles') + config.resolve.alias['utils'] = path.join(__dirname, '../utils') config.resolve.alias['hooks'] = path.join(__dirname, '../hooks') if (mode === 'DEVELOPMENT') { diff --git a/.stylelintrc.json b/.stylelintrc.json index 99f9f2c..f52b8ef 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -1,5 +1,10 @@ { - "extends": ["stylelint-config-recess-order", "stylelint-config-standard", "stylelint-config-prettier"], + "extends": [ + "stylelint-config-recess-order", + "stylelint-config-standard", + "stylelint-config-css-modules", + "stylelint-config-prettier" + ], "plugins": ["stylelint-prettier"], "rules": { "prettier/prettier": true diff --git a/.vscode/settings.json b/.vscode/settings.json index 7428127..a2d1840 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,17 +1,13 @@ { - "editor.formatOnSave": true, - "eslint.validate": [ - "javascript", - "javascriptreact", - "typescript", - "typescriptreact" - ], - "eslint.run": "onType", - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true, - "source.fixAll.stylelint": true - }, - "css.validate": false, - "less.validate": false, - "scss.validate": false + "editor.formatOnSave": true, + "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], + "eslint.run": "onType", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true, + "source.fixAll.stylelint": true + }, + "css.validate": false, + "less.validate": false, + "scss.validate": false, + "cssModules.camelCase": true } diff --git a/README.md b/README.md index bb9ba64..18f2794 100644 --- a/README.md +++ b/README.md @@ -20,25 +20,25 @@ Favicon generator tool ## Favicon recommended sizes -| | apple-touch-icon | icon | msapplication | -| ------- | ---------------- | ---- | ------------- | -| 57x57 | ✅ | | | -| 60x60 | ✅ | | | -| 72x72 | ✅ | | | -| 76x76 | ✅ | | | -| 114x114 | ✅ | | | -| 120x120 | ✅ | | | -| 144x144 | ✅ | | ✅ | -| 152x152 | ✅ | | | -| 180x180 | ✅ | | | -| 16x16 | | ✅ | | -| 32x32 | | ✅ | | -| 96x96 | | ✅ | | -| 192x192 | | ✅ | | -| 70x70 | | | ✅ | -| 150x150 | | | ✅ | -| 310x150 | | | ✅ | -| 310x310 | | | ✅ | +| | apple-icon | favicon | ms-icon | +| ------- | ---------- | ------- | ------- | +| 57x57 | ✅ | | | +| 60x60 | ✅ | | | +| 72x72 | ✅ | | | +| 76x76 | ✅ | | | +| 114x114 | ✅ | | | +| 120x120 | ✅ | | | +| 144x144 | ✅ | | ✅ | +| 152x152 | ✅ | | | +| 180x180 | ✅ | | | +| 16x16 | | ✅ | | +| 32x32 | | ✅ | | +| 96x96 | | ✅ | | +| 192x192 | | ✅ | | +| 70x70 | | | ✅ | +| 150x150 | | | ✅ | +| 310x150 | | | ✅ | +| 310x310 | | | ✅ | ## HTML tags to add for all favicon sizes @@ -58,7 +58,7 @@ Favicon generator tool - + ``` @@ -67,16 +67,28 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next ## Getting Started -First, run the development server: +First, install the project dependencies: + +```bash +yarn install +``` + +Then, run the development server: ```bash -npm run dev -# or yarn dev ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Also, you can run the Storybook server: + +```bash +yarn storybook +``` + +Open [http://localhost:6006](http://localhost:6006) with your browser to see all stories. + ## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): diff --git a/components/base-layout/index.module.scss b/components/base-layout/index.module.scss new file mode 100644 index 0000000..5d4185b --- /dev/null +++ b/components/base-layout/index.module.scss @@ -0,0 +1,16 @@ +@import '../../styles/variables.scss'; + +.root { + padding: 0; +} + +.container { + max-width: 1440px; + padding: 20px; +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; +} diff --git a/components/base-layout/index.tsx b/components/base-layout/index.tsx new file mode 100644 index 0000000..c2bbb3e --- /dev/null +++ b/components/base-layout/index.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { DarkModeToggle } from 'components/dark-mode-toggle' + +import styles from './index.module.scss' +import { Typography } from 'components/typography' + +type BaseLayoutProps = { + children: PropTypes.ReactNodeLike +} + +const BaseLayout = ({ children }: BaseLayoutProps) => { + return ( +
+
+
+ Menu + +
+
+
{children}
+
+ ) +} + +export { BaseLayout } diff --git a/components/button/index.module.scss b/components/button/index.module.scss new file mode 100644 index 0000000..198eea1 --- /dev/null +++ b/components/button/index.module.scss @@ -0,0 +1,61 @@ +@import '../../styles/variables.scss'; + +.base { + padding: 6px 12px; + font-size: $font-size-medium-body; + line-height: $line-height-medium-body; + color: $black; + cursor: pointer; + border-radius: 4px; +} + +.primary { + composes: base; + background-color: $off-white; + border: 1px solid $light-grey; + box-shadow: 0 2px 4px 0 rgba($black, 0.12); + transition: all 0.2s ease; + + &:focus { + outline: none; + } + + &:disabled { + color: $grey; + } +} + +.noClick { + cursor: default; +} + +.transparent { + composes: base; + border: 1px solid transparent; +} + +.dark { + color: $white; + background-color: $darker-grey; + border: 1px solid $darker-grey; + + &:disabled { + color: $grey; + } +} + +.regular { + font-weight: 400; +} + +.medium { + font-weight: 500; +} + +.semiBold { + font-weight: 600; +} + +.bold { + font-weight: 700; +} diff --git a/components/button/index.stories.tsx b/components/button/index.stories.tsx new file mode 100644 index 0000000..9177853 --- /dev/null +++ b/components/button/index.stories.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import { withKnobs, text, select, boolean } from '@storybook/addon-knobs' +import { Button } from '.' +import useDarkMode from 'use-dark-mode' + +export default { + title: 'Button', + decorators: [withKnobs], +} + +export const ButtonDesktop = () => { + const { toggle } = useDarkMode() + const variant = select('variant', ['primary', 'transparent'], 'primary') + const weight = select('weight', ['regular', 'medium', 'semiBold', 'bold'], 'bold') + const body = text('body', 'Mac app coming soon') + const disabled = boolean('disabled', false) + return ( +
+ +
+
+ +
+ ) +} + +ButtonDesktop.story = { + name: 'Desktop', +} diff --git a/components/button/index.tsx b/components/button/index.tsx new file mode 100644 index 0000000..af2c84d --- /dev/null +++ b/components/button/index.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import PropTypes from 'prop-types' +import classNames from 'classnames' +import useDarkMode from 'use-dark-mode' + +import styles from './index.module.scss' + +type ButtonProps = { + children: PropTypes.ReactNodeLike + variant: 'primary' | 'transparent' + weight: 'regular' | 'medium' | 'semiBold' | 'bold' +} + +const Button = ({ + children, + variant, + weight, + ...props +}: ButtonProps & React.DetailedHTMLProps, HTMLButtonElement>) => { + const { value: isDark } = useDarkMode(false) + const className = classNames(styles[variant], styles[weight], { + [styles.dark]: isDark, + [styles.noClick]: !props.onClick, + }) + + return ( + + ) +} + +Button.defaultProps = { + variant: 'primary', + weight: 'bold', +} + +export { Button } diff --git a/components/dark-mode-toggle/index.module.scss b/components/dark-mode-toggle/index.module.scss new file mode 100644 index 0000000..ad95fe5 --- /dev/null +++ b/components/dark-mode-toggle/index.module.scss @@ -0,0 +1,31 @@ +@import '../../styles/variables.scss'; + +.root { + position: relative; +} + +.label { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + cursor: pointer; + user-select: none; +} + +.checkbox { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + margin: 0; + overflow: hidden; + cursor: pointer; + visibility: hidden; + outline: none; + -webkit-appearance: none; + -moz-appearance: none; + -webkit-tap-highlight-color: transparent; +} diff --git a/components/dark-mode-toggle/index.stories.tsx b/components/dark-mode-toggle/index.stories.tsx new file mode 100644 index 0000000..f843fae --- /dev/null +++ b/components/dark-mode-toggle/index.stories.tsx @@ -0,0 +1,24 @@ +import React from 'react' +import { DarkModeToggle } from '.' +import useDarkMode from 'use-dark-mode' +import { Typography } from 'components/typography' + +export default { + title: 'Dark Mode Toggle', +} + +export const ToggleDesktop = () => { + const darkMode = useDarkMode(false) + + return ( +
+ +
+ dark mode: {darkMode.value ? 'enabled' : 'disabled'} +
+ ) +} + +ToggleDesktop.story = { + name: 'Desktop', +} diff --git a/components/dark-mode-toggle/index.tsx b/components/dark-mode-toggle/index.tsx new file mode 100644 index 0000000..ac629c2 --- /dev/null +++ b/components/dark-mode-toggle/index.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import useDarkMode from 'use-dark-mode' +import { SvgSun } from 'components/svgs/svg-sun' +import { SvgMoon } from 'components/svgs/svg-moon' + +import styles from './index.module.scss' + +const DarkModeToggle = () => { + const darkMode = useDarkMode(false) + + return ( +
+ + +
+ ) +} + +export { DarkModeToggle } diff --git a/components/drag-and-drop/index.module.scss b/components/drag-and-drop/index.module.scss new file mode 100644 index 0000000..c415eac --- /dev/null +++ b/components/drag-and-drop/index.module.scss @@ -0,0 +1,101 @@ +@import '../../styles/variables.scss'; + +.root { + width: 100%; + max-width: 476px; + padding: 24px 24px 20px; + background: $white; + border-radius: 8px; + box-shadow: 0 2px 4px rgba($black, 0.12); + transition: background 0.2s ease; + + &.dark { + background: $darker-grey; + } +} + +.dropZoneWrapper { + position: relative; + height: 244px; + margin-bottom: 16px; + text-align: center; + background: $off-white; + border-radius: 6px; + transition: background 0.2s ease; + + &.dark { + background: $dark-grey; + } +} + +.dashed { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + border-radius: 6px; + + rect { + transition: stroke 0.2s ease; + animation: dash 0.75s linear infinite forwards; + animation-play-state: paused; + } + + &.dragActive rect { + animation-play-state: running; + } +} + +@keyframes dash { + 0% { + stroke-dashoffset: 0; + } + 100% { + stroke-dashoffset: -20; + } +} + +.dropZone { + position: relative; + z-index: 2; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + + &:focus { + outline: none; + } +} + +.imageUpload { + margin-bottom: 20px; +} + +.imageUploadText { + u { + font-weight: 700; + color: $link; + cursor: pointer; + transition: color 0.2s ease; + } + + &.dark u { + color: $link-dark; + } +} + +.info { + display: flex; + align-items: center; + margin-top: 8px; + + svg { + flex: 0 0 20px; + margin-right: 8px; + } +} diff --git a/components/drag-and-drop/index.stories.tsx b/components/drag-and-drop/index.stories.tsx new file mode 100644 index 0000000..65bf73e --- /dev/null +++ b/components/drag-and-drop/index.stories.tsx @@ -0,0 +1,27 @@ +import React from 'react' +import useDarkMode from 'use-dark-mode' +import { DragAndDrop } from '.' +import { Button } from 'components/button' + +export default { + title: 'Drag & Drop', +} + +export const DragAndDropDesktop = () => { + const { toggle } = useDarkMode(false) + const onDrop = (acceptedFiles: File[]) => { + console.log('acceptedFiles[0]', acceptedFiles[0]) + } + + return ( +
+ +
+ +
+ ) +} + +DragAndDropDesktop.story = { + name: 'Desktop', +} diff --git a/components/drag-and-drop/index.tsx b/components/drag-and-drop/index.tsx new file mode 100644 index 0000000..8e804bc --- /dev/null +++ b/components/drag-and-drop/index.tsx @@ -0,0 +1,48 @@ +import React from 'react' +import classnames from 'classnames' +import useDarkMode from 'use-dark-mode' +import { useDropzone } from 'react-dropzone' +import { Typography } from 'components/typography' +import { SvgDropZone } from 'components/svgs/svg-drop-zone' +import { SvgImageUpload } from 'components/svgs/svg-image-upload' +import { SvgInfo } from 'components/svgs/svg-info' + +import styles from './index.module.scss' + +type DragAndDropProps = { + onDrop: (acceptedFiles: File[]) => void +} + +const DragAndDrop = ({ onDrop }: DragAndDropProps) => { + const { value: isDark } = useDarkMode(false) + const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }) + return ( +
+
+ +
+ +
+ +
+ + Drag & drop a .png file or +
+ click here to upload it. +
+
+
+ + We recommend a square png image with at least 310px + + + No data or images are stored + +
+ ) +} + +export { DragAndDrop } diff --git a/components/lazy-image/index.module.scss b/components/lazy-image/index.module.scss new file mode 100644 index 0000000..9388e59 --- /dev/null +++ b/components/lazy-image/index.module.scss @@ -0,0 +1,29 @@ +@import '../../styles/variables.scss'; + +.root { + position: relative; + width: 100%; +} + +.image { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + font-family: 'object-fit: cover; object-position: center;', sans-serif; + object-fit: cover; + object-position: center; +} + +.placeholder { + composes: image; + transition: opacity 0.2s linear 0.2s; +} + +.normal { + composes: image; + transition: opacity 0.2s linear; +} diff --git a/components/lazy-image/index.tsx b/components/lazy-image/index.tsx new file mode 100644 index 0000000..3e302ca --- /dev/null +++ b/components/lazy-image/index.tsx @@ -0,0 +1,54 @@ +import React, { useRef, useEffect } from 'react' +import { LazyImageFull, ImageState } from 'react-lazy-images' +import objectFitImages from 'object-fit-images' + +import styles from './index.module.scss' + +type LazyImageProps = { + src: string + srcRetina: string + srcPlaceholder: string + alt: string + aspectRatio: string +} + +const LazyImage = ({ src, srcRetina, srcPlaceholder, alt, aspectRatio }: LazyImageProps) => { + const [widthString, heightString] = aspectRatio.split('/') + const width = parseInt(widthString) + const height = parseInt(heightString) + + const imgRef = useRef(null) + + useEffect(() => { + if (imgRef.current) { + objectFitImages(imgRef.current) + } + }, [imgRef]) + + return ( + + {({ imageProps, imageState, ref }) => { + const loaded = imageState === ImageState.LoadSuccess + return ( +
+ {imageProps.alt} + {imageProps.alt} +
+ ) + }} +
+ ) +} + +export { LazyImage } diff --git a/components/seo/index.tsx b/components/seo/index.tsx new file mode 100644 index 0000000..9ed8096 --- /dev/null +++ b/components/seo/index.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import Head from 'next/head' + +type SEOProps = { + title: string + description: string +} + +const SEO = ({ title, description }: SEOProps) => ( + + {title} + + + + + + + + + + + + + + + + +) + +export { SEO } diff --git a/components/svgs/svg-appycon.tsx b/components/svgs/svg-appycon.tsx new file mode 100644 index 0000000..977551e --- /dev/null +++ b/components/svgs/svg-appycon.tsx @@ -0,0 +1,45 @@ +import React from 'react' +import { uniqueId } from 'utils/ids' + +const SvgAppycon: React.FC> = (props) => { + const idPrefix = uniqueId('svg-') + return ( + + Appycon logo + + + + + + + + + + + + + + + + + + + + + + ) +} + +export { SvgAppycon } diff --git a/components/svgs/svg-arrow-down.tsx b/components/svgs/svg-arrow-down.tsx new file mode 100644 index 0000000..5231ea1 --- /dev/null +++ b/components/svgs/svg-arrow-down.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import useDarkMode from 'use-dark-mode' +import { uniqueId } from 'utils/ids' + +const SvgArrowDown: React.FC> = (props) => { + const { value: isDark } = useDarkMode(false) + const idPrefix = uniqueId('svg-') + return ( + + Arrow down + + + ) +} + +export { SvgArrowDown } diff --git a/components/svgs/svg-drop-zone.tsx b/components/svgs/svg-drop-zone.tsx new file mode 100644 index 0000000..128786c --- /dev/null +++ b/components/svgs/svg-drop-zone.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import useDarkMode from 'use-dark-mode' + +const SvgDropZone: React.FC> = (props) => { + const { value: isDark } = useDarkMode(false) + return ( + + + + + ) +} + +export { SvgDropZone } diff --git a/components/svgs/svg-favycon.tsx b/components/svgs/svg-favycon.tsx new file mode 100644 index 0000000..bdb75ea --- /dev/null +++ b/components/svgs/svg-favycon.tsx @@ -0,0 +1,45 @@ +import React from 'react' +import { uniqueId } from 'utils/ids' + +const SvgFavycon: React.FC> = (props) => { + const idPrefix = uniqueId('svg-') + return ( + + Favycon logo + + + + + + + + + + + + + + + + + + + + + + ) +} + +export { SvgFavycon } diff --git a/components/svgs/svg-image-upload.tsx b/components/svgs/svg-image-upload.tsx new file mode 100644 index 0000000..009624d --- /dev/null +++ b/components/svgs/svg-image-upload.tsx @@ -0,0 +1,33 @@ +import React from 'react' +import useDarkMode from 'use-dark-mode' +import { uniqueId } from 'utils/ids' + +const SvgImageUpload: React.FC> = (props) => { + const { value: isDark } = useDarkMode(false) + const idPrefix = uniqueId('svg-') + return ( + + Image Upload + + + + + + + ) +} + +export { SvgImageUpload } diff --git a/components/svgs/svg-info.tsx b/components/svgs/svg-info.tsx new file mode 100644 index 0000000..718f0b9 --- /dev/null +++ b/components/svgs/svg-info.tsx @@ -0,0 +1,30 @@ +import React from 'react' +import useDarkMode from 'use-dark-mode' +import { uniqueId } from 'utils/ids' + +const SvgInfo: React.FC> = (props) => { + const { value: isDark } = useDarkMode(false) + const idPrefix = uniqueId('svg-') + return ( + + Info + + + + + ) +} + +export { SvgInfo } diff --git a/components/svgs/svg-moon.tsx b/components/svgs/svg-moon.tsx new file mode 100644 index 0000000..8569721 --- /dev/null +++ b/components/svgs/svg-moon.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import { uniqueId } from 'utils/ids' + +const SvgMoon: React.FC> = (props) => { + const idPrefix = uniqueId('svg-') + return ( + + Activate dark mode + + + ) +} + +export { SvgMoon } diff --git a/components/svgs/svg-sun.tsx b/components/svgs/svg-sun.tsx new file mode 100644 index 0000000..21721c1 --- /dev/null +++ b/components/svgs/svg-sun.tsx @@ -0,0 +1,17 @@ +import React from 'react' +import { uniqueId } from 'utils/ids' + +const SvgSun: React.FC> = (props) => { + const idPrefix = uniqueId('svg-') + return ( + + Activate light mode + + + ) +} + +export { SvgSun } diff --git a/components/tool-title/index.module.scss b/components/tool-title/index.module.scss new file mode 100644 index 0000000..a8dc65b --- /dev/null +++ b/components/tool-title/index.module.scss @@ -0,0 +1,6 @@ +@import '../../styles/variables.scss'; + +.root { + display: flex; + align-items: flex-start; +} diff --git a/components/tool-title/index.stories.tsx b/components/tool-title/index.stories.tsx new file mode 100644 index 0000000..e5ccd3d --- /dev/null +++ b/components/tool-title/index.stories.tsx @@ -0,0 +1,26 @@ +import React from 'react' +import { withKnobs, text } from '@storybook/addon-knobs' +import useDarkMode from 'use-dark-mode' +import { Button } from 'components/button' +import { ToolTitle } from '.' + +export default { + title: 'Tool Title', + decorators: [withKnobs], +} + +export const ToolTitleDesktop = () => { + const { toggle } = useDarkMode(false) + const body = text('body', 'Favycon') + return ( +
+ {body} +
+ +
+ ) +} + +ToolTitleDesktop.story = { + name: 'desktop', +} diff --git a/components/tool-title/index.tsx b/components/tool-title/index.tsx new file mode 100644 index 0000000..2e1e13b --- /dev/null +++ b/components/tool-title/index.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import { Typography } from 'components/typography' + +import styles from './index.module.scss' + +type ToolTitleProps = { + children: string +} + +const ToolTitle = ({ children }: ToolTitleProps) => ( +
+ + {children} + + + TOOL + +
+) + +export { ToolTitle } diff --git a/components/typography/index.module.scss b/components/typography/index.module.scss index b86a4c3..5753995 100644 --- a/components/typography/index.module.scss +++ b/components/typography/index.module.scss @@ -28,6 +28,16 @@ .footer { font-size: $font-size-footer; line-height: $line-height-footer; + + a { + color: inherit; + + &:focus, + &:active, + &:hover { + color: inherit; + } + } } .superscript { @@ -53,12 +63,22 @@ .black { color: $black; + transition: color 0.2s ease; + + &.dark { + color: $white; + } } .gray { - color: $gray; + color: $grey; } .white { color: $white; + transition: color 0.2s ease; + + &.dark { + color: $black; + } } diff --git a/components/typography/index.stories.tsx b/components/typography/index.stories.tsx new file mode 100644 index 0000000..46545e9 --- /dev/null +++ b/components/typography/index.stories.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { withKnobs, text, select } from '@storybook/addon-knobs' +import useDarkMode from 'use-dark-mode' +import { Button } from 'components/button' +import { Typography } from '.' + +export default { + title: 'Typography', + decorators: [withKnobs], +} + +export const TypographyDesktop = () => { + const { toggle } = useDarkMode(false) + const variant = select( + 'variant', + ['h1', 'smallBody', 'regularBody', 'mediumBody', 'largeBody', 'footer', 'superscript'], + 'h1' + ) + const weight = select('weight', ['regular', 'medium', 'semiBold', 'bold'], 'regular') + const color = select('color', ['black', 'gray', 'white'], 'black') + const body = text('body', 'Some text') + return ( +
+ + {body} + +
+ +
+ ) +} + +TypographyDesktop.story = { + name: 'Desktop', +} diff --git a/components/typography/index.tsx b/components/typography/index.tsx index 3f6d19d..1d7a7f0 100644 --- a/components/typography/index.tsx +++ b/components/typography/index.tsx @@ -1,6 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import classNames from 'classnames' +import useDarkMode from 'use-dark-mode' import styles from './index.module.scss' @@ -10,13 +11,20 @@ type TypographyProps = { weight: 'regular' | 'medium' | 'semiBold' | 'bold' color: 'black' | 'gray' | 'white' tag?: string -} +} & React.DetailedHTMLProps, HTMLParagraphElement> -const Typography = ({ children, variant, weight, color, tag }: TypographyProps) => { - const className = classNames(styles[variant], styles[color], styles[weight]) +const Typography = ({ children, variant, weight, color, tag, ...props }: TypographyProps) => { + const { value: isDark } = useDarkMode(false) + const className = classNames( + styles[variant], + styles[color], + styles[weight], + { [styles.dark]: isDark }, + props.className + ) const componentType = ['h1'].includes(variant) ? variant : 'p' - return React.createElement(tag || componentType, { className }, children) + return React.createElement(tag || componentType, { ...props, className }, children) } Typography.defaultProps = { diff --git a/next.config.js b/next.config.js index 91559f1..754efb3 100644 --- a/next.config.js +++ b/next.config.js @@ -27,6 +27,9 @@ module.exports = { } config.resolve.alias['components'] = path.join(__dirname, 'components') + config.resolve.alias['pages'] = path.join(__dirname, 'pages') + config.resolve.alias['styles'] = path.join(__dirname, 'styles') + config.resolve.alias['utils'] = path.join(__dirname, 'utils') config.resolve.alias['hooks'] = path.join(__dirname, 'hooks') config.resolve.alias['react'] = 'preact/compat' config.resolve.alias['react-dom'] = 'preact/compat' diff --git a/package.json b/package.json index d16a6b8..4d7d178 100644 --- a/package.json +++ b/package.json @@ -38,14 +38,22 @@ "commit": "git-cz" }, "dependencies": { + "@fiahfy/ico-convert": "0.0.7", + "adm-zip": "0.4.14", "classnames": "2.2.6", + "multer": "1.4.2", "next": "9.3.4", + "object-fit-images": "3.2.4", "preact": "10.4.0", "preact-render-to-string": "5.1.6", "preact-ssr-prepass": "1.0.1", "prop-types": "15.7.2", "react": "16.13.1", - "react-dom": "16.13.1" + "react-dom": "16.13.1", + "react-dropzone": "10.2.2", + "react-lazy-images": "1.1.0", + "sharp": "0.25.2", + "use-dark-mode": "2.3.1" }, "devDependencies": { "@babel/core": "7.9.0", @@ -61,9 +69,13 @@ "@storybook/addons": "5.3.18", "@storybook/react": "5.3.18", "@storybook/source-loader": "5.3.18", + "@types/adm-zip": "0.4.33", "@types/classnames": "2.2.10", + "@types/multer": "1.4.2", "@types/node": "13.11.1", + "@types/object-fit-images": "3.2.0", "@types/react": "16.9.34", + "@types/sharp": "0.24.0", "@typescript-eslint/eslint-plugin": "2.27.0", "@typescript-eslint/parser": "2.27.0", "all-contributors-cli": "6.14.1", @@ -92,6 +104,7 @@ "sass-loader": "8.0.2", "semantic-release": "17.0.4", "stylelint": "13.3.1", + "stylelint-config-css-modules": "2.2.0", "stylelint-config-prettier": "8.0.1", "stylelint-config-recess-order": "2.0.4", "stylelint-config-standard": "20.0.0", diff --git a/pages/_document.tsx b/pages/_document.tsx new file mode 100644 index 0000000..630dcf2 --- /dev/null +++ b/pages/_document.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import Document, { Html, Head, Main, NextScript } from 'next/document' + +class MyDocument extends Document { + render() { + return ( + + + +