Apply OpenSats UI enhancements

This commit is contained in:
Artur N
2024-06-06 16:10:21 -03:00
parent a8583e6761
commit ada9405b9c
40 changed files with 6994 additions and 1117 deletions

View File

@@ -1,3 +1,6 @@
{
"extends": "next/core-web-vitals"
"extends": "next/core-web-vitals",
"rules": {
"react-hooks/exhaustive-deps": "off"
}
}

27
components/CustomLink.tsx Normal file
View File

@@ -0,0 +1,27 @@
/* eslint-disable jsx-a11y/anchor-has-content */
import Link from 'next/link'
import { AnchorHTMLAttributes, DetailedHTMLProps } from 'react'
const CustomLink = ({
href,
...rest
}: DetailedHTMLProps<
AnchorHTMLAttributes<HTMLAnchorElement>,
HTMLAnchorElement
>) => {
const isInternalLink = href && href.startsWith('/')
const isAnchorLink = href && href.startsWith('#')
if (isInternalLink) {
// @ts-ignore
return <Link href={href} {...rest} />
}
if (isAnchorLink) {
return <a href={href} {...rest} />
}
return <a target="_blank" rel="noopener noreferrer" href={href} {...rest} />
}
export default CustomLink

View File

@@ -58,7 +58,7 @@ const DonationSteps: React.FC<DonationStepsProps> = ({
const payload = {
amount,
project_slug: projectSlug,
project_name: projectNamePretty
project_name: projectNamePretty,
}
if (email) {
@@ -218,7 +218,9 @@ const DonationSteps: React.FC<DonationStepsProps> = ({
className="color-me-monero h-8 w-8"
/>
)}
<span className="whitespace-nowrap">Donate with Monero or Bitcoin</span>
<span className="whitespace-nowrap">
Donate with Monero or Bitcoin
</span>
</button>
<button
name="stripe"

23
components/Footer.tsx Normal file
View File

@@ -0,0 +1,23 @@
import CustomLink from './CustomLink'
// import SocialIcon from '@/components/social-icons'
function Footer() {
return (
<footer>
<div className="mb-4 mt-16 flex flex-col items-center">
<div className="space-x-4 text-center text-xs text-gray-500 dark:text-gray-400">
MAGIC Grants is a 501(c)(3) non-profit organization. All gifts and
donations are tax-deductible to the full extent of the law.
</div>
<div className="mb-2 flex space-x-2 text-xs text-gray-500 dark:text-gray-400"></div>
<div className="space-x-4 text-center text-xs text-gray-500 dark:text-gray-400">
© {new Date().getFullYear()} MAGIC Grants. This website builds upon
technology by Open Sats.
</div>
</div>
</footer>
)
}
export default Footer

View File

@@ -1,29 +1,41 @@
import Nav from 'react-bootstrap/Nav'
import Navbar from 'react-bootstrap/Navbar'
import Container from 'react-bootstrap/Container'
// import NavDropdown from 'react-bootstrap/NavDropdown'
// import Row from 'react-bootstrap/Row'
// import Col from 'react-bootstrap/Col'
// import Image from 'next/image'
// import Link from 'next/link'
// import samplelogo from '/public/favicon.png'
import Link from './CustomLink'
import MobileNav from './MobileNav'
import ThemeSwitch from './ThemeSwitch'
import headerNavLinks from '../data/headerNavLinks'
import Logo from './Logo'
const Header = () => {
return (
<header className="pb-10 sm:pb-10">
<Navbar collapseOnSelect expand="lg" variant="light" className="color-nav navbar-expand-sm px-2 sm:px-4 py-2.5 fixed w-full z-20 top-0 left-0">
<Container>
<Navbar.Brand href="/">MAGIC Monero Fund</Navbar.Brand>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="me-auto">
<Nav.Link href="/apply">Apply</Nav.Link>
<Nav.Link href="/faq">FAQs</Nav.Link>
<Nav.Link href="/about">About Us</Nav.Link>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
<header className="flex items-center justify-between py-10">
<div>
<Link
href="/"
aria-label="Monero Fund"
className="flex items-center mr-3 gap-4"
>
<Logo className="w-12 h-12" />
<span className="text-lg font-bold">MAGIC Monero Fund</span>
</Link>
</div>
<div className="flex items-center text-base leading-5">
<div className="block">
{headerNavLinks.map((link) => (
<Link
key={link.title}
href={link.href}
className={
link.isButton
? 'rounded border border-orange-500 bg-transparent px-4 py-2 font-semibold text-orange-500 hover:border-transparent hover:bg-orange-500 hover:text-white'
: 'hidden p-1 font-medium text-gray-900 dark:text-gray-100 sm:p-4 md:inline-block'
}
>
{link.title}
</Link>
))}
</div>
<ThemeSwitch />
<MobileNav />
</div>
</header>
)
}

View File

@@ -1,41 +1,30 @@
import Navbar from './Header'
import React from 'react'
import Head from 'next/head'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faBug } from '@fortawesome/free-solid-svg-icons'
import Link from 'next/link'
import { ReactNode } from 'react'
import { Inter } from 'next/font/google'
const Layout: React.FC<React.PropsWithChildren> = ({ children }) => {
import SectionContainer from './SectionContainer'
import Footer from './Footer'
import Header from './Header'
interface Props {
children: ReactNode
}
const inter = Inter({
subsets: ['latin'],
})
const LayoutWrapper = ({ children }: Props) => {
return (
<div className="min-h-screen flex flex-col">
<Head>
<title>MAGIC Monero Fund</title>
<meta name="description" content="TKTK" />
<link rel="icon" href="https://monerofund.org/favicon.ico" />
{/* Twitter */}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:creator" content="@MagicGrants" />
<meta name="twitter:site" content="@MagicGrants" />
<meta name="twitter:title" content="MAGIC Monero Fund" />
<meta name="twitter:image" content="https://monerofund.org/img/crystalball.jpg" />
{/* Open Graph */}
<meta property="og:url" content="https://monerofund.org/" key="ogurl" />
<meta property="og:image" content="https://monerofund.org/img/crystalball.jpg" key="ogimage" />
<meta property="og:site_name" content="MAGIC Monero Fund" key="ogsitename" />
<meta property="og:title" content="MAGIC Monero Fund" key="ogtitle" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:description" content="Support the MAGIC Monero Fund and open source software and research for the Monero Project." key="ogdesc" />
</Head>
<Navbar />
<main className="flex-1 flex flex-col">{ children }</main>
<footer className="flex justify-between p-4 md:p-8 bg-light">
<strong>© MAGIC Grants. This website builds upon technology by Open Sats.</strong>
</footer>
</div>
<SectionContainer>
<div
className={`${inter.className} flex h-screen flex-col justify-between font-sans`}
>
<Header />
<main className="mb-auto">{children}</main>
<Footer />
</div>
</SectionContainer>
)
}
export default Layout
export default LayoutWrapper

127
components/Logo.tsx Normal file

File diff suppressed because one or more lines are too long

725
components/LogoCrystal.tsx Normal file

File diff suppressed because one or more lines are too long

84
components/MobileNav.tsx Normal file
View File

@@ -0,0 +1,84 @@
import { useState } from 'react'
import CustomLink from './CustomLink'
import headerNavLinks from '../data/headerNavLinks'
const MobileNav = () => {
const [navShow, setNavShow] = useState(false)
const onToggleNav = () => {
setNavShow((status) => {
if (status) {
document.body.style.overflow = 'auto'
} else {
// Prevent scrolling
document.body.style.overflow = 'hidden'
}
return !status
})
}
return (
<div className="md:hidden">
<button
className="ml-1 mr-1 h-8 w-8 rounded py-1"
aria-label="Toggle Menu"
onClick={onToggleNav}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="h-8 w-8 text-gray-900 dark:text-gray-100"
>
<path
fillRule="evenodd"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clipRule="evenodd"
/>
</svg>
</button>
<div
className={`fixed left-0 top-0 z-10 h-full w-full transform bg-gray-200 opacity-95 duration-300 ease-in-out dark:bg-gray-800 ${
navShow ? 'translate-x-0' : 'translate-x-full'
}`}
>
<div className="flex justify-end">
<button
className="mr-5 mt-11 h-8 w-8 rounded"
aria-label="Toggle Menu"
onClick={onToggleNav}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="h-8 w-8 text-gray-900 dark:text-gray-100"
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
clipRule="evenodd"
/>
</svg>
</button>
</div>
<nav className="fixed mt-8 h-full">
{headerNavLinks.map((link) => (
<div key={link.title} className="px-12 py-4">
<CustomLink
href={link.href}
className="text-2xl font-bold tracking-tight text-gray-900 dark:text-gray-100"
onClick={onToggleNav}
>
{link.title}
</CustomLink>
</div>
))}
</nav>
</div>
</div>
)
}
export default MobileNav

View File

@@ -0,0 +1,61 @@
import { ReactNode } from 'react'
import Link from 'next/link'
import { ProjectItem } from '../utils/types'
import CustomLink from './CustomLink'
import Image from 'next/image'
import SocialIcon from './social-icons'
interface Props {
project: ProjectItem
children: ReactNode
}
export default function PageHeading({ project, children }: Props) {
return (
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="items-start space-y-2 pb-8 pt-6 md:space-y-5 xl:grid xl:grid-cols-3 xl:gap-x-8">
<Image
src={project.coverImage}
alt="avatar"
width={300}
height={300}
className="h-60 w-60 mx-auto my-auto object-contain row-span-3 hidden xl:block"
/>
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14 xl:col-span-2">
{project.title}
</h1>
<p>{project.summary}</p>
<div></div>
<div className="flex space-x-3 items-center">
<p>
by{' '}
<a className="text-orange-500" href={project.personalWebsite}>
{project.nym}
</a>
</p>
<SocialIcon kind="website" href={project.website} />
{project.twitter && (
<SocialIcon
kind="twitter"
href={`https://twitter.com/${project.twitter}`}
/>
)}
<SocialIcon kind="github" href={project.git} />
{/* {nostr && (
<SocialIcon kind="nostr" href={`https://njump.me/${nostr}`} />
)} */}
</div>
</div>
<div className="items-start space-y-2 xl:grid xl:grid-cols-3 xl:gap-x-8 xl:space-y-0">
{children}
</div>
</div>
)
}

View File

@@ -1,6 +1,5 @@
import ReactModal from 'react-modal'
import Image from 'next/image'
import waffledog from '../public/waffledog.jpg'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faClose } from '@fortawesome/free-solid-svg-icons'
import DonationForm from './DonationForm'
@@ -11,6 +10,7 @@ type ModalProps = {
onRequestClose: () => void
project: ProjectItem | undefined
}
const PaymentModal: React.FC<ModalProps> = ({
isOpen,
onRequestClose,
@@ -25,7 +25,7 @@ const PaymentModal: React.FC<ModalProps> = ({
<ReactModal
isOpen={isOpen}
onRequestClose={onRequestClose}
className="p-16 sm:p-8 bg-white shadow-xl overflow-y-auto max-h-full sm:rounded-xl w-full sm:m-8"
className="max-h-full max-w-4xl w-full overflow-y-auto bg-white p-8 shadow-xl dark:bg-stone-800 sm:m-8 sm:rounded-xl"
overlayClassName="inset-0 fixed bg-[rgba(0,_0,_0,_0.75)] flex items-center justify-center"
appElement={
typeof window === 'undefined'
@@ -33,15 +33,15 @@ const PaymentModal: React.FC<ModalProps> = ({
: document?.getElementById('root') || undefined
}
>
<div className="flex justify-end relative -mb-12">
<div className="relative -mb-12 flex justify-end">
<FontAwesomeIcon
icon={faClose}
className="w-[2rem] h-[2rem] hover:text-primary cursor-pointer"
className="hover:text-primary h-[2rem] w-[2rem] cursor-pointer"
onClick={onRequestClose}
/>
</div>
<div className="flex flex-col space-y-4 py-4">
<div className="flex gap-4 items-center">
<div className="flex items-center gap-4">
<Image
alt={project.title}
src={project.coverImage}
@@ -52,7 +52,7 @@ const PaymentModal: React.FC<ModalProps> = ({
/>
<div className="flex flex-col">
<h2 className="font-sans font-bold">{project.title}</h2>
<h3 className="font-sans text-textgray">Pledge your support</h3>
<h3 className="text-textgray font-sans">Pledge your support</h3>
</div>
</div>
</div>

View File

@@ -1,12 +1,20 @@
import ProgressBar from 'react-bootstrap/ProgressBar';
type ProgressProps = {
text: number;
};
const AnimatedExample = (props: ProgressProps) => {
const { text } = props;
return <ProgressBar animated variant="success" now={text} label={`${text}%`} />;
percent: number
}
export default AnimatedExample;
const Progress = ({ percent }: ProgressProps) => {
return (
<div className="w-full flex flex-col items-center">
<div className="w-full bg-gray-200 rounded-full h-4">
<div
className="bg-green-500 h-4 rounded-full text-xs"
style={{ width: `${percent}%` }}
></div>
</div>
<span className="text-sm font-semibold">{percent}%</span>
</div>
)
}
export default Progress

View File

@@ -1,71 +1,71 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
import escapeHTML from 'escape-html'
import Image from 'next/image'
import Link from 'next/link'
import { useState, useEffect } from 'react'
import { ProjectItem } from '../utils/types'
import ShareButtons from './ShareButtons'
export type ProjectCardProps = {
project: ProjectItem
openPaymentModal: (project: ProjectItem) => void
customImageStyles?: React.CSSProperties
}
const ProjectCard: React.FC<ProjectCardProps> = ({
project,
openPaymentModal,
customImageStyles,
}) => {
const { slug, title, summary, coverImage, git, twitter, personalTwitter, personalWebsite, nym, goal, isFunded } =
project
const [isHorizontal, setIsHorizontal] = useState<boolean | null>(null)
useEffect(() => {
const img = document.createElement('img')
img.src = project.coverImage
// check if image is horizontal - added additional 10% to height to ensure only true
// horizontals get flagged.
img.onload = () => {
const { naturalWidth, naturalHeight } = img
const isHorizontal = naturalWidth >= naturalHeight * 1.1
setIsHorizontal(isHorizontal)
}
}, [project.coverImage])
let cardStyle
// if (tags.includes('Nostr')) {
// cardStyle =
// 'h-full space-y-4 rounded-xl border-b-4 border-purple-600 bg-stone-100 dark:border-purple-600 dark:bg-stone-900'
// } else if (tags.includes('Lightning')) {
// cardStyle =
// 'h-full space-y-4 rounded-xl border-b-4 border-yellow-300 bg-stone-100 dark:border-yellow-300 dark:bg-stone-900'
// } else if (tags.includes('Bitcoin')) {
// cardStyle =
// 'h-full space-y-4 rounded-xl border-b-4 border-orange-400 bg-stone-100 dark:border-orange-400 dark:bg-stone-900'
// } else {
cardStyle =
'h-full space-y-4 rounded-xl border-b-4 border-orange-500 bg-stone-100 dark:bg-stone-900'
// }
return (
<figure className=" bg-white space-y-4 border border-lightgray rounded-xl h-full">
<div className="relative h-64 w-full">
<Link href={`/projects/${escapeHTML(slug)}`} passHref>
<div className='relative h-64 w-full'>
<Image
alt={title}
src={coverImage}
layout="fill"
objectFit="cover"
objectPosition="50% 50%"
className="rounded-t-xl border border-lightgray cursor-pointer"
/>
</div>
</Link>
</div>
<figcaption className="p-4 space-y-4">
<h2>{title}</h2>
<p>
by{' '}
<Link href={escapeHTML(personalWebsite)} passHref legacyBehavior>
<a className="projectlist">{nym}</a>
</Link>
</p>
<p className="prose line-clamp-3">{summary}</p>
<div className="flex justify-end"></div>
<ShareButtons project={project} />
<div className="flex space-x-4 items-center justify-center pt-4">
{(isFunded)? `` : <button
className="bg-black basis-1/2"
onClick={() => openPaymentModal(project)}
>
Donate
</button> }
<div className="flex items-center justify-center basis-1/2">
<Link href={`/projects/${escapeHTML(slug)}`} passHref legacyBehavior>
<a className="projectlist">View Details</a>
</Link>
<FontAwesomeIcon
icon={faArrowRight}
className="ml-1 w-4 h-4 text-textgray cursor-pointer"
/>
</div>
<figure className={cardStyle}>
<Link href={`/projects/${project.slug}`} passHref>
<div className="flex h-36 w-full sm:h-52">
<Image
alt={project.title}
src={project.coverImage}
width={1200}
height={1200}
style={{
objectFit: 'cover',
...customImageStyles,
}}
priority={true}
className="cursor-pointer rounded-t-xl bg-white"
/>
</div>
</figcaption>
<figcaption className="p-2">
<h2 className="font-bold">{project.title}</h2>
<div className="mb-4 text-sm">by {project.nym}</div>
<div className="mb-2 line-clamp-3">{project.summary}</div>
</figcaption>
</Link>
</figure>
)
}

View File

@@ -9,37 +9,31 @@ type ProjectListProps = {
header?: string
exclude?: string
projects: ProjectItem[]
openPaymentModal: (project: ProjectItem) => void
}
const ProjectList: React.FC<ProjectListProps> = ({
header = 'Explore Projects',
exclude,
projects,
openPaymentModal,
}) => {
const [sortedProjects, setSortedProjects] = useState<ProjectItem[]>()
useEffect(() => {
setSortedProjects(projects.filter(p => p.slug !== exclude).sort(() => 0.5 - Math.random()))
setSortedProjects(
projects.filter((p) => p.slug !== exclude).sort(() => 0.5 - Math.random())
)
}, [projects])
return (
<section className="p-4 md:p-8 flex flex-col items-center">
<div className="flex justify-between items-center pb-8 w-full">
<h1>{header}</h1>
<div className="flex items-center">
<Link href="/projects" className="projectlist">View All</Link>
<FontAwesomeIcon
icon={faArrowRight}
className="ml-1 w-4 h-4 text-textgray cursor-pointer"
/>
</div>
</div>
<ul className="grid md:grid-cols-3 gap-4 max-w-5xl">
<section className="bg-light items-left flex flex-col">
<ul className="grid max-w-5xl grid-cols-2 gap-4 md:grid-cols-4">
{sortedProjects &&
sortedProjects.slice(0, 3).map((p, i) => (
sortedProjects.slice(0, 4).map((p, i) => (
<li key={i} className="">
<ProjectCard project={p} openPaymentModal={openPaymentModal} />
<ProjectCard
project={p}
// tags={p.tags}
/>
</li>
))}
</ul>

View File

@@ -0,0 +1,13 @@
import { ReactNode } from 'react'
interface Props {
children: ReactNode
}
export default function SectionContainer({ children }: Props) {
return (
<section className="mx-2 max-w-6xl px-4 sm:px-6 lg:mx-auto xl:mx-auto xl:max-w-5xl xl:px-0">
{children}
</section>
)
}

View File

@@ -0,0 +1,41 @@
import { useEffect, useState } from 'react'
import { useTheme } from 'next-themes'
const ThemeSwitch = () => {
const [mounted, setMounted] = useState(false)
const { theme, setTheme, resolvedTheme } = useTheme()
// When mounted on client, now we can show the UI
useEffect(() => setMounted(true), [])
return (
<button
aria-label="Toggle Dark Mode"
className="ml-1 mr-1 h-8 w-8 rounded p-1 sm:ml-4"
onClick={() =>
setTheme(
theme === 'dark' || resolvedTheme === 'dark' ? 'light' : 'dark'
)
}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="text-gray-900 dark:text-gray-100"
>
{mounted && (theme === 'dark' || resolvedTheme === 'dark') ? (
<path
fillRule="evenodd"
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
clipRule="evenodd"
/>
) : (
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
)}
</svg>
</button>
)
}
export default ThemeSwitch

29
components/Typing.tsx Normal file
View File

@@ -0,0 +1,29 @@
import React from 'react'
import Typed from 'typed.js'
export default function Typing() {
const el = React.useRef(null)
React.useEffect(() => {
const typed = new Typed(el.current, {
strings: [
'Monero Fund',
'Open Source Software',
'The Monero Project',
],
typeSpeed: 50,
loop: true,
cursorChar: '_',
})
return () => {
typed.destroy()
}
}, [])
return (
<>
<span ref={el} />
</>
)
}

View File

@@ -0,0 +1 @@
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Facebook icon</title><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>

After

Width:  |  Height:  |  Size: 403 B

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub icon</title><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>

After

Width:  |  Height:  |  Size: 827 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M352 256c0 22.2-1.2 43.6-3.3 64H163.3c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64H348.7c2.2 20.4 3.3 41.8 3.3 64zm28.8-64H503.9c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64H380.8c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32H376.7c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0H167.7c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 20.9 58.2 27 94.7zm-209 0H18.6C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192H131.2c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64H8.1C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6H344.3c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352H135.3zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6H493.4z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,54 @@
import Mail from './mail.svg'
import Github from './github.svg'
import Facebook from './facebook.svg'
import Youtube from './youtube.svg'
import Linkedin from './linkedin.svg'
import Twitter from './twitter.svg'
import Nostr from './nostr.svg'
import Web from './globe-solid.svg'
// Icons taken from: https://simpleicons.org/ and https://fontawesome.com (globe)
const components = {
mail: Mail,
github: Github,
facebook: Facebook,
youtube: Youtube,
linkedin: Linkedin,
twitter: Twitter,
nostr: Nostr,
website: Web,
}
type Props = {
kind: keyof typeof components
href: string
size?: number
}
const SocialIcon = ({ kind, href, size = 5 }: Props) => {
if (
!href ||
(kind === 'mail' &&
!/^mailto:\w+([.-]?\w+)@\w+([.-]?\w+)(.\w{2,3})+$/.test(href))
)
return <></>
const SocialSvg = components[kind]
return (
<a
className="text-sm text-gray-500 transition hover:text-gray-600"
target="_blank"
rel="noopener noreferrer"
href={href}
>
<span className="sr-only">{kind}</span>
<SocialSvg
className={`fill-current text-gray-700 hover:text-orange-500 dark:text-gray-200 dark:hover:text-orange-500 h-${size} w-${size}`}
/>
</a>
)
}
export default SocialIcon

View File

@@ -0,0 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>LinkedIn icon</title><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.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 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>

After

Width:  |  Height:  |  Size: 615 B

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
</svg>

After

Width:  |  Height:  |  Size: 224 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M278.5 215.6L23 471c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l57-57h68c49.7 0 97.9-14.4 139-41c11.1-7.2 5.5-23-7.8-23c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l81-24.3c2.5-.8 4.8-2.1 6.7-4l22.4-22.4c10.1-10.1 2.9-27.3-11.3-27.3l-32.2 0c-5.1 0-9.2-4.1-9.2-9.2c0-4.1 2.7-7.6 6.5-8.8l112-33.6c4-1.2 7.4-3.9 9.3-7.7C506.4 207.6 512 184.1 512 160c0-41-16.3-80.3-45.3-109.3l-5.5-5.5C432.3 16.3 393 0 352 0s-80.3 16.3-109.3 45.3L139 149C91 197 64 262.1 64 330v55.3L253.6 195.8c6.2-6.2 16.4-6.2 22.6 0c5.4 5.4 6.1 13.6 2.2 19.8z"/></svg>

After

Width:  |  Height:  |  Size: 770 B

View File

@@ -0,0 +1 @@
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Twitter icon</title><path d="M23.953 4.57a10 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.69 8.095 4.067 6.13 1.64 3.162a4.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.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg>

After

Width:  |  Height:  |  Size: 607 B

View File

@@ -0,0 +1 @@
<svg role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>YouTube icon</title><path d="M23.499 6.203a3.008 3.008 0 00-2.089-2.089c-1.87-.501-9.4-.501-9.4-.501s-7.509-.01-9.399.501a3.008 3.008 0 00-2.088 2.09A31.258 31.26 0 000 12.01a31.258 31.26 0 00.523 5.785 3.008 3.008 0 002.088 2.089c1.869.502 9.4.502 9.4.502s7.508 0 9.399-.502a3.008 3.008 0 002.089-2.09 31.258 31.26 0 00.5-5.784 31.258 31.26 0 00-.5-5.808zm-13.891 9.4V8.407l6.266 3.604z"/></svg>

After

Width:  |  Height:  |  Size: 474 B

7
data/headerNavLinks.ts Normal file
View File

@@ -0,0 +1,7 @@
const headerNavLinks = [
{title: 'Apply', href: '/apply', isButton: false},
{title: 'FAQs', href: '/faq', isButton: false},
{title: 'About', href: '/about', isButton: false}
]
export default headerNavLinks

View File

@@ -2,6 +2,14 @@
const nextConfig = {
reactStrictMode: true,
output: 'standalone',
webpack: (config, options) => {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
})
return config
},
}
module.exports = nextConfig

5504
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,6 @@
"watch": "npm-watch"
},
"dependencies": {
"@fontsource/source-code-pro": "^5.0.18",
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-brands-svg-icons": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
@@ -18,9 +17,11 @@
"@sendgrid/mail": "^8.1.3",
"@stripe/react-stripe-js": "^2.7.1",
"@stripe/stripe-js": "^3.4.1",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.7",
"@types/escape-html": "^1.0.4",
"base-64": "^1.0.0",
"bootstrap": "^5.3.3",
"classnames": "^2.5.1",
"dotenv": "^16.4.5",
"escape-html": "^1.0.3",
"exclude": "^1.0.0",
@@ -29,8 +30,8 @@
"micro": "^10.0.1",
"micro-cors": "^0.1.1",
"next": "^14.2.3",
"next-themes": "^0.3.0",
"react": "^18.3.1",
"react-bootstrap": "^2.10.2",
"react-dom": "^18.3.1",
"react-hook-form": "^7.51.5",
"react-modal": "^3.16.1",
@@ -41,11 +42,13 @@
"sharp": "^0.33.4",
"stripe": "^15.9.0",
"swr": "^2.2.5",
"typed.js": "^2.1.0",
"watch": "^0.13.0",
"wicg-inert": "^3.1.2",
"xss": "^1.0.15"
},
"devDependencies": {
"@svgr/webpack": "^8.1.0",
"@tailwindcss/line-clamp": "^0.4.4",
"@tailwindcss/typography": "^0.5.13",
"@types/base-64": "^1.0.2",

View File

@@ -1,22 +1,22 @@
import '@fontsource/source-code-pro/400.css'
import '@fontsource/source-code-pro/600.css'
import '@fontsource/source-code-pro/800.css'
import '../styles/globals.css'
import '../node_modules/bootstrap/dist/css/bootstrap.css'
import { useEffect } from "react";
import type { AppProps } from 'next/app'
import { ThemeProvider } from 'next-themes'
import '../styles/globals.css'
import '../styles/tailwind.css'
import Layout from '../components/Layout'
import Head from 'next/head'
function MyApp({ Component, pageProps }: AppProps) {
useEffect(() => {
require("../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js");
}, []);
return (
<ThemeProvider attribute="class" defaultTheme='system'>
<Head>
<meta content="width=device-width, initial-scale=1" name="viewport" />
</Head>
<Layout>
<Component {...pageProps} />
</Layout>
</ThemeProvider>
)
}

View File

@@ -1,9 +1,15 @@
import markdownToHtml from '../utils/markdownToHtml'
import { getSingleFile } from '../utils/md'
import BigDumbMarkdown from '../components/BigDumbMarkdown'
import xss from 'xss'
export default function About({ content }: { content: string }) {
return <BigDumbMarkdown content={content} />
return (
<article
className="prose max-w-3xl mx-auto pb-8 pt-8 dark:prose-dark xl:col-span-2"
dangerouslySetInnerHTML={{ __html: xss(content || '') }}
/>
)
}
export async function getStaticProps() {

View File

@@ -4,7 +4,7 @@ import { useForm } from 'react-hook-form'
import { fetchPostJSON } from '../utils/api-helpers'
import Link from 'next/link'
export default function Apply() {
async function handleClick() { }
async function handleClick() {}
const [loading, setLoading] = useState(false)
const router = useRouter()
const {
@@ -26,62 +26,94 @@ export default function Apply() {
}
return (
<div className="flex-1 flex flex-col items-center justify-center gap-4 py-8">
<div className="mx-auto flex-1 flex flex-col items-center justify-center gap-4 py-8 prose dark:prose-dark">
<form
onSubmit={handleSubmit(onSubmit)}
className="apply flex flex-col gap-4 p-4 max-w-2xl"
>
{/* {errors.exampleRequired && <span>This field is required</span>} */}
<div className="prose">
<h1>Application for Monero Fund Project Listing or General Fund Grant</h1>
<p>
Thanks for your interest in the Monero Fund!
</p>
<div>
<h1>
Application for Monero Fund Project Listing or General Fund Grant
</h1>
<p>Thanks for your interest in the Monero Fund!</p>
<p>
We&#39;re incredibly grateful to contributors like you working to
support Monero, Bitcoin and other free and open source projects.
</p>
<p>
The MAGIC Monero Fund is offering a grant program and fundraising platform to support Monero research and development,
especially relating to privacy, security, user experience, and efficiency.
Proposals can be related to the Monero protocol directly, or they can be related to other
areas of the Monero ecosystem. For research projects, please refer to special instructions
The MAGIC Monero Fund is offering a grant program and fundraising
platform to support Monero research and development, especially
relating to privacy, security, user experience, and efficiency.
Proposals can be related to the Monero protocol directly, or they
can be related to other areas of the Monero ecosystem. For research
projects, please refer to special instructions
<Link href="/apply_research"> here</Link>.
</p>
<h2>Proposal Evaluation Criteria</h2>
<div>
Submitted proposals will be evaluated by the committee based on the following criteria:
Submitted proposals will be evaluated by the committee based on the
following criteria:
<ul>
<li><b>Impact:</b> The proposal should have a clear impact on the Monero Project.</li>
<li><b>Originality:</b> The proposal should be original and not a rehash of existing work.</li>
<li><b>Feasibility:</b> The proposal should be feasible to complete within the proposed time frame.</li>
<li><b>Quality:</b> The proposal should be well-written and well-organized.</li>
<li><b>Relevance:</b> The proposal should be relevant to the Monero Project.</li>
<li>
<b>Impact:</b> The proposal should have a clear impact on the
Monero Project.
</li>
<li>
<b>Originality:</b> The proposal should be original and not a
rehash of existing work.
</li>
<li>
<b>Feasibility:</b> The proposal should be feasible to complete
within the proposed time frame.
</li>
<li>
<b>Quality:</b> The proposal should be well-written and
well-organized.
</li>
<li>
<b>Relevance:</b> The proposal should be relevant to the Monero
Project.
</li>
</ul>
</div>
<h2 id="Eligibility">Eligibility</h2>
<p>
All qualified researchers are eligible to apply, regardless of educational attainment or occupation.
However, as a nonprofit organization registered under U.S. tax laws, MAGIC Grants is required to comply
with certain laws when disbursing funds to grant recipients.
Grant recipients must complete a Due Diligence checklist,
which are the last two pages of <a href="https://magicgrants.org/funds/MAGIC%20Fund%20Grant%20Disbursement%20Process%20and%20Requirements.pdf">this document</a>.
This includes the collection of your ID and tax information. We will conduct sanctions checks.
All qualified researchers are eligible to apply, regardless of
educational attainment or occupation. However, as a nonprofit
organization registered under U.S. tax laws, MAGIC Grants is
required to comply with certain laws when disbursing funds to grant
recipients. Grant recipients must complete a Due Diligence
checklist, which are the last two pages of{' '}
<a href="https://magicgrants.org/funds/MAGIC%20Fund%20Grant%20Disbursement%20Process%20and%20Requirements.pdf">
this document
</a>
. This includes the collection of your ID and tax information. We
will conduct sanctions checks.
</p>
<h2>How to Submit a Proposal</h2>
<p>
To submit a proposal, please complete the form below or create an issue on <a href="https://github.com/MAGICGrants/Monero-Fund/issues/new?assignees=&labels=&template=grant-application.md&title=[Grant+Title]">Github</a>.
Applicants are free to use their legal name or a pseudonym at this step,
although note the <a href="#Eligibility"><b>Eligibility</b></a> section.
You can choose to apply for a direct grant from the MAGIC Monero Fund&#39;s General Fund and/or
request that your project be listed on MoneroFund.org to raise funds from Monero
community members.
To submit a proposal, please complete the form below or create an
issue on{' '}
<a href="https://github.com/MAGICGrants/Monero-Fund/issues/new?assignees=&labels=&template=grant-application.md&title=[Grant+Title]">
Github
</a>
. Applicants are free to use their legal name or a pseudonym at this
step, although note the{' '}
<a href="#Eligibility">
<b>Eligibility</b>
</a>{' '}
section. You can choose to apply for a direct grant from the MAGIC
Monero Fund&#39;s General Fund and/or request that your project be
listed on MoneroFund.org to raise funds from Monero community
members.
</p>
<p>
Please note this form is not considered confidential and is effectively equivalent to a public GitHub issue.
In order to reach out privately, please send an email to MoneroFund@magicgrants.org.
Please note this form is not considered confidential and is
effectively equivalent to a public GitHub issue. In order to reach
out privately, please send an email to MoneroFund@magicgrants.org.
</p>
</div>
<label className="checkbox">
<input type="checkbox" {...register('general_fund')} />
Apply to receive a grant from the Magic Monero Fund.
@@ -92,118 +124,192 @@ export default function Apply() {
Apply for project to be listed on the Monero Fund Donation Page.
</label>
<label>
Project Name *
<small>
The name of the project to be listed for fundraising
</small>
<div className="w-full flex flex-col">
<label htmlFor="project_name">Project Name *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="project_name"
type="text"
{...register('project_name', { required: true })}
/>
</label>
</div>
<label>
Your Name or Pseudonym *
<input type="text" {...register('your_name', { required: true })} />
</label>
<label>
Email *
<input type="text" {...register('email', { required: true })} />
</label>
<label>
Project Github (if applicable)
<input type="text" {...register('github')} />
</label>
<label>
Personal Github (if applicable)
<input type="text" {...register('personal_github')} />
</label>
<label>
Other Contact Details (if applicable)
<div className="w-full flex flex-col">
<label htmlFor="your_name">Your Name or Pseudonym *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="your_name"
type="text"
{...register('your_name', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="email">Email *</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="email"
type="text"
{...register('email', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="github">Project GitHub (if applicable)</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="github"
type="text"
{...register('github')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="personal_github">
Personal GitHub (if applicable)
</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="personal_github"
type="text"
{...register('personal_github')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="other_contact">
Other Contact Details (if applicable)
</label>
<small>
Please list any other relevant contact details you are comfortable
sharing in case we need to reach out with questions.
sharing in case we need to reach out with questions. These could
include github username, twitter username, LinkedIn, Reddit handle,
other social media handles, emails, phone numbers, usernames, etc.
</small>
<small>
These could include github username, twitter username, LinkedIn,
reddit handle, other social media handles, emails, phone
numbers, usernames, etc.
</small>
<textarea {...register('other_contact')} />
</label>
<label>
Short Project Description *
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="other_contact"
{...register('other_contact')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="short_description">Short Project Description *</label>
<small>
This will be listed on the explore projects page of the Monero Fund
website. 2-3 sentences.
</small>
<textarea {...register('short_description', { required: true })} />
</label>
<label>
Long Project Description
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="short_description"
{...register('short_description', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="long_description">Long Project Description</label>
<small>
This will be listed on your personal project page of the Monero Fund
website. It can be longer and go into detail about your project.
</small>
<textarea {...register('long_description')} />
</label>
{/* <label>
Project Images: (attachment)
<input type="text" {...register('your_name', { required: true })} />
</label> */}
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="long_description"
{...register('long_description')}
/>
</div>
<label className="checkbox">
<input type="checkbox" {...register('free_open_source')} />
Is the project free and open source?
</label>
<label className="checkbox">
<input type="checkbox" {...register('are_you_lead')} />
Are you the Project Lead / Lead Contributor
</label>
<label>
If someone else, please list the project&#39;s Lead Contributor or
Maintainer <input type="text" {...register('other_lead')} />
</label>
<label>
Potential Impact *
<small>
Why is this project important to the Monero community?
</small>
<textarea {...register('potential_impact', { required: true })} />
</label>
<label>
Project Timelines and Potential Milestones *
<textarea {...register('timelines', { required: true })} />
</label>
<label>
If you&#39;re applying for a grant from the general fund, please
submit a proposed budget for the requested amount and how it will be used.
<input type="text" {...register('proposed_budget')} />
</label>
<label>
Applicant Bios (Optional)
<small>
List revevant accomplishments.
</small>
<textarea {...register('bios')} />
</label>
<div className="prose">
<small>
The MAGIC Monero Fund may require each recipient to sign a Grant Agreement
before any funds are disbursed. This agreement will set milestones and funds
will only be released upon completion of milestones. In order to comply with
US regulations, recipients will need to identify themselves to MAGIC, in
accordance with US law.
</small>
<div className="w-full flex flex-col">
<label htmlFor="other_lead">
If someone else, please list the project&#39;s Lead Contributor or
Maintainer
</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="other_lead"
type="text"
{...register('other_lead')}
/>
</div>
<button type="submit" disabled={loading}>
<div className="w-full flex flex-col">
<label htmlFor="potential_impact">Potential Impact *</label>
<small>Why is this project important to the Monero community?</small>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="potential_impact"
{...register('potential_impact', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="timelines">
Project Timelines and Potential Milestones *
</label>
<textarea
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="timelines"
{...register('timelines', { required: true })}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="proposed_budget">
If you&#39;re applying for a grant from the general fund, please
submit a proposed budget for the requested amount and how it will be
used.
</label>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="proposed_budget"
type="text"
{...register('proposed_budget')}
/>
</div>
<div className="w-full flex flex-col">
<label htmlFor="bios">Applicant Bios (Optional)</label>
<small>List relevant accomplishments.</small>
<input
className="appearance-none block w-full text-gray-700 border rounded py-2 px-3 mb-3 leading-tight focus:outline-none focus:ring-0"
id="bios"
type="text"
{...register('bios')}
/>
</div>
<small>
The MAGIC Monero Fund may require each recipient to sign a Grant
Agreement before any funds are disbursed. This agreement will set
milestones and funds will only be released upon completion of
milestones. In order to comply with US regulations, recipients will
need to identify themselves to MAGIC, in accordance with US law.
</small>
<button
className="mb-2 mr-2 w-full rounded bg-orange-500 px-4 text-xl font-semibold text-white hover:border-transparent hover:bg-orange-500 hover:text-black dark:text-black dark:hover:text-white md:max-w-[98%] disabled:opacity-50 disabled:cursor-not-allowed"
disabled={loading}
>
Apply
</button>
<p>
After submitting your application, please allow our team up to three weeks to review your application.
Email us at <a href="mailto:monerofund@magicgrants.org">monerofund@magicgrants.org</a> if you have any questions.
After submitting your application, please allow our team up to three
weeks to review your application. Email us at{' '}
<a href="mailto:monerofund@magicgrants.org">
monerofund@magicgrants.org
</a>{' '}
if you have any questions.
</p>
</form>
</div>

View File

@@ -1,9 +1,15 @@
import markdownToHtml from '../utils/markdownToHtml'
import { getSingleFile } from '../utils/md'
import BigDumbMarkdown from '../components/BigDumbMarkdown'
import xss from 'xss'
export default function Faq({ content }: { content: string }) {
return <BigDumbMarkdown content={content} />
return (
<article
className="prose max-w-3xl mx-auto pb-8 pt-8 dark:prose-dark xl:col-span-2"
dangerouslySetInnerHTML={{ __html: xss(content || '') }}
/>
)
}
export async function getStaticProps() {

View File

@@ -10,6 +10,8 @@ import magiclogo from '/public/img/crystalball.jpg'
import { getAllPosts } from '../utils/md'
import { ProjectItem } from '../utils/types'
import { useRouter } from 'next/router'
import Typing from '../components/Typing'
import CustomLink from '../components/CustomLink'
// These shouldn't be swept up in the regular list so we hardcode them
const generalFund: ProjectItem = {
@@ -18,8 +20,7 @@ const generalFund: ProjectItem = {
website: 'https://monerofund.org',
personalWebsite: 'https://monerofund.org',
title: 'MAGIC Monero General Fund',
summary:
'Support contributors to Monero',
summary: 'Support contributors to Monero',
coverImage: '/img/crystalball.jpg',
git: 'magicgrants',
twitter: 'magicgrants',
@@ -48,7 +49,7 @@ const Home: NextPage<{ projects: any }> = ({ projects }) => {
useEffect(() => {
if (router.isReady) {
console.log(router.query);
console.log(router.query)
}
}, [router.isReady])
@@ -59,31 +60,62 @@ const Home: NextPage<{ projects: any }> = ({ projects }) => {
<meta name="description" content="TKTK" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<section id="root" className="flex flex-col sm:flex-row">
<div className="flex-auto p-4 md:p-8 space-y-8 sm:order-first">
<h1>
Support the Monero Fund and open source software and research for the Monero Project.
</h1>
<p className="text-textgray">
Donations are tax-deductible in the USA as MAGIC is a 501(c)(3) charity
</p>
<button role={'button'} onClick={openGeneralFundModal}>
Donate to Monero Committee General Fund
</button>
<p>
Want to receive funding for your work? {' '}
<Link href="/apply" legacyBehavior >
<a className="custom-link">Apply for a Monero development or research grant!</a>
</Link>
</p>
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="pt-4 md:pb-8">
<h1 className="py-4 text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
Support <Typing />
</h1>
<p className="text-xl leading-7 text-gray-500 dark:text-gray-400">
Help us to provide sustainable funding for free and open-source
contributors working on freedom tech and projects that help Monero
flourish.
</p>
<div className="flex flex-wrap py-4">
<div className="w-full md:w-1/2">
<button
onClick={openGeneralFundModal}
className="mb-2 mr-2 w-full rounded bg-orange-500 px-4 text-xl font-semibold text-white hover:border-transparent hover:bg-orange-500 hover:text-black dark:text-black dark:hover:text-white md:max-w-[98%]"
>
Donate to Monero Comittee General Fund
</button>
</div>
</div>
<div className="order-first flex-auto my-auto">
<Image className="img-fluid" src={magiclogo} alt="magiclogo" />
<p className="text-lg leading-7 text-gray-500 dark:text-gray-400">
Want to receive funding for your work?
<CustomLink href="/apply" className="text-orange-500">
{' '}
Apply for a Monero development or research grant!
</CustomLink>
</p>
<p className="text-md leading-7 text-gray-500 dark:text-gray-400">
We are a 501(c)(3) public charity. All donations are tax deductible.
</p>
</div>
</div>
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<div className="xl:pt-18 space-y-2 pt-8 md:space-y-5">
<h1 className="text-3xl font-extrabold leading-9 tracking-tight text-gray-900 dark:text-gray-100 sm:text-4xl sm:leading-10 md:text-6xl md:leading-14">
Explore Projects
</h1>
<p className="pt-2 text-lg leading-7 text-gray-500 dark:text-gray-400">
Browse through a showcase of projects supported by us.
</p>
<ProjectList projects={projects} />
<div className="flex justify-end pt-4 text-base font-medium leading-6">
<Link
href="/projects"
className="text-primary-500 hover:text-primary-600 dark:hover:text-primary-400"
aria-label="View All Projects"
>
View Projects &rarr;
</Link>
</div>
</section>
<ProjectList projects={projects} openPaymentModal={openPaymentModal} />
</main>
</div>
</div>
<PaymentModal
isOpen={modalOpen}
onRequestClose={closeModal}

View File

@@ -1,21 +1,22 @@
import { useState } from 'react'
import { useRouter } from 'next/router'
import { NextPage } from 'next/types'
import ErrorPage from 'next/error'
import sanitize from 'sanitize-filename'
import Image from 'next/image'
import xss from 'xss'
import { ProjectItem, Stats } from '../../utils/types'
import { getPostBySlug, getAllPosts } from '../../utils/md'
import markdownToHtml from '../../utils/markdownToHtml'
import markdownStyles from '../../components/markdown-styles.module.css'
import Image from 'next/image'
import ProjectList from '../../components/ProjectList'
import BackToProjects from '../../components/BackToProjects'
import { ProjectItem, Stats } from '../../utils/types'
import { NextPage } from 'next/types'
import { useEffect, useState } from 'react'
import {
fetchPostJSON,
fetchGetJSONAuthedBTCPay,
fetchGetJSONAuthedStripe,
} from '../../utils/api-helpers'
import PaymentModal from '../../components/PaymentModal'
import Link from 'next/link'
import ShareButtons from '../../components/ShareButtons'
import PageHeading from '../../components/PageHeading'
import SocialIcon from '../../components/social-icons'
import Progress from '../../components/Progress'
import { fetchPostJSON, fetchGetJSONAuthedBTCPay, fetchGetJSONAuthedStripe } from '../../utils/api-helpers'
import xss from 'xss'
type SingleProjectPageProps = {
project: ProjectItem
@@ -23,9 +24,11 @@ type SingleProjectPageProps = {
stats: Stats
}
const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats }) => {
const Project: NextPage<SingleProjectPageProps> = ({
project,
projects,
stats,
}) => {
const router = useRouter()
const [modalOpen, setModalOpen] = useState(false)
@@ -58,10 +61,9 @@ const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats })
isFunded,
} = project
function formatBtc(bitcoin: number) {
if (bitcoin > 0.1) {
return `${bitcoin.toFixed(3) || 0.00}`;
return `${bitcoin.toFixed(3) || 0.0}`
} else {
return `${Math.floor(bitcoin * 100000000).toLocaleString()} sats`
}
@@ -69,23 +71,92 @@ const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats })
function formatUsd(dollars: number): string {
if (dollars == 0) {
return ""
} else if
(dollars / 1000 > 1) {
return `+ $${Math.round(dollars / 1000)}k`
return '$0'
} else if (dollars / 1000 > 1) {
return `$${Math.round(dollars / 1000)}k+`
} else {
return `+ $${dollars.toFixed(0)}`
return `$${dollars.toFixed(0)}`
}
}
if (!router.isFallback && !slug) {
return <ErrorPage statusCode={404} />
}
return (
<>
<div className="flex flex-col items-center">
<div className={"h-[15rem] w-full relative bg-gradient-to-b from-white to-gray-200"}>
<div className="divide-y divide-gray-200 dark:divide-gray-700">
<PageHeading project={project}>
<div className="flex flex-col items-center space-x-2 pt-8 xl:block">
<Image
src={coverImage}
alt="avatar"
width={300}
height={300}
className="h-72 w-72 mx-auto object-contain xl:hidden"
/>
<div className="space-y-4">
<h1 className="mb-4 font-bold">Raised</h1>
<Progress
percent={Math.floor(
((stats[slug].xmr.totaldonationsinfiat +
stats[slug].btc.totaldonationsinfiat +
stats[slug].usd.totaldonationsinfiat) /
goal) *
100
)}
/>
<ul className="font-semibold space-y-1">
<li className="flex items-center space-x-1">
<span className="text-green-500 text-xl">{`${formatUsd(stats[slug].xmr.totaldonationsinfiat + stats[slug].btc.totaldonationsinfiat + stats[slug].usd.totaldonationsinfiat)}`}</span>{' '}
<span className="font-normal text-sm text-gray">
in{' '}
{stats[slug].xmr.numdonations +
stats[slug].btc.numdonations +
stats[slug].usd.numdonations}{' '}
donations total
</span>
</li>
<li>
{stats[slug].xmr.totaldonations} XMR{' '}
<span className="font-normal text-sm text-gray">
in {stats[slug].xmr.numdonations} {''}
donations
</span>
</li>
<li>
{stats[slug].btc.totaldonations} BTC{' '}
<span className="font-normal text-sm text-gray">
in {stats[slug].btc.numdonations} {''}
donations
</span>
</li>
<li>
{`${formatUsd(stats[slug].usd.totaldonations)}`} Fiat{' '}
<span className="font-normal text-sm text-gray">
in {stats[slug].usd.numdonations} {''}
donations
</span>
</li>
</ul>
</div>
</div>
<article
className="prose max-w-none pb-8 pt-8 dark:prose-dark xl:col-span-2"
dangerouslySetInnerHTML={{ __html: xss(content || '') }}
/>
</PageHeading>
</div>
{/* <div className="flex flex-col items-center">
<div
className={
'h-[15rem] w-full relative bg-gradient-to-b from-white to-gray-200'
}
>
<Image
alt={title}
src={coverImage}
@@ -95,12 +166,15 @@ const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats })
/>
</div>
<div className="flex w-full p-4 py-8 md:px-8">
</div>
<div className="flex w-full p-4 py-8 md:px-8"></div>
<article className="px-4 md:px-8 pb-8 lg:flex lg:flex-row-reverse lg:items-start">
<div className={markdownStyles['markdown']}>
</div>
<aside className="p-4 bg-light rounded-xl flex lg:flex-col lg:items-start gap-4 min-w-[20rem] justify-between items-center mb-8">
{(isFunded)? `` : <button onClick={openPaymentModal}>Donate</button> }
{stats &&
{isFunded ? `` : <button onClick={openPaymentModal}>Donate</button>}
{stats && (
<div>
<h5>Raised</h5>
<h4>{`${formatUsd(stats[slug].xmr.totaldonationsinfiat + stats[slug].btc.totaldonationsinfiat + stats[slug].usd.totaldonationsinfiat)}`}</h4>
@@ -108,40 +182,46 @@ const Project: NextPage<SingleProjectPageProps> = ({ project, projects, stats })
<h6>{stats[slug].btc.totaldonations} BTC</h6>
<h6>{`${formatUsd(stats[slug].usd.totaldonations)}`} Fiat</h6>
</div>
}
)}
{stats && <div>
<h5>Donations</h5>
<h4>{stats[slug].xmr.numdonations + stats[slug].btc.numdonations + stats[slug].usd.numdonations}</h4>
<h6>{stats[slug].xmr.numdonations} in XMR</h6>
<h6>{stats[slug].btc.numdonations} in BTC</h6>
<h6>{stats[slug].usd.numdonations} in Fiat</h6>
{stats && (
<div>
<h5>Donations</h5>
<h4>
{stats[slug].xmr.numdonations +
stats[slug].btc.numdonations +
stats[slug].usd.numdonations}
</h4>
<h6>{stats[slug].xmr.numdonations} in XMR</h6>
<h6>{stats[slug].btc.numdonations} in BTC</h6>
<h6>{stats[slug].usd.numdonations} in Fiat</h6>
<Progress text={Math.floor((stats[slug].xmr.totaldonationsinfiat + stats[slug].btc.totaldonationsinfiat + stats[slug].usd.totaldonationsinfiat)/goal * 100) } ></Progress>
</div>
<Progress
text={Math.floor(
((stats[slug].xmr.totaldonationsinfiat +
stats[slug].btc.totaldonationsinfiat +
stats[slug].usd.totaldonationsinfiat) /
goal) *
100
)}
></Progress>
</div>
}
</aside>
</article>
<div className={markdownStyles['markdown']}>
<Link href={website} legacyBehavior>
<a className='!no-underline'>
<h1>{title}</h1>
</a>
</Link>
<p>{summary}</p>
<aside className="bg-light mb-8 flex min-w-[20rem] items-center justify-between gap-4 rounded-xl p-4 lg:flex-col lg:items-start">
{!isFunded && (
<button
onClick={openPaymentModal}
className="block rounded border border-stone-800 bg-stone-800 px-4 py-2 font-semibold text-white hover:border-transparent hover:bg-orange-500 hover:text-stone-800 dark:bg-white dark:text-black dark:hover:bg-orange-500"
>
Donate
</button>
)}
</aside>
</div> */}
<p>
by{' '}
<Link href={personalWebsite} passHref legacyBehavior>
<a>{nym}</a>
</Link >
</p >
<ShareButtons project={project} />
<hr />
{content && <div dangerouslySetInnerHTML={{ __html: xss(content) }} />}
</div >
</article >
</div >
<PaymentModal
isOpen={modalOpen}
onRequestClose={closeModal}
@@ -160,34 +240,35 @@ export async function getServerSideProps({ params }: { params: any }) {
const content = await markdownToHtml(post.content || '')
let stats : any = {};
let stats: any = {}
for(let i=0;i<projects.length;i++){
let xmr;
let btc;
let usd;
for (let i = 0; i < projects.length; i++) {
let xmr
let btc
let usd
if (projects[i].isFunded) {
xmr = {
numdonations: projects[i].numdonationsxmr,
totaldonationsinfiat: projects[i].totaldonationsinfiatxmr,
totaldonations: projects[i].totaldonationsxmr,
}
btc = {
numdonations: projects[i].numdonationsbtc,
totaldonationsinfiat: projects[i].totaldonationsinfiatbtc,
totaldonations: projects[i].totaldonationsbtc,
}
usd = {numdonations: projects[i].fiatnumdonations,
totaldonationsinfiat: projects[i].fiattotaldonationsinfiat,
totaldonations: projects[i].fiattotaldonations,
xmr = {
numdonations: projects[i].numdonationsxmr,
totaldonationsinfiat: projects[i].totaldonationsinfiatxmr,
totaldonations: projects[i].totaldonationsxmr,
}
} else {
const crypto = await fetchGetJSONAuthedBTCPay(projects[i].slug)
xmr = await crypto.xmr
btc = await crypto.btc
usd = await fetchGetJSONAuthedStripe(projects[i].slug)
}
btc = {
numdonations: projects[i].numdonationsbtc,
totaldonationsinfiat: projects[i].totaldonationsinfiatbtc,
totaldonations: projects[i].totaldonationsbtc,
}
usd = {
numdonations: projects[i].fiatnumdonations,
totaldonationsinfiat: projects[i].fiattotaldonationsinfiat,
totaldonations: projects[i].fiattotaldonations,
}
} else {
const crypto = await fetchGetJSONAuthedBTCPay(projects[i].slug)
xmr = await crypto.xmr
btc = await crypto.btc
usd = await fetchGetJSONAuthedStripe(projects[i].slug)
}
stats[projects[i].slug] = { xmr, btc, usd }
}
@@ -203,5 +284,3 @@ export async function getServerSideProps({ params }: { params: any }) {
},
}
}

View File

@@ -40,7 +40,7 @@ const AllProjects: NextPage<{ projects: ProjectItem[] }> = ({ projects }) => {
{sortedProjects &&
sortedProjects.map((p, i) => (
<li key={i} className="">
<ProjectCard project={p} openPaymentModal={openPaymentModal} />
<ProjectCard project={p} />
</li>
))}
</ul>

View File

@@ -1,91 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
code {
@apply break-all whitespace-normal select-all;
}
/* When in a code block, let XMR addresses wrap, but a click or tap will select whole address */
h1,
h2 {
@apply font-mono font-semibold;
}
h1 {
@apply text-3xl md:text-4xl;
}
h2 {
@apply text-2xl;
}
h4 {
@apply text-2xl font-bold;
}
h5 {
@apply text-textgray font-mono uppercase;
}
button,
button[type='submit'] {
@apply py-2 px-4 bg-primary text-white rounded font-mono hover:shadow-glow;
}
button.secondary {
@apply bg-white text-black border border-primary;
}
a {
@apply text-textgray underline underline-offset-2 hover:no-underline;
}
.projectlist {
color: rgb(242,95,19);
@apply hover:no-underline no-underline;
}
a:hover {
color: black !important;
}
p {
@apply text-textgray;
}
p a {
@apply text-primary;
}
button.selected {
@apply bg-primary;
}
input[type='text'],
input[type='number'],
input[type='email'],
input[type='textarea'],
textarea {
@apply border border-lightgray rounded p-2 focus:border-black;
}
input[type='textarea'] {
@apply resize-y;
}
input[type='radio'] {
@apply mr-2;
}
nav li a {
@apply text-black no-underline ;
}
form h3 {
@apply font-sans font-normal;
}
button.small {
@apply p-1 text-sm bg-white text-black border border-black;
}
@@ -95,37 +7,28 @@ button.group {
}
button.pay {
@apply flex-1 min-w-[20rem] border-2 border-black bg-white p-8 text-xl text-black hover:text-primary rounded-xl flex justify-start gap-4;
@apply rounded border border-orange-500 bg-transparent font-semibold text-orange-500 hover:border-transparent hover:bg-orange-500 hover:text-white flex-1 min-w-[20rem] border-2 border-black bg-white p-8 text-xl rounded-xl flex justify-start gap-4;
}
button.pay:disabled {
@apply bg-gray-100 border-gray-400 text-gray-400 hover:shadow-none;
}
.credit a {
@apply no-underline hover:underline;
}
form.apply label {
@apply flex flex-col gap-2;
}
form.apply label.checkbox {
@apply flex flex-row items-center;
}
input[type='checkbox'] {
@apply mr-2;
}
.color-nav{
background-color: rgb(242,95,19);
.video-container {
position: relative;
width: 100%;
padding-bottom: 56.25%;
}
.custom-link{
@apply no-underline;
}
.color-me-monero{
color: rgb(242,95,19);
.video-container iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}

90
styles/tailwind.css Normal file
View File

@@ -0,0 +1,90 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.task-list-item::before {
@apply hidden;
}
.task-list-item {
@apply list-none;
}
.footnotes {
@apply mt-12 border-t border-gray-200 pt-8 dark:border-gray-700;
}
.data-footnote-backref {
@apply no-underline;
}
.csl-entry {
@apply my-5;
}
/* https://stackoverflow.com/questions/61083813/how-to-avoid-internal-autofill-selected-style-to-be-applied */
input:-webkit-autofill,
input:-webkit-autofill:focus {
transition:
background-color 600000s 0s,
color 600000s 0s;
}
@layer base {
h1 {
@apply text-2xl;
}
h2 {
@apply text-xl;
}
h3 {
@apply text-lg;
}
}
button.small {
@apply p-1 text-sm bg-white text-black border border-black;
}
button.group {
@apply p-2 text-sm bg-white text-black border border-black;
}
button.pay {
@apply flex-1 min-w-[20rem] border-2 border-black bg-white p-8 text-xl text-black rounded-xl flex justify-start gap-4;
}
button.pay:disabled {
@apply bg-gray-100 border-gray-400 text-gray-400 hover:shadow-none;
}
button,
button[type='submit'] {
@apply py-2 px-4 text-white rounded;
}
button.secondary {
@apply bg-white text-black border;
}
.prose > ul > li > p {
margin-top: 0;
margin-bottom: 0;
}
.prose small {
@apply leading-normal;
}
form label {
@apply text-sm font-semibold leading-normal;
}
.checkbox {
@apply flex items-center;
}
form input[type='text'],
form textarea {
@apply mt-2;
}

View File

@@ -1,33 +1,175 @@
const defaultTheme = require('tailwindcss/defaultTheme')
// @ts-check
const { fontFamily } = require('tailwindcss/defaultTheme')
const colors = require('tailwindcss/colors')
// ../node_modules/pliny/dist/**/*.mjs is needed for monorepo setup
/** @type {import("tailwindcss/types").Config } */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'../node_modules/pliny/**/*.{js,ts,tsx}',
'./node_modules/pliny/**/*.{js,ts,tsx}',
'./app/**/*.{js,ts,jsx,tsx}',
'./pages/**/*.{js,ts,tsx}',
'./components/**/*.{js,ts,tsx}',
'./layouts/**/*.{js,ts,tsx}',
'./lib/**/*.{js,ts,tsx}',
'./data/**/*.mdx',
],
darkMode: 'class',
theme: {
extend: {
boxShadow: {
glow: '0px 0px 4px #FF6800',
spacing: {
'9/16': '56.25%',
},
backgroundImage: {
'hero-bloom':
'radial-gradient(circle, #e95d1c 4%, #e93e1c 61%, #e91f1c 100%)',
},
colors: {
primary: '#FF6800',
subtle: '#999999',
lightgray: '#E5E5E5',
textgray: '#4F4F4F',
light: '#FAFAFA',
lineHeight: {
11: '2.75rem',
12: '3rem',
13: '3.25rem',
14: '3.5rem',
},
fontFamily: {
mono: ['Source Code Pro', ...defaultTheme.fontFamily.sans],
sans: ['Inter', ...fontFamily.sans],
},
colors: {
primary: colors.orange,
gray: colors.neutral,
},
typography: (theme) => ({
DEFAULT: {
css: {
color: theme('colors.gray.700'),
a: {
color: theme('colors.primary.500'),
'&:hover': {
color: `${theme('colors.primary.600')} !important`,
},
code: { color: theme('colors.primary.400') },
},
h1: {
fontWeight: '700',
letterSpacing: theme('letterSpacing.tight'),
color: theme('colors.gray.900'),
},
h2: {
fontWeight: '700',
letterSpacing: theme('letterSpacing.tight'),
color: theme('colors.gray.900'),
},
h3: {
fontWeight: '600',
color: theme('colors.gray.900'),
},
'h4,h5,h6': {
color: theme('colors.gray.900'),
},
pre: {
backgroundColor: theme('colors.gray.800'),
},
code: {
color: theme('colors.pink.500'),
backgroundColor: theme('colors.gray.100'),
paddingLeft: '4px',
paddingRight: '4px',
paddingTop: '2px',
paddingBottom: '2px',
borderRadius: '0.25rem',
},
'code::before': {
content: 'none',
},
'code::after': {
content: 'none',
},
details: {
backgroundColor: theme('colors.gray.100'),
paddingLeft: '4px',
paddingRight: '4px',
paddingTop: '2px',
paddingBottom: '2px',
borderRadius: '0.25rem',
},
hr: { borderColor: theme('colors.gray.200') },
'ol li::marker': {
fontWeight: '600',
color: theme('colors.gray.500'),
},
'ul li::marker': {
backgroundColor: theme('colors.gray.500'),
},
strong: { color: theme('colors.gray.600') },
blockquote: {
color: theme('colors.gray.900'),
borderLeftColor: theme('colors.gray.200'),
},
},
},
dark: {
css: {
color: theme('colors.gray.300'),
a: {
color: theme('colors.primary.500'),
'&:hover': {
color: `${theme('colors.primary.400')} !important`,
},
code: { color: theme('colors.primary.400') },
},
h1: {
fontWeight: '700',
letterSpacing: theme('letterSpacing.tight'),
color: theme('colors.gray.100'),
},
h2: {
fontWeight: '700',
letterSpacing: theme('letterSpacing.tight'),
color: theme('colors.gray.100'),
},
h3: {
fontWeight: '600',
color: theme('colors.gray.100'),
},
'h4,h5,h6': {
color: theme('colors.gray.100'),
},
pre: {
backgroundColor: theme('colors.gray.800'),
},
code: {
backgroundColor: theme('colors.gray.800'),
},
details: {
backgroundColor: theme('colors.gray.800'),
},
hr: { borderColor: theme('colors.gray.700') },
'ol li::marker': {
fontWeight: '600',
color: theme('colors.gray.400'),
},
'ul li::marker': {
backgroundColor: theme('colors.gray.400'),
},
strong: { color: theme('colors.gray.100') },
thead: {
th: {
color: theme('colors.gray.100'),
},
},
tbody: {
tr: {
borderBottomColor: theme('colors.gray.700'),
},
},
blockquote: {
color: theme('colors.gray.100'),
borderLeftColor: theme('colors.gray.700'),
},
},
},
}),
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/line-clamp'),
require('@tailwindcss/aspect-ratio'),
],
}