diff --git a/README.md b/README.md index 0c48213..252653c 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,42 @@ # testnet-onboarding-app + React app for onboarding participants to laconicd chain with Nitro/Cosmos key attestation ## Setup for testnet-onboarding-app 1. Clone the repository - ``` + ```zsh git clone git@git.vdb.to:cerc-io/testnet-onboarding-app.git ``` 2. Enter the project directory - ``` + ```zsh cd testnet-onboarding-app ``` 3. Install dependencies - ``` + ```zsh yarn ``` 4. Setup .env - Copy and update `.env` - ``` + ```zsh cp .env.example .env ``` - In the `.env` file, add the WalletConnect project ID used in your [laconic-wallet](https://git.vdb.to/cerc-io/laconic-wallet) setup. - ``` + + ```zsh WALLET_CONNECT_PROJECT_ID=39bc93c... ``` 5. Start the application - ``` + ```zsh yarn start ``` diff --git a/config/webpack.config.js b/config/webpack.config.js index 9287627..245f8fe 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -1,56 +1,56 @@ -const fs = require('fs'); -const path = require('path'); -const webpack = require('webpack'); -const resolve = require('resolve'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); -const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); -const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); -const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); -const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); -const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); -const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); -const ESLintPlugin = require('eslint-webpack-plugin'); -const paths = require('./paths'); -const modules = require('./modules'); -const getClientEnvironment = require('./env'); -const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); +const fs = require("fs"); +const path = require("path"); +const webpack = require("webpack"); +const resolve = require("resolve"); +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin"); +const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin"); +const TerserPlugin = require("terser-webpack-plugin"); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); +const { WebpackManifestPlugin } = require("webpack-manifest-plugin"); +const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin"); +const WorkboxWebpackPlugin = require("workbox-webpack-plugin"); +const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin"); +const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent"); +const ESLintPlugin = require("eslint-webpack-plugin"); +const paths = require("./paths"); +const modules = require("./modules"); +const getClientEnvironment = require("./env"); +const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin"); const ForkTsCheckerWebpackPlugin = - process.env.TSC_COMPILE_ON_ERROR === 'true' - ? require('react-dev-utils/ForkTsCheckerWarningWebpackPlugin') - : require('react-dev-utils/ForkTsCheckerWebpackPlugin'); -const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); + process.env.TSC_COMPILE_ON_ERROR === "true" + ? require("react-dev-utils/ForkTsCheckerWarningWebpackPlugin") + : require("react-dev-utils/ForkTsCheckerWebpackPlugin"); +const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin"); -const createEnvironmentHash = require('./webpack/persistentCache/createEnvironmentHash'); +const createEnvironmentHash = require("./webpack/persistentCache/createEnvironmentHash"); // Source maps are resource heavy and can cause out of memory issue for large source files. -const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; +const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false"; -const reactRefreshRuntimeEntry = require.resolve('react-refresh/runtime'); +const reactRefreshRuntimeEntry = require.resolve("react-refresh/runtime"); const reactRefreshWebpackPluginRuntimeEntry = require.resolve( - '@pmmmwh/react-refresh-webpack-plugin' + "@pmmmwh/react-refresh-webpack-plugin" ); -const babelRuntimeEntry = require.resolve('babel-preset-react-app'); +const babelRuntimeEntry = require.resolve("babel-preset-react-app"); const babelRuntimeEntryHelpers = require.resolve( - '@babel/runtime/helpers/esm/assertThisInitialized', + "@babel/runtime/helpers/esm/assertThisInitialized", { paths: [babelRuntimeEntry] } ); -const babelRuntimeRegenerator = require.resolve('@babel/runtime/regenerator', { +const babelRuntimeRegenerator = require.resolve("@babel/runtime/regenerator", { paths: [babelRuntimeEntry], }); // Some apps do not need the benefits of saving a web request, so not inlining the chunk // makes for a smoother build process. -const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false'; +const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false"; -const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === 'true'; -const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true'; +const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === "true"; +const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === "true"; const imageInlineSizeLimit = parseInt( - process.env.IMAGE_INLINE_SIZE_LIMIT || '10000' + process.env.IMAGE_INLINE_SIZE_LIMIT || "10000" ); // Check if TypeScript is setup @@ -58,7 +58,7 @@ const useTypeScript = fs.existsSync(paths.appTsConfig); // Check if Tailwind config exists const useTailwind = fs.existsSync( - path.join(paths.appPath, 'tailwind.config.js') + path.join(paths.appPath, "tailwind.config.ts") ); // Get the path to the uncompiled service worker (if it exists). @@ -71,12 +71,12 @@ const sassRegex = /\.(scss|sass)$/; const sassModuleRegex = /\.module\.(scss|sass)$/; const hasJsxRuntime = (() => { - if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { + if (process.env.DISABLE_NEW_JSX_TRANSFORM === "true") { return false; } try { - require.resolve('react/jsx-runtime'); + require.resolve("react/jsx-runtime"); return true; } catch (e) { return false; @@ -86,13 +86,13 @@ const hasJsxRuntime = (() => { // This is the production and development configuration. // It is focused on developer experience, fast rebuilds, and a minimal bundle. module.exports = function (webpackEnv) { - const isEnvDevelopment = webpackEnv === 'development'; - const isEnvProduction = webpackEnv === 'production'; + const isEnvDevelopment = webpackEnv === "development"; + const isEnvProduction = webpackEnv === "production"; // Variable used for enabling profiling in Production // passed into alias object. Uses a flag if passed into the build command const isEnvProductionProfile = - isEnvProduction && process.argv.includes('--profile'); + isEnvProduction && process.argv.includes("--profile"); // We will provide `paths.publicUrlOrPath` to our app // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. @@ -105,38 +105,38 @@ module.exports = function (webpackEnv) { // common function to get style loaders const getStyleLoaders = (cssOptions, preProcessor) => { const loaders = [ - isEnvDevelopment && require.resolve('style-loader'), + isEnvDevelopment && require.resolve("style-loader"), isEnvProduction && { loader: MiniCssExtractPlugin.loader, // css is located in `static/css`, use '../../' to locate index.html folder // in production `paths.publicUrlOrPath` can be a relative path - options: paths.publicUrlOrPath.startsWith('.') - ? { publicPath: '../../' } + options: paths.publicUrlOrPath.startsWith(".") + ? { publicPath: "../../" } : {}, }, { - loader: require.resolve('css-loader'), + loader: require.resolve("css-loader"), options: cssOptions, }, { // Options for PostCSS as we reference these options twice // Adds vendor prefixing based on your specified browser support in // package.json - loader: require.resolve('postcss-loader'), + loader: require.resolve("postcss-loader"), options: { postcssOptions: { // Necessary for external CSS imports to work // https://github.com/facebook/create-react-app/issues/2677 - ident: 'postcss', + ident: "postcss", config: false, plugins: !useTailwind ? [ - 'postcss-flexbugs-fixes', + "postcss-flexbugs-fixes", [ - 'postcss-preset-env', + "postcss-preset-env", { autoprefixer: { - flexbox: 'no-2009', + flexbox: "no-2009", }, stage: 3, }, @@ -144,16 +144,16 @@ module.exports = function (webpackEnv) { // Adds PostCSS Normalize as the reset css with default options, // so that it honors browserslist config in package.json // which in turn let's users customize the target behavior as per their needs. - 'postcss-normalize', + "postcss-normalize", ] : [ - 'tailwindcss', - 'postcss-flexbugs-fixes', + "tailwindcss", + "postcss-flexbugs-fixes", [ - 'postcss-preset-env', + "postcss-preset-env", { autoprefixer: { - flexbox: 'no-2009', + flexbox: "no-2009", }, stage: 3, }, @@ -167,7 +167,7 @@ module.exports = function (webpackEnv) { if (preProcessor) { loaders.push( { - loader: require.resolve('resolve-url-loader'), + loader: require.resolve("resolve-url-loader"), options: { sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, root: paths.appSrc, @@ -185,17 +185,17 @@ module.exports = function (webpackEnv) { }; return { - target: ['browserslist'], + target: ["browserslist"], // Webpack noise constrained to errors and warnings - stats: 'errors-warnings', - mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', + stats: "errors-warnings", + mode: isEnvProduction ? "production" : isEnvDevelopment && "development", // Stop compilation early in production bail: isEnvProduction, devtool: isEnvProduction ? shouldUseSourceMap - ? 'source-map' + ? "source-map" : false - : isEnvDevelopment && 'cheap-module-source-map', + : isEnvDevelopment && "cheap-module-source-map", // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. entry: paths.appIndexJs, @@ -207,41 +207,42 @@ module.exports = function (webpackEnv) { // There will be one main bundle, and one file per asynchronous chunk. // In development, it does not produce real files. filename: isEnvProduction - ? 'static/js/[name].[contenthash:8].js' - : isEnvDevelopment && 'static/js/bundle.js', + ? "static/js/[name].[contenthash:8].js" + : isEnvDevelopment && "static/js/bundle.js", // There are also additional JS chunk files if you use code splitting. chunkFilename: isEnvProduction - ? 'static/js/[name].[contenthash:8].chunk.js' - : isEnvDevelopment && 'static/js/[name].chunk.js', - assetModuleFilename: 'static/media/[name].[hash][ext]', + ? "static/js/[name].[contenthash:8].chunk.js" + : isEnvDevelopment && "static/js/[name].chunk.js", + assetModuleFilename: "static/media/[name].[hash][ext]", // webpack uses `publicPath` to determine where the app is being served from. // It requires a trailing slash, or the file assets will get an incorrect path. // We inferred the "public path" (such as / or /my-project) from homepage. publicPath: paths.publicUrlOrPath, // Point sourcemap entries to original disk location (format as URL on Windows) devtoolModuleFilenameTemplate: isEnvProduction - ? info => + ? (info) => path .relative(paths.appSrc, info.absoluteResourcePath) - .replace(/\\/g, '/') + .replace(/\\/g, "/") : isEnvDevelopment && - (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')), + ((info) => + path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")), }, cache: { - type: 'filesystem', + type: "filesystem", version: createEnvironmentHash(env.raw), cacheDirectory: paths.appWebpackCache, - store: 'pack', + store: "pack", buildDependencies: { - defaultWebpack: ['webpack/lib/'], + defaultWebpack: ["webpack/lib/"], config: [__filename], - tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f => + tsconfig: [paths.appTsConfig, paths.appJsConfig].filter((f) => fs.existsSync(f) ), }, }, infrastructureLogging: { - level: 'none', + level: "none", }, optimization: { minimize: isEnvProduction, @@ -312,16 +313,16 @@ module.exports = function (webpackEnv) { // `web` extension prefixes have been added for better support // for React Native Web. extensions: paths.moduleFileExtensions - .map(ext => `.${ext}`) - .filter(ext => useTypeScript || !ext.includes('ts')), + .map((ext) => `.${ext}`) + .filter((ext) => useTypeScript || !ext.includes("ts")), alias: { // Support React Native Web // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ - 'react-native': 'react-native-web', + "react-native": "react-native-web", // Allows for better profiling with ReactDevTools ...(isEnvProductionProfile && { - 'react-dom$': 'react-dom/profiling', - 'scheduler/tracing': 'scheduler/tracing-profiling', + "react-dom$": "react-dom/profiling", + "scheduler/tracing": "scheduler/tracing-profiling", }), ...(modules.webpackAliases || {}), }, @@ -346,10 +347,10 @@ module.exports = function (webpackEnv) { rules: [ // Handle node_modules packages that contain sourcemaps shouldUseSourceMap && { - enforce: 'pre', + enforce: "pre", exclude: /@babel(?:\/|\\{1,2})runtime/, test: /\.(js|mjs|jsx|ts|tsx|css)$/, - loader: require.resolve('source-map-loader'), + loader: require.resolve("source-map-loader"), }, { // "oneOf" will traverse all following loaders until one will @@ -360,8 +361,8 @@ module.exports = function (webpackEnv) { // https://github.com/jshttp/mime-db { test: [/\.avif$/], - type: 'asset', - mimetype: 'image/avif', + type: "asset", + mimetype: "image/avif", parser: { dataUrlCondition: { maxSize: imageInlineSizeLimit, @@ -373,7 +374,7 @@ module.exports = function (webpackEnv) { // A missing `test` is equivalent to a match. { test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], - type: 'asset', + type: "asset", parser: { dataUrlCondition: { maxSize: imageInlineSizeLimit, @@ -384,7 +385,7 @@ module.exports = function (webpackEnv) { test: /\.svg$/, use: [ { - loader: require.resolve('@svgr/webpack'), + loader: require.resolve("@svgr/webpack"), options: { prettier: false, svgo: false, @@ -396,9 +397,9 @@ module.exports = function (webpackEnv) { }, }, { - loader: require.resolve('file-loader'), + loader: require.resolve("file-loader"), options: { - name: 'static/media/[name].[hash].[ext]', + name: "static/media/[name].[hash].[ext]", }, }, ], @@ -411,16 +412,16 @@ module.exports = function (webpackEnv) { { test: /\.(js|mjs|jsx|ts|tsx)$/, include: paths.appSrc, - loader: require.resolve('babel-loader'), + loader: require.resolve("babel-loader"), options: { customize: require.resolve( - 'babel-preset-react-app/webpack-overrides' + "babel-preset-react-app/webpack-overrides" ), presets: [ [ - require.resolve('babel-preset-react-app'), + require.resolve("babel-preset-react-app"), { - runtime: hasJsxRuntime ? 'automatic' : 'classic', + runtime: hasJsxRuntime ? "automatic" : "classic", }, ], ], @@ -428,7 +429,7 @@ module.exports = function (webpackEnv) { plugins: [ isEnvDevelopment && shouldUseReactRefresh && - require.resolve('react-refresh/babel'), + require.resolve("react-refresh/babel"), ].filter(Boolean), // This is a feature of `babel-loader` for webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ @@ -444,14 +445,14 @@ module.exports = function (webpackEnv) { { test: /\.(js|mjs)$/, exclude: /@babel(?:\/|\\{1,2})runtime/, - loader: require.resolve('babel-loader'), + loader: require.resolve("babel-loader"), options: { babelrc: false, configFile: false, compact: false, presets: [ [ - require.resolve('babel-preset-react-app/dependencies'), + require.resolve("babel-preset-react-app/dependencies"), { helpers: true }, ], ], @@ -482,7 +483,7 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { - mode: 'icss', + mode: "icss", }, }), // Don't consider CSS imports dead code even if the @@ -501,7 +502,7 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { - mode: 'local', + mode: "local", getLocalIdent: getCSSModuleLocalIdent, }, }), @@ -519,10 +520,10 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { - mode: 'icss', + mode: "icss", }, }, - 'sass-loader' + "sass-loader" ), // Don't consider CSS imports dead code even if the // containing package claims to have no side effects. @@ -541,11 +542,11 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { - mode: 'local', + mode: "local", getLocalIdent: getCSSModuleLocalIdent, }, }, - 'sass-loader' + "sass-loader" ), }, // "file" loader makes sure those assets get served by WebpackDevServer. @@ -559,7 +560,7 @@ module.exports = function (webpackEnv) { // Also exclude `html` and `json` extensions so they get processed // by webpacks internal loaders. exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/], - type: 'asset/resource', + type: "asset/resource", }, // ** STOP ** Are you adding a new loader? // Make sure to add the new loader(s) before the "file" loader. @@ -633,8 +634,8 @@ module.exports = function (webpackEnv) { new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional - filename: 'static/css/[name].[contenthash:8].css', - chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', + filename: "static/css/[name].[contenthash:8].css", + chunkFilename: "static/css/[name].[contenthash:8].chunk.css", }), // Generate an asset manifest file with the following content: // - "files" key: Mapping of all asset filenames to their corresponding @@ -643,7 +644,7 @@ module.exports = function (webpackEnv) { // - "entrypoints" key: Array of files which are included in `index.html`, // can be used to reconstruct the HTML if necessary new WebpackManifestPlugin({ - fileName: 'asset-manifest.json', + fileName: "asset-manifest.json", publicPath: paths.publicUrlOrPath, generate: (seed, files, entrypoints) => { const manifestFiles = files.reduce((manifest, file) => { @@ -651,7 +652,7 @@ module.exports = function (webpackEnv) { return manifest; }, seed); const entrypointFiles = entrypoints.main.filter( - fileName => !fileName.endsWith('.map') + (fileName) => !fileName.endsWith(".map") ); return { @@ -687,7 +688,7 @@ module.exports = function (webpackEnv) { new ForkTsCheckerWebpackPlugin({ async: isEnvDevelopment, typescript: { - typescriptPath: resolve.sync('typescript', { + typescriptPath: resolve.sync("typescript", { basedir: paths.appNodeModules, }), configOverwrite: { @@ -707,7 +708,7 @@ module.exports = function (webpackEnv) { diagnosticOptions: { syntactic: true, }, - mode: 'write-references', + mode: "write-references", // profile: true, }, issue: { @@ -716,41 +717,41 @@ module.exports = function (webpackEnv) { // '../cra-template-typescript/template/src/App.tsx' // otherwise. include: [ - { file: '../**/src/**/*.{ts,tsx}' }, - { file: '**/src/**/*.{ts,tsx}' }, + { file: "../**/src/**/*.{ts,tsx}" }, + { file: "**/src/**/*.{ts,tsx}" }, ], exclude: [ - { file: '**/src/**/__tests__/**' }, - { file: '**/src/**/?(*.){spec|test}.*' }, - { file: '**/src/setupProxy.*' }, - { file: '**/src/setupTests.*' }, + { file: "**/src/**/__tests__/**" }, + { file: "**/src/**/?(*.){spec|test}.*" }, + { file: "**/src/setupProxy.*" }, + { file: "**/src/setupTests.*" }, ], }, logger: { - infrastructure: 'silent', + infrastructure: "silent", }, }), !disableESLintPlugin && new ESLintPlugin({ // Plugin options - extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'], - formatter: require.resolve('react-dev-utils/eslintFormatter'), - eslintPath: require.resolve('eslint'), + extensions: ["js", "mjs", "jsx", "ts", "tsx"], + formatter: require.resolve("react-dev-utils/eslintFormatter"), + eslintPath: require.resolve("eslint"), failOnError: !(isEnvDevelopment && emitErrorsAsWarnings), context: paths.appSrc, cache: true, cacheLocation: path.resolve( paths.appNodeModules, - '.cache/.eslintcache' + ".cache/.eslintcache" ), // ESLint class options cwd: paths.appPath, resolvePluginsRelativeTo: __dirname, baseConfig: { - extends: [require.resolve('eslint-config-react-app/base')], + extends: [require.resolve("eslint-config-react-app/base")], rules: { ...(!hasJsxRuntime && { - 'react/react-in-jsx-scope': 'error', + "react/react-in-jsx-scope": "error", }), }, }, diff --git a/package.json b/package.json index 7a76c72..45843a6 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "resolve": "^1.20.0", "semver": "^7.3.5", "stream-browserify": "^3.0.0", - "tailwindcss": "^3.0.2", + "tailwindcss": "^3.4.9", "typescript": "^4.9.5" }, "scripts": { @@ -66,8 +66,10 @@ "@types/node": "^16.18.90", "@types/react": "^18.2.67", "@types/react-dom": "^18.2.22", + "@types/tailwindcss": "^3.1.0", "@typescript-eslint/eslint-plugin": "^6.13.2", "@typescript-eslint/parser": "^6.13.2", + "autoprefixer": "^10.4.20", "babel-jest": "^27.4.2", "babel-loader": "^8.2.3", "babel-plugin-named-asset-import": "^0.3.8", @@ -92,7 +94,7 @@ "jest-resolve": "^27.4.2", "jest-watch-typeahead": "^1.0.0", "mini-css-extract-plugin": "^2.4.5", - "postcss": "^8.4.4", + "postcss": "^8.4.41", "postcss-flexbugs-fixes": "^5.0.2", "postcss-loader": "^6.2.1", "postcss-normalize": "^10.0.1", diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/public/laconic_logo.svg b/public/laconic_logo.svg new file mode 100644 index 0000000..ab8603a --- /dev/null +++ b/public/laconic_logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/laconic_logo_with_text.svg b/public/laconic_logo_with_text.svg new file mode 100644 index 0000000..fdfcf05 --- /dev/null +++ b/public/laconic_logo_with_text.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/App.tsx b/src/App.tsx index 4737a39..d794326 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,6 +15,8 @@ import VerifyEmail from "./pages/VerifyEmail"; import Email from "./pages/Email"; import Thanks from "./pages/Thanks"; +import "./App.css"; + function App() { return ( @@ -23,19 +25,13 @@ function App() { } /> } /> - } /> + } /> } /> } /> }> } /> - } - /> - } - /> + } /> + } /> } diff --git a/src/components/CustomIcon/CustomIcon.tsx b/src/components/CustomIcon/CustomIcon.tsx new file mode 100644 index 0000000..f887596 --- /dev/null +++ b/src/components/CustomIcon/CustomIcon.tsx @@ -0,0 +1,30 @@ +import React, { ComponentPropsWithoutRef } from "react"; + +export interface CustomIconProps extends ComponentPropsWithoutRef<"svg"> { + size?: number | string; // width and height will both be set as the same value + name?: string; +} + +export const CustomIcon: React.FC = ({ + children, + width = 24, + height = 24, + size, + viewBox = "0 0 24 24", + name, + ...rest +}: CustomIconProps) => { + return ( + + {children} + + ); +}; diff --git a/src/components/CustomIcon/README.md b/src/components/CustomIcon/README.md new file mode 100644 index 0000000..7376c3a --- /dev/null +++ b/src/components/CustomIcon/README.md @@ -0,0 +1,78 @@ +# CustomIcon + +`CustomIcon` is a flexible and reusable React component for rendering SVG icons. It allows for easy customization of size, color, and other SVG properties. + +- Viewbox "0 0 24 24": From where you're exporting from, please make sure the icon is using viewBox="0 0 24 24" before downloading/exporting. Not doing so will result in incorrect icon scaling + +## Create a Custom Icon + +1. Duplicate a current icon e.g. `LaconicIcon` and rename it accordingly. +2. Rename the function inside the new file you duplicated too +3. Replace the markup with your SVG markup (make sure it complies with the above section's rule) +4. Depending on the svg you pasted... + A. If the `` has only 1 child, remove the `` parent entirely so you only have the path left + B. If your component has more than 1 paths, rename `` tag with the `` tag. Then, remove all attributes of this `` tag so that it's just `` +5. Usually, icons are single colored. If that's the case, replace all fill/stroke color with `currentColor`. E.g. ``. Leave the other attributes without removing them. +6. If your icon has more than one color, then it's up to you to decide whether we want to use tailwind to help set the fill and stroke colors +7. Lastly, export your icon in `index.ts` by following what was done for `LaconicIcon` +8. Make sure to provide a `name` for the `` component for accessibility +9. Done! + +Example: + +```tsx +import { CustomIcon, CustomIconProps } from "../CustomIcon"; + +export const LaconicIcon = (props: CustomIconProps) => { + return ( + + + + ); +}; +``` + +## Usage + +```tsx +import { LaconicIcon } from './components/CustomIcon'; + +... + + +``` + +## Props + +The `CustomIcon` component accepts the following props: + +- `children`: SVG content (paths, groups, etc.) +- `size`: Sets both width and height (number or string) +- `width`: SVG width (default: 24) +- `height`: SVG height (default: 24) +- `viewBox`: SVG viewBox (default: "0 0 24 24") +- `name`: Icon name for accessibility (sets aria-labelledby) +- ...other SVG props + +## Accessibility + +Always provide a `name` prop to the CustomIcon for improved accessibility. This sets the `aria-labelledby` attribute on the SVG. + +```tsx + + {/* SVG content */} + +``` + +For icons that are purely decorative, the component sets `role="presentation"` by default. diff --git a/src/components/CustomIcon/icons/LaconicIcon.tsx b/src/components/CustomIcon/icons/LaconicIcon.tsx new file mode 100644 index 0000000..f712fb5 --- /dev/null +++ b/src/components/CustomIcon/icons/LaconicIcon.tsx @@ -0,0 +1,23 @@ +import React from "react"; + +import { CustomIcon, CustomIconProps } from "../CustomIcon"; + +export const LaconicIcon: React.FC = (props) => { + return ( + + + + ); +}; diff --git a/src/components/CustomIcon/icons/LaconicWithTextIcon.tsx b/src/components/CustomIcon/icons/LaconicWithTextIcon.tsx new file mode 100644 index 0000000..fa123dc --- /dev/null +++ b/src/components/CustomIcon/icons/LaconicWithTextIcon.tsx @@ -0,0 +1,50 @@ +import React from "react"; + +import { CustomIcon, CustomIconProps } from "../CustomIcon"; + +export const LaconicWithTextIcon: React.FC = (props) => { + return ( + + + + + + + + + + + ); +}; diff --git a/src/components/CustomIcon/index.ts b/src/components/CustomIcon/index.ts new file mode 100644 index 0000000..7bbe733 --- /dev/null +++ b/src/components/CustomIcon/index.ts @@ -0,0 +1,2 @@ +export { LaconicIcon } from "./icons/LaconicIcon"; +export { LaconicWithTextIcon } from "./icons/LaconicWithTextIcon"; diff --git a/src/index.css b/src/index.css index ec2585e..ef80a38 100644 --- a/src/index.css +++ b/src/index.css @@ -1,13 +1,17 @@ +@import "tailwindcss/base"; +@import "tailwindcss/components"; +@import "tailwindcss/utilities"; + body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", + "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..491abe5 --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,15 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: { + colors: { + snowball: "#0f86f5", + }, + }, + }, + plugins: [], +}; + +export default config; diff --git a/yarn.lock b/yarn.lock index 7733dc9..9a37400 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3629,6 +3629,13 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== +"@types/tailwindcss@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/tailwindcss/-/tailwindcss-3.1.0.tgz#1185e4b3437c6e0f19d6cc8cd42738a94fd7b64f" + integrity sha512-JxPzrm609hzvF4nmOI3StLjbBEP3WWQxDDJESqR1nh94h7gyyy3XSl0hn5RBMJ9mPudlLjtaXs5YEBtLw7CnPA== + dependencies: + tailwindcss "*" + "@types/testing-library__jest-dom@^5.9.1": version "5.14.9" resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz#0fb1e6a0278d87b6737db55af5967570b67cb466" @@ -4607,6 +4614,18 @@ autoprefixer@^10.4.13: picocolors "^1.0.0" postcss-value-parser "^4.2.0" +autoprefixer@^10.4.20: + version "10.4.20" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" + integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g== + dependencies: + browserslist "^4.23.3" + caniuse-lite "^1.0.30001646" + fraction.js "^4.3.7" + normalize-range "^0.1.2" + picocolors "^1.0.1" + postcss-value-parser "^4.2.0" + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -5017,6 +5036,16 @@ browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.10, browserslist@^ node-releases "^2.0.14" update-browserslist-db "^1.0.13" +browserslist@^4.23.3: + version "4.23.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" + integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== + dependencies: + caniuse-lite "^1.0.30001646" + electron-to-chromium "^1.5.4" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" + bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -5127,6 +5156,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001587, caniuse-lite@^1.0.30001591: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz#571cf4f3f1506df9bf41fcbb6d10d5d017817bce" integrity sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA== +caniuse-lite@^1.0.30001646: + version "1.0.30001651" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz#52de59529e8b02b1aedcaaf5c05d9e23c0c28138" + integrity sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg== + canonical-json@^0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/canonical-json/-/canonical-json-0.0.4.tgz#6579c072c3db5c477ec41dc978fbf2b8f41074a3" @@ -6225,6 +6259,11 @@ electron-to-chromium@^1.4.668: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.710.tgz#d0ec4ea8a97df4c5eaeb8c69d45bf81f248b3855" integrity sha512-w+9yAVHoHhysCa+gln7AzbO9CdjFcL/wN/5dd+XW/Msl2d/4+WisEaCF1nty0xbAKaxdaJfgLB2296U7zZB7BA== +electron-to-chromium@^1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.5.tgz#03bfdf422bdd2c05ee2657efedde21264a1a566b" + integrity sha512-QR7/A7ZkMS8tZuoftC/jfqNkZLQO779SSW3YuZHP4eXpj3EffGLFcB/Xu9AAZQzLccTiCV+EmUo3ha4mQ9wnlA== + elliptic@6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -6508,7 +6547,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -escalade@^3.1.1: +escalade@^3.1.1, escalade@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== @@ -8869,7 +8908,7 @@ jest@^27.4.3: import-local "^3.0.2" jest-cli "^27.5.1" -jiti@^1.19.1, jiti@^1.21.0: +jiti@^1.21.0: version "1.21.0" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== @@ -9710,6 +9749,11 @@ node-releases@^2.0.14: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + node-yaml@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/node-yaml/-/node-yaml-4.0.1.tgz#3675d27c275fbea9c02e2b0faa18bb1699444cb3" @@ -10171,6 +10215,11 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -10792,7 +10841,7 @@ postcss@^7.0.35: picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.3.5, postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.4: +postcss@^8.3.5, postcss@^8.4.23, postcss@^8.4.33: version "8.4.36" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.36.tgz#dba513c3c3733c44e0288a712894f8910bbaabc6" integrity sha512-/n7eumA6ZjFHAsbX30yhHup/IMkOmlmvtEi7P+6RMYf+bGJSUHc3geH4a0NSZxAz/RJfiS9tooCTs9LAVYUZKw== @@ -10801,6 +10850,15 @@ postcss@^8.3.5, postcss@^8.4.23, postcss@^8.4.33, postcss@^8.4.4: picocolors "^1.0.0" source-map-js "^1.1.0" +postcss@^8.4.41: + version "8.4.41" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.41.tgz#d6104d3ba272d882fe18fc07d15dc2da62fa2681" + integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.1" + source-map-js "^1.2.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -11823,6 +11881,11 @@ source-map-js@^1.0.1, source-map-js@^1.1.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.1.0.tgz#9e7d5cb46f0689fb6691b30f226937558d0fa94b" integrity sha512-9vC2SfsJzlej6MAaMPLu8HiBSHGdRAJ9hVFYN1ibZoNkeanmDmLUcIrj6G9DGL7XMJ54AKg/G75akXl1/izTOw== +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + source-map-loader@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-3.0.2.tgz#af23192f9b344daa729f6772933194cc5fa54fee" @@ -12249,10 +12312,10 @@ system-architecture@^0.1.0: resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d" integrity sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA== -tailwindcss@^3.0.2: - version "3.4.1" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.1.tgz#f512ca5d1dd4c9503c7d3d28a968f1ad8f5c839d" - integrity sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA== +tailwindcss@*, tailwindcss@^3.4.9: + version "3.4.9" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.9.tgz#9e04cddce1924d530df62af37d3520f0e2a9d85e" + integrity sha512-1SEOvRr6sSdV5IDf9iC+NU4dhwdqzF4zKKq3sAbasUWHEM6lsMhX+eNN5gkPx1BvLFEnZQEUFbXnGj8Qlp83Pg== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" @@ -12262,7 +12325,7 @@ tailwindcss@^3.0.2: fast-glob "^3.3.0" glob-parent "^6.0.2" is-glob "^4.0.3" - jiti "^1.19.1" + jiti "^1.21.0" lilconfig "^2.1.0" micromatch "^4.0.5" normalize-path "^3.0.0" @@ -12751,6 +12814,14 @@ update-browserslist-db@^1.0.13: escalade "^3.1.1" picocolors "^1.0.0" +update-browserslist-db@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + uqr@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/uqr/-/uqr-0.1.2.tgz#5c6cd5dcff9581f9bb35b982cb89e2c483a41d7d"