Initial commit
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"presets": [
|
||||
[
|
||||
"next/babel",
|
||||
{
|
||||
"styled-jsx": {
|
||||
"plugins": ["styled-jsx-plugin-postcss"]
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
out
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
// Configuration for JavaScript files
|
||||
"extends": [
|
||||
"airbnb-base",
|
||||
"next/core-web-vitals",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
// Configuration for TypeScript files
|
||||
{
|
||||
"files": ["**/*.ts", "**/*.tsx"],
|
||||
"plugins": ["@typescript-eslint", "unused-imports"],
|
||||
"extends": [
|
||||
"airbnb-typescript",
|
||||
"next/core-web-vitals",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"rules": {
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
],
|
||||
"react/destructuring-assignment": "off", // Vscode doesn't support automatically destructuring, it's a pain to add a new variable
|
||||
"jsx-a11y/anchor-is-valid": "off", // Next.js use his own internal link system
|
||||
"react/require-default-props": "off", // Allow non-defined react props as undefined
|
||||
"react/jsx-props-no-spreading": "off", // _app.tsx uses spread operator and also, react-hook-form
|
||||
"@next/next/no-img-element": "off", // We currently not using next/image because it isn't supported with SSG mode
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"groups": ["builtin", "external", "internal"],
|
||||
"pathGroups": [
|
||||
{
|
||||
"pattern": "react",
|
||||
"group": "external",
|
||||
"position": "before"
|
||||
}
|
||||
],
|
||||
"pathGroupsExcludedImportTypes": ["react"],
|
||||
"newlines-between": "always",
|
||||
"alphabetize": {
|
||||
"order": "asc",
|
||||
"caseInsensitive": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/comma-dangle": "off", // Avoid conflict rule between Eslint and Prettier
|
||||
"import/prefer-default-export": "off", // Named export is easier to refactor automatically
|
||||
"class-methods-use-this": "off", // _document.tsx use render method without `this` keyword
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
{ "argsIgnorePattern": "^_" }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
custom: ['https://creativedesignsguru.com/category/nextjs/', 'https://www.buymeacoffee.com/ixartz']
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next
|
||||
/out
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
Thumbs.db
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# dotenv local files
|
||||
.env*.local
|
||||
|
||||
# local folder
|
||||
local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# Disable concurent to run build-types after ESLint in lint-staged
|
||||
npx lint-staged --concurrent false
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"mikestead.dotenv",
|
||||
"csstools.postcss",
|
||||
"blanu.vscode-styled-jsx",
|
||||
"msjsdiag.debugger-for-chrome",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Next: Chrome",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Next: Node",
|
||||
"program": "${workspaceFolder}/node_modules/.bin/next",
|
||||
"args": ["dev"],
|
||||
"autoAttachChildProcesses": true,
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Next: Full",
|
||||
"configurations": ["Next: Node", "Next: Chrome"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"editor.tabSize": 2,
|
||||
"editor.detectIndentation": false,
|
||||
"jest.autoRun": {
|
||||
"watch": false,
|
||||
"onSave": "test-file"
|
||||
},
|
||||
"search.exclude": {
|
||||
"package-lock.json": true
|
||||
},
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
"editor.formatOnSave": false,
|
||||
"editor.codeActionsOnSave": [
|
||||
"source.addMissingImports",
|
||||
"source.fixAll.eslint"
|
||||
],
|
||||
// Multiple language settings for json and jsonc files
|
||||
"[json][jsonc]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Project wide type checking with TypeScript",
|
||||
"type": "npm",
|
||||
"script": "build-types",
|
||||
"problemMatcher": ["$tsc"],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"clear": true,
|
||||
"reveal": "never"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Daniele Salatti
|
||||
|
||||
Template:
|
||||
Copyright (c) 2020 Rem W.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'eslint'],
|
||||
'**/*.ts?(x)': () => 'npm run build-types',
|
||||
'*.json': ['prettier --write'],
|
||||
};
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
[build]
|
||||
publish = "out"
|
||||
command = "npm run build-prod"
|
||||
|
||||
[build.environment]
|
||||
NETLIFY_NEXT_PLUGIN_SKIP = "true"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
const withBundleAnalyzer = require('@next/bundle-analyzer')({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
});
|
||||
|
||||
module.exports = withBundleAnalyzer({
|
||||
poweredByHeader: false,
|
||||
trailingSlash: true,
|
||||
basePath: '',
|
||||
// The starter code load resources from `public` folder with `router.basePath` in React components.
|
||||
// So, the source code is "basePath-ready".
|
||||
// You can remove `basePath` if you don't need it.
|
||||
reactStrictMode: true,
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"name": "convictionvotingwtf",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"email": "me@danielesalatti.com",
|
||||
"name": "Daniele Salatti",
|
||||
"url": "https://danielesalatti.com"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"build-stats": "cross-env ANALYZE=true npm run build",
|
||||
"export": "next export",
|
||||
"build-prod": "run-s clean build export",
|
||||
"clean": "rimraf .next out",
|
||||
"lint": "next lint",
|
||||
"build-types": "tsc --noEmit --pretty",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.3.1",
|
||||
"next": "^12.0.9",
|
||||
"next-seo": "^4.29.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"styled-jsx-plugin-postcss": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^12.0.9",
|
||||
"@types/node": "^17.0.13",
|
||||
"@types/react": "^17.0.38",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.1",
|
||||
"@typescript-eslint/parser": "^5.10.1",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"cssnano": "^5.0.16",
|
||||
"eslint": "^8.7.0",
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-next": "^12.0.9",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^12.3.2",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.5",
|
||||
"prettier": "^2.5.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"tailwindcss": "^3.0.17",
|
||||
"typescript": "^4.5.5"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Please do not use the array form (like ['tailwindcss', 'postcss-preset-env'])
|
||||
// it will create an unexpected error: Invalid PostCSS Plugin found: [0]
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
...(process.env.NODE_ENV === 'production' ? { cssnano: {} } : {}),
|
||||
},
|
||||
};
|
||||
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 409 KiB |
|
After Width: | Height: | Size: 445 B |
|
After Width: | Height: | Size: 1001 B |
|
After Width: | Height: | Size: 15 KiB |
|
|
@ -0,0 +1,12 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
type IBackgroundProps = {
|
||||
children: ReactNode;
|
||||
color: string;
|
||||
};
|
||||
|
||||
const Background = (props: IBackgroundProps) => (
|
||||
<div className={props.color}>{props.children}</div>
|
||||
);
|
||||
|
||||
export { Background };
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
import className from 'classnames';
|
||||
|
||||
type IButtonProps = {
|
||||
xl?: boolean;
|
||||
children: string;
|
||||
};
|
||||
|
||||
const Button = (props: IButtonProps) => {
|
||||
const btnClass = className({
|
||||
btn: true,
|
||||
'btn-xl': props.xl,
|
||||
'btn-base': !props.xl,
|
||||
'btn-primary': true,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={btnClass}>
|
||||
{props.children}
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.btn {
|
||||
@apply inline-block rounded-md text-center;
|
||||
}
|
||||
|
||||
.btn-base {
|
||||
@apply text-lg font-semibold py-2 px-4;
|
||||
}
|
||||
|
||||
.btn-xl {
|
||||
@apply font-extrabold text-xl py-4 px-6;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply text-white bg-primary-500;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
@apply bg-primary-600;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { Button };
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
type ICTABannerProps = {
|
||||
title: ReactNode;
|
||||
subtitle: string;
|
||||
button: ReactNode;
|
||||
text: ReactNode;
|
||||
};
|
||||
|
||||
const CTABanner = (props: ICTABannerProps) => (
|
||||
<div className="text-center flex flex-col p-4 sm:text-left sm:flex-row sm:items-center sm:justify-between sm:p-12 bg-primary-100 rounded-md">
|
||||
<div className="text-2xl font-semibold">
|
||||
<div className="text-gray-900">{props.title}</div>
|
||||
<div className="text-primary-500">{props.subtitle}</div>
|
||||
<div className="text-gray-900 text-lg font-normal">{props.text}</div>
|
||||
</div>
|
||||
|
||||
<div className="whitespace-no-wrap mt-3 sm:mt-0 sm:ml-2 w-32">
|
||||
{props.button}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { CTABanner };
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import className from 'classnames';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
type IVerticalFeatureRowProps = {
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
imageAlt: string;
|
||||
reverse?: boolean;
|
||||
};
|
||||
|
||||
const VerticalFeatureRow = (props: IVerticalFeatureRowProps) => {
|
||||
const verticalFeatureClass = className(
|
||||
'mt-20',
|
||||
'flex',
|
||||
'flex-wrap',
|
||||
'items-center',
|
||||
{
|
||||
'flex-row-reverse': props.reverse,
|
||||
}
|
||||
);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className={verticalFeatureClass}>
|
||||
<div className="w-full sm:w-1/2 text-center sm:px-6">
|
||||
<h3 className="text-3xl text-gray-900 font-semibold">{props.title}</h3>
|
||||
<div className="mt-6 text-xl leading-9">{props.description}</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full sm:w-1/2 p-6">
|
||||
<img src={`${router.basePath}${props.image}`} alt={props.imageAlt} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { VerticalFeatureRow };
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
import { FooterCopyright } from './FooterCopyright';
|
||||
import { FooterIconList } from './FooterIconList';
|
||||
|
||||
type ICenteredFooterProps = {
|
||||
logo: ReactNode;
|
||||
iconList: ReactNode;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const CenteredFooter = (props: ICenteredFooterProps) => (
|
||||
<div className="text-center">
|
||||
{props.logo}
|
||||
|
||||
<nav>
|
||||
<ul className="navbar mt-5 flex flex-row justify-center font-medium text-xl text-gray-800">
|
||||
{props.children}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div className="mt-8 flex justify-center">
|
||||
<FooterIconList>{props.iconList}</FooterIconList>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 text-sm">
|
||||
<FooterCopyright />
|
||||
</div>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.navbar :global(li) {
|
||||
@apply mx-4;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { CenteredFooter };
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import { AppConfig } from '../utils/AppConfig';
|
||||
|
||||
const FooterCopyright = () => (
|
||||
<div className="footer-copyright">
|
||||
© Copyright {new Date().getFullYear()} <a href="https://danielesalatti.com">Daniele Salatti</a> - All rights reserved.
|
||||
<style jsx>
|
||||
{`
|
||||
.footer-copyright :global(a) {
|
||||
@apply text-primary-500;
|
||||
}
|
||||
|
||||
.footer-copyright :global(a:hover) {
|
||||
@apply underline;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { FooterCopyright };
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
type IFooterIconListProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const FooterIconList = (props: IFooterIconListProps) => (
|
||||
<div className="footer-icon-list flex flex-wrap">
|
||||
{props.children}
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.footer-icon-list :global(a:not(:last-child)) {
|
||||
@apply mr-3;
|
||||
}
|
||||
|
||||
.footer-icon-list :global(a) {
|
||||
@apply text-gray-500;
|
||||
}
|
||||
|
||||
.footer-icon-list :global(a:hover) {
|
||||
@apply text-gray-700;
|
||||
}
|
||||
|
||||
.footer-icon-list :global(svg) {
|
||||
@apply fill-current w-5 h-5;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { FooterIconList };
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
type IHeroOneButtonProps = {
|
||||
title: ReactNode;
|
||||
description: ReactNode;
|
||||
button: ReactNode;
|
||||
};
|
||||
|
||||
const HeroOneButton = (props: IHeroOneButtonProps) => (
|
||||
<header className="text-center">
|
||||
<h1 className="text-5xl text-gray-900 font-bold whitespace-pre-line leading-hero">
|
||||
{props.title}
|
||||
</h1>
|
||||
<div className="text-2xl mt-4 mb-16">{props.description}</div>
|
||||
|
||||
{props.button}
|
||||
</header>
|
||||
);
|
||||
|
||||
export { HeroOneButton };
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import { NextSeo } from 'next-seo';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import { AppConfig } from '../utils/AppConfig';
|
||||
|
||||
type IMetaProps = {
|
||||
title: string;
|
||||
description: string;
|
||||
canonical?: string;
|
||||
};
|
||||
|
||||
const Meta = (props: IMetaProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta charSet="UTF-8" key="charset" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,initial-scale=1"
|
||||
key="viewport"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
href={`${router.basePath}/apple-touch-icon.png`}
|
||||
key="apple"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href={`${router.basePath}/favicon-32x32.png`}
|
||||
key="icon32"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href={`${router.basePath}/favicon-16x16.png`}
|
||||
key="icon16"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
href={`${router.basePath}/favicon.ico`}
|
||||
key="favicon"
|
||||
/>
|
||||
</Head>
|
||||
<NextSeo
|
||||
title={props.title}
|
||||
description={props.description}
|
||||
canonical={props.canonical}
|
||||
openGraph={{
|
||||
title: props.title,
|
||||
description: props.description,
|
||||
url: props.canonical,
|
||||
locale: AppConfig.locale,
|
||||
site_name: AppConfig.site_name,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { Meta };
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
type ISectionProps = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
yPadding?: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const Section = (props: ISectionProps) => (
|
||||
<div
|
||||
className={`max-w-screen-lg mx-auto px-3 ${
|
||||
props.yPadding ? props.yPadding : 'py-16'
|
||||
}`}
|
||||
>
|
||||
{(props.title || props.description) && (
|
||||
<div className="mb-12 text-center">
|
||||
{props.title && (
|
||||
<h2 className="text-4xl text-gray-900 font-bold">{props.title}</h2>
|
||||
)}
|
||||
{props.description && (
|
||||
<div className="mt-4 text-xl md:px-20">{props.description}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export { Section };
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import { ReactNode } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
type INavbarProps = {
|
||||
logo: ReactNode;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const NavbarTwoColumns = (props: INavbarProps) => (
|
||||
<div className="flex flex-wrap justify-between items-center">
|
||||
<div>
|
||||
<Link href="/">
|
||||
<a>{props.logo}</a>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<nav>
|
||||
<ul className="navbar flex items-center font-medium text-xl text-gray-800">
|
||||
{props.children}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<style jsx>
|
||||
{`
|
||||
.navbar :global(li:not(:first-child)) {
|
||||
@apply mt-0;
|
||||
}
|
||||
|
||||
.navbar :global(li:not(:last-child)) {
|
||||
@apply mr-5;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { NavbarTwoColumns };
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
import { AppProps } from 'next/app';
|
||||
|
||||
import '../styles/global.css';
|
||||
|
||||
const MyApp = ({ Component, pageProps }: AppProps) => (
|
||||
<Component {...pageProps} />
|
||||
);
|
||||
|
||||
export default MyApp;
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||
|
||||
import { AppConfig } from '../utils/AppConfig';
|
||||
|
||||
// Need to create a custom _document because i18n support is not compatible with `next export`.
|
||||
class MyDocument extends Document {
|
||||
render() {
|
||||
return (
|
||||
<Html lang={AppConfig.locale}>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MyDocument;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import { Base } from '../templates/Base';
|
||||
|
||||
const Index = () => <Base />;
|
||||
|
||||
export default Index;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
@tailwind base;
|
||||
|
||||
@tailwind components;
|
||||
|
||||
@tailwind utilities;
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import Link from 'next/link';
|
||||
|
||||
import { Button } from '../button/Button';
|
||||
import { CTABanner } from '../cta/CTABanner';
|
||||
import { Section } from '../layout/Section';
|
||||
|
||||
const Banner = () => (
|
||||
<Section>
|
||||
<CTABanner
|
||||
title={<>{"🖖 Hi, I'm "}
|
||||
<Link href='/'>
|
||||
<a href='https://danielesalatti.com' target='_blank'>
|
||||
Daniele Salatti
|
||||
</a>
|
||||
</Link>
|
||||
</>}
|
||||
subtitle="And I am part of the 🏰 BuidlGuidl"
|
||||
text={<>
|
||||
<p>{"A curated group of Ethereum builders creating products, prototypes, and tutorials to enrich the web3 ecosytem."}</p>
|
||||
<p className="mt-4">{"❤️ We are an Ethereum public good."}</p>
|
||||
</>}
|
||||
button={
|
||||
<Link href="/">
|
||||
<a href="https://buidlguidl.com" target="_blank">
|
||||
<Button>Join us</Button>
|
||||
</a>
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
|
||||
export { Banner };
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import { Meta } from '../layout/Meta';
|
||||
import { AppConfig } from '../utils/AppConfig';
|
||||
import { Banner } from './Banner';
|
||||
import { Footer } from './Footer';
|
||||
import { Hero } from './Hero';
|
||||
import { VerticalFeatures } from './VerticalFeatures';
|
||||
|
||||
const Base = () => (
|
||||
<div className="antialiased text-gray-600">
|
||||
<Meta title={AppConfig.title} description={AppConfig.description} />
|
||||
<Hero />
|
||||
<VerticalFeatures />
|
||||
<Banner />
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
|
||||
export { Base };
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import Link from 'next/link';
|
||||
|
||||
import { Background } from '../background/Background';
|
||||
import { CenteredFooter } from '../footer/CenteredFooter';
|
||||
import { Section } from '../layout/Section';
|
||||
import { Logo } from './Logo';
|
||||
|
||||
const Footer = () => (
|
||||
<Background color="bg-gray-100">
|
||||
<Section>
|
||||
<CenteredFooter
|
||||
logo={<Logo />}
|
||||
iconList={
|
||||
<>
|
||||
<Link href="/">
|
||||
<a href='https://github.com/DanieleSalatti' target='_blank'>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
||||
</svg>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Link href="/">
|
||||
<a href='https://twitter.com/DanieleSalatti' target='_blank'>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23.954 4.569a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.691 8.094 4.066 6.13 1.64 3.161a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.061a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.937 4.937 0 004.604 3.417 9.868 9.868 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63a9.936 9.936 0 002.46-2.548l-.047-.02z" />
|
||||
</svg>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Link href="/">
|
||||
<a href='https://www.youtube.com/channel/UCK0Q8q7wFeM_DJU8cz6sgjA' target='_blank'>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M23.495 6.205a3.007 3.007 0 00-2.088-2.088c-1.87-.501-9.396-.501-9.396-.501s-7.507-.01-9.396.501A3.007 3.007 0 00.527 6.205a31.247 31.247 0 00-.522 5.805 31.247 31.247 0 00.522 5.783 3.007 3.007 0 002.088 2.088c1.868.502 9.396.502 9.396.502s7.506 0 9.396-.502a3.007 3.007 0 002.088-2.088 31.247 31.247 0 00.5-5.783 31.247 31.247 0 00-.5-5.805zM9.609 15.601V8.408l6.264 3.602z" />
|
||||
</svg>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Link href="/">
|
||||
<a href='https://linkedin.com/in/DanieleSalatti' target='_blank'>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
||||
</svg>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Link href="/">
|
||||
<a href='mailto:me@danielesalatti.com'>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.585 5.267c1.834 0 3.558.811 4.824 2.08v.004c0-.609.41-1.068.979-1.068h.145c.891 0 1.073.842 1.073 1.109l.005 9.475c-.063.621.64.941 1.029.543 1.521-1.564 3.342-8.038-.946-11.79-3.996-3.497-9.357-2.921-12.209-.955-3.031 2.091-4.971 6.718-3.086 11.064 2.054 4.74 7.931 6.152 11.424 4.744 1.769-.715 2.586 1.676.749 2.457-2.776 1.184-10.502 1.064-14.11-5.188C-.977 13.521-.847 6.093 5.62 2.245 10.567-.698 17.09.117 21.022 4.224c4.111 4.294 3.872 12.334-.139 15.461-1.816 1.42-4.516.037-4.498-2.031l-.019-.678c-1.265 1.256-2.948 1.988-4.782 1.988-3.625 0-6.813-3.189-6.813-6.812 0-3.659 3.189-6.885 6.814-6.885zm4.561 6.623c-.137-2.653-2.106-4.249-4.484-4.249h-.09c-2.745 0-4.268 2.159-4.268 4.61 0 2.747 1.842 4.481 4.256 4.481 2.693 0 4.464-1.973 4.592-4.306l-.006-.536z" />
|
||||
</svg>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Link href="/">
|
||||
<a href='https://danielesalatti.com/feed.xml' target='_blank'>
|
||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.199 24C19.199 13.467 10.533 4.8 0 4.8V0c13.165 0 24 10.835 24 24h-4.801zM3.291 17.415a3.3 3.3 0 013.293 3.295A3.303 3.303 0 013.283 24C1.47 24 0 22.526 0 20.71s1.475-3.294 3.291-3.295zM15.909 24h-4.665c0-6.169-5.075-11.245-11.244-11.245V8.09c8.727 0 15.909 7.184 15.909 15.91z" />
|
||||
</svg>
|
||||
</a>
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<li>
|
||||
<Link href="/">
|
||||
<a>Home</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="https://github.com/DanieleSalatti/convictionvotingwtf">
|
||||
<a>GitHub</a>
|
||||
</Link>
|
||||
</li>
|
||||
</CenteredFooter>
|
||||
</Section>
|
||||
</Background>
|
||||
);
|
||||
|
||||
export { Footer };
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
import Link from 'next/link';
|
||||
|
||||
import { Background } from '../background/Background';
|
||||
import { Button } from '../button/Button';
|
||||
import { HeroOneButton } from '../hero/HeroOneButton';
|
||||
import { Section } from '../layout/Section';
|
||||
import { NavbarTwoColumns } from '../navigation/NavbarTwoColumns';
|
||||
import { Logo } from './Logo';
|
||||
|
||||
const Hero = () => (
|
||||
<Background color="bg-gray-100">
|
||||
<Section yPadding="py-6">
|
||||
<NavbarTwoColumns logo={<Logo xl />}>
|
||||
<li>
|
||||
<Link href="https://github.com/DanieleSalatti/convictionvotingwtf">
|
||||
<a>GitHub</a>
|
||||
</Link>
|
||||
</li>
|
||||
</NavbarTwoColumns>
|
||||
</Section>
|
||||
|
||||
<Section yPadding="pt-20 pb-32">
|
||||
<HeroOneButton
|
||||
title={
|
||||
<>
|
||||
{'What is \n'}
|
||||
<span className="text-primary-500">Conviction Voting</span>
|
||||
{'\nand why do we need it?'}
|
||||
|
||||
</>
|
||||
}
|
||||
description={
|
||||
<>{'"Conviction Voting offers a novel decision making process that funds proposals based on the aggregated preference of community members, expressed continuously."'}<br />
|
||||
|
||||
<Link href="/">
|
||||
<a href='https://twitter.com/jeffemmett' target='_blank'>
|
||||
Jeff Emmett
|
||||
</a>
|
||||
</Link>
|
||||
{', '}
|
||||
<Link href="/">
|
||||
<a href='https://commonsstack.org/' target='_blank'>
|
||||
@CommonsStack
|
||||
</a>
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
button={null}
|
||||
/*
|
||||
button={
|
||||
<Link href="">
|
||||
<a>
|
||||
<Button xl>CTA</Button>
|
||||
</a>
|
||||
</Link>
|
||||
}*/
|
||||
/>
|
||||
</Section>
|
||||
</Background>
|
||||
);
|
||||
|
||||
export { Hero };
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import { AppConfig } from '../utils/AppConfig';
|
||||
|
||||
type ILogoProps = {
|
||||
xl?: boolean;
|
||||
};
|
||||
|
||||
const Logo = (props: ILogoProps) => {
|
||||
const size = props.xl ? '44' : '32';
|
||||
const fontStyle = props.xl
|
||||
? 'font-semibold text-3xl'
|
||||
: 'font-semibold text-xl';
|
||||
|
||||
return (
|
||||
<span className={`text-gray-900 inline-flex items-center ${fontStyle}`}>
|
||||
<svg
|
||||
className="text-primary-500 stroke-current mr-1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="1.5"
|
||||
fill="none"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M0 0h24v24H0z" stroke="none" />
|
||||
<rect x="3" y="12" width="6" height="8" rx="1" />
|
||||
<rect x="9" y="8" width="6" height="12" rx="1" />
|
||||
<rect x="15" y="4" width="6" height="16" rx="1" />
|
||||
<path d="M4 20h14" />
|
||||
</svg>
|
||||
|
||||
{AppConfig.site_name}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export { Logo };
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
import { VerticalFeatureRow } from '../feature/VerticalFeatureRow';
|
||||
import { Section } from '../layout/Section';
|
||||
|
||||
const VerticalFeatures = () => (
|
||||
<Section
|
||||
title="Working in progress"
|
||||
description="We are working on this page. Please check back later."
|
||||
>
|
||||
{/*
|
||||
<VerticalFeatureRow
|
||||
title="Your title here"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse bibendum, nunc non posuere consectetur, justo erat semper enim, non hendrerit dui odio id enim."
|
||||
image="/assets/images/feature.svg"
|
||||
imageAlt="First feature alt text"
|
||||
/>
|
||||
<VerticalFeatureRow
|
||||
title="Your title here"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse bibendum, nunc non posuere consectetur, justo erat semper enim, non hendrerit dui odio id enim."
|
||||
image="/assets/images/feature2.svg"
|
||||
imageAlt="Second feature alt text"
|
||||
reverse
|
||||
/>
|
||||
<VerticalFeatureRow
|
||||
title="Your title here"
|
||||
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse bibendum, nunc non posuere consectetur, justo erat semper enim, non hendrerit dui odio id enim."
|
||||
image="/assets/images/feature3.svg"
|
||||
imageAlt="Third feature alt text"
|
||||
/>
|
||||
*/}
|
||||
</Section>
|
||||
);
|
||||
|
||||
export { VerticalFeatures };
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
export const AppConfig = {
|
||||
site_name: 'Conviction Voting',
|
||||
title: 'Conviction Voting',
|
||||
description: 'What is conviction voting and why is important?',
|
||||
locale: 'en',
|
||||
};
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
module.exports = {
|
||||
content: ['./src/**/*.{js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
fontSize: {
|
||||
xs: '0.75rem',
|
||||
sm: '0.875rem',
|
||||
base: '1rem',
|
||||
lg: '1.125rem',
|
||||
xl: '1.25rem',
|
||||
'2xl': '1.5rem',
|
||||
'3xl': '1.875rem',
|
||||
'4xl': '2.25rem',
|
||||
'5xl': '3rem',
|
||||
'6xl': '4rem',
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
100: '#E6F6FE',
|
||||
200: '#C0EAFC',
|
||||
300: '#9ADDFB',
|
||||
400: '#4FC3F7',
|
||||
500: '#03A9F4',
|
||||
600: '#0398DC',
|
||||
700: '#026592',
|
||||
800: '#014C6E',
|
||||
900: '#013349',
|
||||
},
|
||||
gray: {
|
||||
100: '#f7fafc',
|
||||
200: '#edf2f7',
|
||||
300: '#e2e8f0',
|
||||
400: '#cbd5e0',
|
||||
500: '#a0aec0',
|
||||
600: '#718096',
|
||||
700: '#4a5568',
|
||||
800: '#2d3748',
|
||||
900: '#1a202c',
|
||||
},
|
||||
},
|
||||
lineHeight: {
|
||||
hero: '4.5rem',
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: {},
|
||||
plugins: [],
|
||||
};
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"allowUnreachableCode": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
"target": "es5",
|
||||
"outDir": "out",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
"jsx": "preserve",
|
||||
"noEmit": true,
|
||||
"isolatedModules": true,
|
||||
"incremental": true
|
||||
},
|
||||
"exclude": ["./out/**/*", "./node_modules/**/*"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||