fix: .gitignore
@@ -1,38 +1,25 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
# build output
|
||||
dist/
|
||||
.output/
|
||||
|
||||
# dependencies
|
||||
data
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
tmp/
|
||||
node_modules/
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
# next.js
|
||||
.next/
|
||||
out/
|
||||
build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.production
|
||||
|
||||
# turbo
|
||||
.turbo
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
# misc
|
||||
*.pem
|
||||
.cache
|
||||
.astro
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"explorer.excludeGitIgnore": false,
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#95ee3a",
|
||||
"activityBar.background": "#95ee3a",
|
||||
"activityBar.foreground": "#15202b",
|
||||
"activityBar.inactiveForeground": "#15202b99",
|
||||
"activityBarBadge.background": "#3290ed",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"commandCenter.border": "#15202b99",
|
||||
"sash.hoverBorder": "#95ee3a",
|
||||
"statusBar.background": "#7ce114",
|
||||
"statusBar.foreground": "#15202b",
|
||||
"statusBarItem.hoverBackground": "#62b210",
|
||||
"statusBarItem.remoteBackground": "#7ce114",
|
||||
"statusBarItem.remoteForeground": "#15202b",
|
||||
"titleBar.activeBackground": "#7ce114",
|
||||
"titleBar.activeForeground": "#15202b",
|
||||
"titleBar.inactiveBackground": "#7ce11499",
|
||||
"titleBar.inactiveForeground": "#15202b99"
|
||||
},
|
||||
"peacock.color": "#7ce114"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { type MDXComponents } from 'mdx/types'
|
||||
|
||||
export function useMDXComponents(components: MDXComponents) {
|
||||
return {
|
||||
...components,
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
/// <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.
|
||||
@@ -1,130 +0,0 @@
|
||||
import { type Metadata } from 'next'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Container } from '@/components/Container'
|
||||
import {
|
||||
GitHubIcon,
|
||||
InstagramIcon,
|
||||
LinkedInIcon,
|
||||
MediumIcon,
|
||||
TwitterIcon,
|
||||
} from '@/components/SocialIcons'
|
||||
import portraitImage from '@/images/portrait.jpg'
|
||||
|
||||
function SocialLink({
|
||||
className,
|
||||
href,
|
||||
children,
|
||||
icon: Icon,
|
||||
}: {
|
||||
className?: string
|
||||
href: string
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<li className={clsx(className, 'flex')}>
|
||||
<Link
|
||||
href={href}
|
||||
className="group flex text-sm font-medium text-zinc-800 transition hover:text-teal-500 dark:text-zinc-200 dark:hover:text-teal-500"
|
||||
>
|
||||
<Icon className="h-6 w-6 flex-none fill-zinc-500 transition group-hover:fill-teal-500" />
|
||||
<span className="ml-4">{children}</span>
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function MailIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M6 5a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h12a3 3 0 0 0 3-3V8a3 3 0 0 0-3-3H6Zm.245 2.187a.75.75 0 0 0-.99 1.126l6.25 5.5a.75.75 0 0 0 .99 0l6.25-5.5a.75.75 0 0 0-.99-1.126L12 12.251 6.245 7.187Z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'About',
|
||||
description:
|
||||
'I\'m Prad Nukala, founder of Sonr and blockchain identity pioneer. Building the future of decentralized identity in New York City.',
|
||||
}
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<Container className="mt-16 sm:mt-32">
|
||||
<div className="grid grid-cols-1 gap-y-16 lg:grid-cols-2 lg:grid-rows-[auto_1fr] lg:gap-y-12">
|
||||
<div className="lg:pl-20">
|
||||
<div className="max-w-xs px-2.5 lg:max-w-none">
|
||||
<Image
|
||||
src={portraitImage}
|
||||
alt=""
|
||||
sizes="(min-width: 1024px) 32rem, 20rem"
|
||||
className="aspect-square rotate-3 rounded-2xl bg-zinc-100 object-cover dark:bg-zinc-800"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:order-first lg:row-span-2">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
I\'m Prad Nukala. I live in New York City, where I\'m building the future of digital identity.
|
||||
</h1>
|
||||
<div className="mt-6 space-y-7 text-base text-zinc-600 dark:text-zinc-400">
|
||||
<p>
|
||||
I\'ve been passionate about technology since the introduction of the App Store, where I made one
|
||||
of the first 5,000 apps at age 12. This early start in mobile development shaped my understanding
|
||||
of how technology can empower individuals and sparked my lifelong commitment to building tools that
|
||||
put users first.
|
||||
</p>
|
||||
<p>
|
||||
My journey in blockchain began with a simple realization: the internet\'s identity layer is fundamentally
|
||||
broken. Users have no control over their digital selves, leading to privacy breaches, data exploitation,
|
||||
and a web that serves corporations over people. This inspired me to found Sonr in 2020, where we\'re
|
||||
building an IBC-enabled blockchain specifically designed for decentralized identity.
|
||||
</p>
|
||||
<p>
|
||||
As a W3C Working Group Member for DIDs, WebAuthn, and WASM, I\'m actively shaping the standards that
|
||||
will define the future of digital identity. My technical contributions speak to my commitment - with over
|
||||
6,000 GitHub contributions from 2021-2023, I was ranked as the 4th most active contributor worldwide.
|
||||
Before Sonr, I self-developed over 10 projects that garnered more than 1 million downloads combined.
|
||||
</p>
|
||||
<p>
|
||||
Today, I lead Sonr with a clear mission: to create a peer-to-peer identity and asset management system
|
||||
that leverages DID documents, WebAuthn, and IPFS. We\'ve raised $4.7M in funding and onboarded over 120
|
||||
first-time blockchain developers to our platform. Our technology features passkey-based user accounts,
|
||||
DKLS-MPC powered wallets (no seed phrases), and lightning-fast 600ms wallet generation. I believe the
|
||||
future of the internet is one where users own their identity, control their data, and interact on their
|
||||
own terms.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:pl-20">
|
||||
<ul role="list">
|
||||
<SocialLink href="https://twitter.com/thisisprad" icon={TwitterIcon}>
|
||||
Follow on Twitter
|
||||
</SocialLink>
|
||||
<SocialLink href="https://medium.com/@prnk28" icon={MediumIcon} className="mt-4">
|
||||
Follow on Medium
|
||||
</SocialLink>
|
||||
<SocialLink href="https://github.com/prnk28" icon={GitHubIcon} className="mt-4">
|
||||
Follow on GitHub
|
||||
</SocialLink>
|
||||
<SocialLink href="https://linkedin.com/in/pradn" icon={LinkedInIcon} className="mt-4">
|
||||
Follow on LinkedIn
|
||||
</SocialLink>
|
||||
<SocialLink
|
||||
href="mailto:prad.nukala@sonr.io"
|
||||
icon={MailIcon}
|
||||
className="mt-8 border-t border-zinc-100 pt-8 dark:border-zinc-700/40"
|
||||
>
|
||||
prad.nukala@sonr.io
|
||||
</SocialLink>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
|
||||
export const article = {
|
||||
author: 'Prad Nukala',
|
||||
date: '2024-01-15',
|
||||
title: 'Building Sonr: The Journey to Decentralized Identity',
|
||||
description:
|
||||
'How we\'re creating an IBC-enabled blockchain that puts users in control of their digital identity, eliminating passwords and seed phrases while maintaining true self-sovereignty.',
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
When I founded Sonr in 2020, I had a simple but ambitious vision: create a world where people truly own their digital identity. After years of watching data breaches, privacy violations, and the centralization of user data, I knew there had to be a better way.
|
||||
|
||||
## The Problem with Digital Identity Today
|
||||
|
||||
The internet wasn\'t designed with identity in mind. We\'ve patched together solutions using usernames, passwords, and centralized databases, but these approaches are fundamentally broken:
|
||||
|
||||
- **No User Control**: Your identity lives on servers you don\'t control
|
||||
- **Privacy Violations**: Companies monetize your personal data without consent
|
||||
- **Security Nightmares**: Password breaches affect billions of users annually
|
||||
- **Platform Lock-in**: Your identity is fragmented across countless services
|
||||
|
||||
## Enter Sonr: A New Paradigm
|
||||
|
||||
Sonr is an IBC-enabled blockchain specifically designed for decentralized identity. We\'re not just building another blockchain - we\'re creating the identity layer the internet should have had from the beginning.
|
||||
|
||||
### Key Innovations
|
||||
|
||||
1. **Passkey-Based Authentication**: No more passwords. Users authenticate with biometrics or device-based keys, making phishing virtually impossible.
|
||||
|
||||
2. **No Seed Phrases**: Using DKLS-MPC (Distributed Key Lifetime Security - Multi-Party Computation), we\'ve eliminated the need for users to manage complex seed phrases while maintaining true ownership.
|
||||
|
||||
3. **Lightning-Fast Performance**: 600ms wallet generation means users can onboard as quickly as traditional web apps.
|
||||
|
||||
4. **IBC Integration**: As part of the Cosmos ecosystem, Sonr identities work seamlessly across multiple blockchains.
|
||||
|
||||
## Technical Architecture
|
||||
|
||||
At its core, Sonr leverages several cutting-edge technologies:
|
||||
|
||||
```typescript
|
||||
// Example: Creating a Sonr identity
|
||||
const identity = await Sonr.createIdentity({
|
||||
authentication: 'passkey',
|
||||
storage: 'ipfs',
|
||||
recovery: 'social'
|
||||
});
|
||||
|
||||
// The user now has a DID (Decentralized Identifier)
|
||||
console.log(identity.did); // did:sonr:abc123...
|
||||
```
|
||||
|
||||
### DID Documents
|
||||
Every Sonr user has a DID document stored on-chain that contains their public keys and service endpoints. This document is the source of truth for their identity.
|
||||
|
||||
### IPFS Integration
|
||||
User data is stored on IPFS, ensuring it\'s distributed, permanent, and under user control. Only the user\'s keys can decrypt their personal information.
|
||||
|
||||
### UCAN Tokens
|
||||
We use User Controlled Authorization Networks (UCAN) for fine-grained access control. Users can delegate specific permissions without sharing their private keys.
|
||||
|
||||
## Real-World Impact
|
||||
|
||||
Since launching, we\'ve:
|
||||
- Raised $4.7M in funding to accelerate development
|
||||
- Onboarded over 120 first-time blockchain developers
|
||||
- Created tools that make blockchain accessible to mainstream developers
|
||||
- Built partnerships across the Cosmos ecosystem
|
||||
|
||||
## The Road Ahead
|
||||
|
||||
We\'re currently preparing for our public testnet launch, with mainnet coming soon. Our goal is to onboard the next billion users to Web3 - not by forcing them to understand blockchain, but by making it invisible.
|
||||
|
||||
The future of the internet is one where:
|
||||
- Users own their identity and data
|
||||
- Privacy is default, not optional
|
||||
- Security doesn\'t sacrifice usability
|
||||
- Interoperability is built-in, not bolted-on
|
||||
|
||||
## Join the Revolution
|
||||
|
||||
If you\'re a developer interested in building on Sonr, check out our [documentation](https://docs.sonr.io) and [GitHub](https://github.com/sonr-io). We\'re always looking for contributors who share our vision of a more equitable internet.
|
||||
|
||||
The identity layer of the internet is broken, but together, we can fix it. Welcome to Sonr - where your identity truly belongs to you.
|
||||
@@ -1,131 +0,0 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
|
||||
export const article = {
|
||||
author: 'Prad Nukala',
|
||||
date: '2023-09-10',
|
||||
title: 'The Future of Digital Identity: Self-Sovereign and Interoperable',
|
||||
description:
|
||||
'Digital identity is broken. Here\'s how decentralized identifiers (DIDs), verifiable credentials, and blockchain technology will create a future where you truly own your identity.',
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
Every day, billions of people prove who they are online using systems designed in the 1960s. Usernames and passwords were meant to be temporary solutions, yet here we are, six decades later, still typing "password123" and hoping for the best.
|
||||
|
||||
As someone who\'s been building technology since creating one of the first App Store apps, and now as a W3C Working Group Member for DIDs and WebAuthn, I\'ve seen firsthand how broken our identity systems are. It\'s time for a fundamental shift.
|
||||
|
||||
## The Current Identity Crisis
|
||||
|
||||
Today\'s digital identity landscape is a disaster:
|
||||
|
||||
- **2.5 billion** records were exposed in data breaches last year
|
||||
- The average person has **100+ passwords** to manage
|
||||
- Identity theft affects **1 in 15 people** annually
|
||||
- Users have **zero control** over their personal data
|
||||
|
||||
But the real problem isn\'t just security - it\'s ownership. Your digital identity doesn\'t belong to you. It belongs to Facebook, Google, your bank, your employer, and dozens of other entities.
|
||||
|
||||
## Enter Self-Sovereign Identity
|
||||
|
||||
Self-sovereign identity (SSI) flips the script. Instead of companies owning your identity, you do. Here\'s how it works:
|
||||
|
||||
### Decentralized Identifiers (DIDs)
|
||||
A DID is like a phone number for your identity - unique, permanent, and owned by you. Unlike usernames, DIDs aren\'t controlled by any company.
|
||||
|
||||
```
|
||||
did:sonr:1234567890abcdef
|
||||
```
|
||||
|
||||
This simple string represents your entire digital identity, secured by cryptography rather than corporate databases.
|
||||
|
||||
### Verifiable Credentials
|
||||
Imagine your driver\'s license, but digital and cryptographically verifiable. That\'s a verifiable credential. Your university can issue you a degree credential, your employer can issue an employment credential, and you store them in your digital wallet.
|
||||
|
||||
The magic? You can prove things about yourself without revealing everything. Need to prove you\'re over 21? You don\'t need to share your birthdate - just a cryptographic proof that you meet the requirement.
|
||||
|
||||
## The Sonr Implementation
|
||||
|
||||
At Sonr, we\'re not just theorizing about SSI - we\'re building it. Our approach combines:
|
||||
|
||||
1. **IBC-Enabled Blockchain**: Your identity works across multiple chains, not locked to one ecosystem
|
||||
2. **IPFS Storage**: Your data is distributed and permanent, not sitting in vulnerable databases
|
||||
3. **WebAuthn Integration**: Authenticate with your fingerprint, not passwords
|
||||
4. **UCAN Authorization**: Grant specific permissions without sharing your keys
|
||||
|
||||
## Real-World Applications
|
||||
|
||||
This isn\'t just technology for technology\'s sake. Here\'s how SSI will change everyday life:
|
||||
|
||||
### Healthcare
|
||||
- Carry your medical records with you
|
||||
- Share only relevant information with providers
|
||||
- Maintain privacy while ensuring continuity of care
|
||||
|
||||
### Finance
|
||||
- Instant KYC/AML compliance without repeated paperwork
|
||||
- Prove creditworthiness without exposing financial details
|
||||
- Access global financial services with one identity
|
||||
|
||||
### Education
|
||||
- Verifiable diplomas that can\'t be faked
|
||||
- Instant credential verification for employers
|
||||
- Lifelong learning records that follow you
|
||||
|
||||
### Travel
|
||||
- Digital passports that speed up border crossings
|
||||
- Vaccination records that are instantly verifiable
|
||||
- One identity for all your travel needs
|
||||
|
||||
## The Technical Foundation
|
||||
|
||||
Building SSI requires solving complex technical challenges:
|
||||
|
||||
```typescript
|
||||
// Creating a verifiable credential
|
||||
const credential = await Sonr.createCredential({
|
||||
type: 'UniversityDegree',
|
||||
subject: userDID,
|
||||
claims: {
|
||||
degree: 'Computer Science',
|
||||
graduationDate: '2023-05-15'
|
||||
}
|
||||
});
|
||||
|
||||
// Selective disclosure - prove graduation without revealing degree
|
||||
const proof = await credential.generateProof({
|
||||
reveal: ['graduationDate']
|
||||
});
|
||||
```
|
||||
|
||||
## Privacy by Design
|
||||
|
||||
Unlike current systems where privacy is an afterthought, SSI builds privacy in from the ground up:
|
||||
|
||||
- **Minimal Disclosure**: Share only what\'s necessary
|
||||
- **User Consent**: Every data share requires explicit permission
|
||||
- **Pseudonymity**: Use different identifiers for different contexts
|
||||
- **Revocation**: Withdraw access to your data anytime
|
||||
|
||||
## The Path Forward
|
||||
|
||||
Transitioning to SSI won\'t happen overnight, but the momentum is building:
|
||||
|
||||
- W3C standards for DIDs and Verifiable Credentials are finalized
|
||||
- Governments are piloting digital identity programs
|
||||
- Major tech companies are adopting WebAuthn
|
||||
- The EU\'s eIDAS 2.0 regulation mandates digital wallets by 2024
|
||||
|
||||
## Join the Identity Revolution
|
||||
|
||||
The future of identity isn\'t about better passwords or more secure databases. It\'s about fundamentally reimagining the relationship between individuals and their digital selves.
|
||||
|
||||
At Sonr, we\'re building this future today. Whether you\'re a developer wanting to integrate SSI, an organization looking to issue credentials, or just someone tired of password resets, there\'s a place for you in this revolution.
|
||||
|
||||
The question isn\'t whether self-sovereign identity will replace current systems - it\'s how quickly we can make it happen. The technology is here. The standards are set. Now we need builders, advocates, and early adopters to make it reality.
|
||||
|
||||
Your identity belongs to you. It\'s time we built an internet that respects that.
|
||||
@@ -1,60 +0,0 @@
|
||||
import { type Metadata } from 'next'
|
||||
|
||||
import { Card } from '@/components/Card'
|
||||
import { SimpleLayout } from '@/components/SimpleLayout'
|
||||
import { type ArticleWithSlug, getAllArticles } from '@/lib/articles'
|
||||
import { formatDate } from '@/lib/formatDate'
|
||||
|
||||
function Article({ article }: { article: ArticleWithSlug }) {
|
||||
return (
|
||||
<article className="md:grid md:grid-cols-4 md:items-baseline">
|
||||
<Card className="md:col-span-3">
|
||||
<Card.Title href={`/articles/${article.slug}`}>
|
||||
{article.title}
|
||||
</Card.Title>
|
||||
<Card.Eyebrow
|
||||
as="time"
|
||||
dateTime={article.date}
|
||||
className="md:hidden"
|
||||
decorate
|
||||
>
|
||||
{formatDate(article.date)}
|
||||
</Card.Eyebrow>
|
||||
<Card.Description>{article.description}</Card.Description>
|
||||
<Card.Cta>Read article</Card.Cta>
|
||||
</Card>
|
||||
<Card.Eyebrow
|
||||
as="time"
|
||||
dateTime={article.date}
|
||||
className="mt-1 hidden md:block"
|
||||
>
|
||||
{formatDate(article.date)}
|
||||
</Card.Eyebrow>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Articles',
|
||||
description:
|
||||
'All of my long-form thoughts on programming, leadership, product design, and more, collected in chronological order.',
|
||||
}
|
||||
|
||||
export default async function ArticlesIndex() {
|
||||
let articles = await getAllArticles()
|
||||
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="Writing on software design, company building, and the blockchain industry."
|
||||
intro="All of my long-form thoughts on programming, leadership, product design, and more, collected in chronological order."
|
||||
>
|
||||
<div className="md:border-l md:border-zinc-100 md:pl-6 md:dark:border-zinc-700/40">
|
||||
<div className="flex max-w-3xl flex-col space-y-16">
|
||||
{articles.map((article) => (
|
||||
<Article key={article.slug} article={article} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</SimpleLayout>
|
||||
)
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
import { ArticleLayout } from '@/components/ArticleLayout'
|
||||
|
||||
export const article = {
|
||||
author: 'Prad Nukala',
|
||||
date: '2023-11-20',
|
||||
title: 'Why Blockchain Needs Better UX (And How We\'re Fixing It)',
|
||||
description:
|
||||
'The biggest barrier to blockchain adoption isn\'t scalability or regulation - it\'s user experience. Here\'s how we\'re making Web3 as easy as Web2.',
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: article.title,
|
||||
description: article.description,
|
||||
}
|
||||
|
||||
export default (props) => <ArticleLayout article={article} {...props} />
|
||||
|
||||
Ask any blockchain developer about the biggest challenge facing our industry, and you\'ll hear about scalability, regulation, or interoperability. But after years of building in this space and onboarding over 120 developers to blockchain, I\'ve learned the real barrier is much simpler: user experience.
|
||||
|
||||
## The UX Crisis in Web3
|
||||
|
||||
Let\'s be honest about the current state of blockchain UX:
|
||||
|
||||
- **Seed Phrases**: Asking users to write down 24 random words and never lose them is a non-starter for mainstream adoption
|
||||
- **Gas Fees**: Imagine if every click on the internet cost money and required manual approval
|
||||
- **Addresses**: 42-character hexadecimal strings are not how humans think about identity
|
||||
- **Transaction Times**: Waiting minutes for confirmations kills any semblance of modern app experience
|
||||
|
||||
These aren\'t technical limitations - they\'re design choices we\'ve accepted as "the way blockchain works."
|
||||
|
||||
## Learning from History
|
||||
|
||||
When I created one of the first 5,000 apps on the App Store at age 12, the iPhone had just made computing accessible to everyone. Not by making people understand computers, but by hiding the complexity behind intuitive interfaces.
|
||||
|
||||
Blockchain needs its iPhone moment.
|
||||
|
||||
## The Sonr Approach
|
||||
|
||||
At Sonr, we\'ve rethought blockchain UX from first principles:
|
||||
|
||||
### 1. No Seed Phrases
|
||||
Using DKLS-MPC technology, users never see or manage private keys. Account recovery uses familiar methods like email or social recovery - not a piece of paper in a safe.
|
||||
|
||||
### 2. Instant Onboarding
|
||||
Our 600ms wallet generation means users can start using blockchain apps as quickly as they\'d sign up for any web service. No downloads, no extensions, no friction.
|
||||
|
||||
### 3. Passkey Authentication
|
||||
Leveraging WebAuthn standards, users authenticate with their fingerprint or face - the same way they unlock their phones. It\'s more secure than passwords and infinitely more user-friendly.
|
||||
|
||||
### 4. Invisible Blockchain
|
||||
Users shouldn\'t need to understand consensus mechanisms any more than they need to understand TCP/IP to browse the web. Blockchain should be infrastructure, not interface.
|
||||
|
||||
## Real Code, Real Solutions
|
||||
|
||||
Here\'s how simple it is to integrate Sonr:
|
||||
|
||||
```javascript
|
||||
// Traditional Web3
|
||||
const accounts = await ethereum.request({
|
||||
method: 'eth_requestAccounts'
|
||||
});
|
||||
// User must have MetaMask installed
|
||||
// User must understand gas fees
|
||||
// User must protect seed phrase
|
||||
|
||||
// With Sonr
|
||||
const user = await Sonr.authenticate();
|
||||
// That\'s it. No wallet required.
|
||||
// No seed phrases. Just works.
|
||||
```
|
||||
|
||||
## The Broader Impact
|
||||
|
||||
Better UX isn\'t just about convenience - it\'s about accessibility and inclusion. When we make blockchain easier to use, we:
|
||||
|
||||
- **Reduce Scams**: Simpler interfaces mean fewer opportunities for user error
|
||||
- **Increase Adoption**: Lower barriers mean more users and developers
|
||||
- **Enable Innovation**: When basics are solved, builders can focus on novel applications
|
||||
|
||||
## Looking Forward
|
||||
|
||||
As a W3C Working Group Member, I\'m helping shape standards that will make these UX improvements universal. The future of blockchain isn\'t about teaching billions of people new concepts - it\'s about making those concepts invisible.
|
||||
|
||||
## The Challenge to Builders
|
||||
|
||||
If you\'re building in Web3, ask yourself:
|
||||
- Would my parents be able to use this?
|
||||
- Does it require reading documentation?
|
||||
- Are there more than 3 steps to get started?
|
||||
|
||||
If the answer to any of these is yes, we have work to do.
|
||||
|
||||
## Join the UX Revolution
|
||||
|
||||
The blockchain industry has brilliant technologists, but we need more designers, more product thinkers, and more people who put users first. If you\'re interested in making blockchain accessible to everyone, [reach out](mailto:prad.nukala@sonr.io).
|
||||
|
||||
The future of the internet depends on making it usable by everyone, not just the technically sophisticated. Let\'s build that future together.
|
||||
|
Before Width: | Height: | Size: 6.5 KiB |
@@ -1,70 +0,0 @@
|
||||
import assert from 'assert'
|
||||
import * as cheerio from 'cheerio'
|
||||
import { Feed } from 'feed'
|
||||
|
||||
export async function GET(req: Request) {
|
||||
let siteUrl = process.env.NEXT_PUBLIC_SITE_URL
|
||||
|
||||
if (!siteUrl) {
|
||||
throw Error('Missing NEXT_PUBLIC_SITE_URL environment variable')
|
||||
}
|
||||
|
||||
let author = {
|
||||
name: 'Prad Nukala',
|
||||
email: 'spencer@planetaria.tech',
|
||||
}
|
||||
|
||||
let feed = new Feed({
|
||||
title: author.name,
|
||||
description: 'Your blog description',
|
||||
author,
|
||||
id: siteUrl,
|
||||
link: siteUrl,
|
||||
image: `${siteUrl}/favicon.ico`,
|
||||
favicon: `${siteUrl}/favicon.ico`,
|
||||
copyright: `All rights reserved ${new Date().getFullYear()}`,
|
||||
feedLinks: {
|
||||
rss2: `${siteUrl}/feed.xml`,
|
||||
},
|
||||
})
|
||||
|
||||
let articleIds = require
|
||||
.context('../articles', true, /\/page\.mdx$/)
|
||||
.keys()
|
||||
.filter((key) => key.startsWith('./'))
|
||||
.map((key) => key.slice(2).replace(/\/page\.mdx$/, ''))
|
||||
|
||||
for (let id of articleIds) {
|
||||
let url = String(new URL(`/articles/${id}`, req.url))
|
||||
let html = await (await fetch(url)).text()
|
||||
let $ = cheerio.load(html)
|
||||
|
||||
let publicUrl = `${siteUrl}/articles/${id}`
|
||||
let article = $('article').first()
|
||||
let title = article.find('h1').first().text()
|
||||
let date = article.find('time').first().attr('datetime')
|
||||
let content = article.find('[data-mdx-content]').first().html()
|
||||
|
||||
assert(typeof title === 'string')
|
||||
assert(typeof date === 'string')
|
||||
assert(typeof content === 'string')
|
||||
|
||||
feed.addItem({
|
||||
title,
|
||||
id: publicUrl,
|
||||
link: publicUrl,
|
||||
content,
|
||||
author: [author],
|
||||
contributor: [author],
|
||||
date: new Date(date),
|
||||
})
|
||||
}
|
||||
|
||||
return new Response(feed.rss2(), {
|
||||
status: 200,
|
||||
headers: {
|
||||
'content-type': 'application/xml',
|
||||
'cache-control': 's-maxage=31556952',
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { type Metadata } from 'next'
|
||||
|
||||
import { Providers } from '@/app/providers'
|
||||
import { Layout } from '@/components/Layout'
|
||||
|
||||
import '@/styles/tailwind.css'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
template: '%s - Prad Nukala',
|
||||
default:
|
||||
'Prad Nukala - Founder & CEO of Sonr, Blockchain Identity Pioneer',
|
||||
},
|
||||
description:
|
||||
'I\'m Prad Nukala, founder and CEO of Sonr, an IBC-enabled blockchain for decentralized identity. W3C Working Group Member for DIDs, WebAuthn, and WASM. Building the future of self-sovereign identity and empowering users with control over their digital assets.',
|
||||
alternates: {
|
||||
types: {
|
||||
'application/rss+xml': `${process.env.NEXT_PUBLIC_SITE_URL}/feed.xml`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" className="h-full antialiased" suppressHydrationWarning>
|
||||
<body className="flex h-full bg-zinc-50 dark:bg-black">
|
||||
<Providers>
|
||||
<div className="flex w-full">
|
||||
<Layout>{children}</Layout>
|
||||
</div>
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Button } from '@/components/Button'
|
||||
import { Container } from '@/components/Container'
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<Container className="flex h-full items-center pt-16 sm:pt-32">
|
||||
<div className="flex flex-col items-center">
|
||||
<p className="text-base font-semibold text-zinc-400 dark:text-zinc-500">
|
||||
404
|
||||
</p>
|
||||
<h1 className="mt-4 text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
Page not found
|
||||
</h1>
|
||||
<p className="mt-4 text-base text-zinc-600 dark:text-zinc-400">
|
||||
Sorry, we couldn’t find the page you’re looking for.
|
||||
</p>
|
||||
<Button href="/" variant="secondary" className="mt-4">
|
||||
Go back home
|
||||
</Button>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
import Image, { type ImageProps } from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { Button } from '@/components/Button'
|
||||
import { Card } from '@/components/Card'
|
||||
import { Container } from '@/components/Container'
|
||||
import {
|
||||
GitHubIcon,
|
||||
InstagramIcon,
|
||||
LinkedInIcon,
|
||||
MediumIcon,
|
||||
TwitterIcon,
|
||||
} from '@/components/SocialIcons'
|
||||
import logoAirbnb from '@/images/logos/spacebolt.svg'
|
||||
import logoFacebook from '@/images/logos/voluntree.svg'
|
||||
import logoPlanetaria from '@/images/logos/sonr.svg'
|
||||
import logoStarbucks from '@/images/logos/superball.svg'
|
||||
import image1 from '@/images/photos/image-1.jpg'
|
||||
import image2 from '@/images/photos/image-2.jpg'
|
||||
import image3 from '@/images/photos/image-3.jpg'
|
||||
import image4 from '@/images/photos/image-4.jpg'
|
||||
import image5 from '@/images/photos/image-5.jpg'
|
||||
import { type ArticleWithSlug, getAllArticles } from '@/lib/articles'
|
||||
import { formatDate } from '@/lib/formatDate'
|
||||
|
||||
function MailIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M2.75 7.75a3 3 0 0 1 3-3h12.5a3 3 0 0 1 3 3v8.5a3 3 0 0 1-3 3H5.75a3 3 0 0 1-3-3v-8.5Z"
|
||||
className="fill-zinc-100 stroke-zinc-400 dark:fill-zinc-100/10 dark:stroke-zinc-500"
|
||||
/>
|
||||
<path
|
||||
d="m4 6 6.024 5.479a2.915 2.915 0 0 0 3.952 0L20 6"
|
||||
className="stroke-zinc-400 dark:stroke-zinc-500"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function BriefcaseIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M2.75 9.75a3 3 0 0 1 3-3h12.5a3 3 0 0 1 3 3v8.5a3 3 0 0 1-3 3H5.75a3 3 0 0 1-3-3v-8.5Z"
|
||||
className="fill-zinc-100 stroke-zinc-400 dark:fill-zinc-100/10 dark:stroke-zinc-500"
|
||||
/>
|
||||
<path
|
||||
d="M3 14.25h6.249c.484 0 .952-.002 1.316.319l.777.682a.996.996 0 0 0 1.316 0l.777-.682c.364-.32.832-.319 1.316-.319H21M8.75 6.5V4.75a2 2 0 0 1 2-2h2.5a2 2 0 0 1 2 2V6.5"
|
||||
className="stroke-zinc-400 dark:stroke-zinc-500"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function ArrowDownIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" fill="none" aria-hidden="true" {...props}>
|
||||
<path
|
||||
d="M4.75 8.75 8 12.25m0 0 3.25-3.5M8 12.25v-8.5"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function Article({ article }: { article: ArticleWithSlug }) {
|
||||
return (
|
||||
<Card as="article">
|
||||
<Card.Title href={`/articles/${article.slug}`}>
|
||||
{article.title}
|
||||
</Card.Title>
|
||||
<Card.Eyebrow as="time" dateTime={article.date} decorate>
|
||||
{formatDate(article.date)}
|
||||
</Card.Eyebrow>
|
||||
<Card.Description>{article.description}</Card.Description>
|
||||
<Card.Cta>Read article</Card.Cta>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
function SocialLink({
|
||||
icon: Icon,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Link> & {
|
||||
icon: React.ComponentType<{ className?: string }>
|
||||
}) {
|
||||
return (
|
||||
<Link className="group -m-1 p-1" {...props}>
|
||||
<Icon className="h-6 w-6 fill-zinc-500 transition group-hover:fill-zinc-600 dark:fill-zinc-400 dark:group-hover:fill-zinc-300" />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
function Newsletter() {
|
||||
return (
|
||||
<form
|
||||
action="/thank-you"
|
||||
className="rounded-2xl border border-zinc-100 p-6 dark:border-zinc-700/40"
|
||||
>
|
||||
<h2 className="flex text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<MailIcon className="h-6 w-6 flex-none" />
|
||||
<span className="ml-3">Stay up to date</span>
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
|
||||
Get notified about updates on Sonr, blockchain technology insights, and decentralized identity developments.
|
||||
</p>
|
||||
<div className="mt-6 flex">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email address"
|
||||
aria-label="Email address"
|
||||
required
|
||||
className="min-w-0 flex-auto appearance-none rounded-md border border-zinc-900/10 bg-white px-3 py-[calc(theme(spacing.2)-1px)] shadow-md shadow-zinc-800/5 placeholder:text-zinc-400 focus:border-teal-500 focus:outline-none focus:ring-4 focus:ring-teal-500/10 dark:border-zinc-700 dark:bg-zinc-700/[0.15] dark:text-zinc-200 dark:placeholder:text-zinc-500 dark:focus:border-teal-400 dark:focus:ring-teal-400/10 sm:text-sm"
|
||||
/>
|
||||
<Button type="submit" className="ml-4 flex-none">
|
||||
Join
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
interface Role {
|
||||
company: string
|
||||
title: string
|
||||
logo: ImageProps['src']
|
||||
start: string | { label: string; dateTime: string }
|
||||
end: string | { label: string; dateTime: string }
|
||||
}
|
||||
|
||||
function Role({ role }: { role: Role }) {
|
||||
let startLabel =
|
||||
typeof role.start === 'string' ? role.start : role.start.label
|
||||
let startDate =
|
||||
typeof role.start === 'string' ? role.start : role.start.dateTime
|
||||
|
||||
let endLabel = typeof role.end === 'string' ? role.end : role.end.label
|
||||
let endDate = typeof role.end === 'string' ? role.end : role.end.dateTime
|
||||
|
||||
return (
|
||||
<li className="flex gap-4">
|
||||
<div className="relative mt-1 flex h-10 w-10 flex-none items-center justify-center rounded-full shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5 dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0">
|
||||
<Image src={role.logo} alt="" className="h-7 w-7 rounded-full" unoptimized />
|
||||
</div>
|
||||
<dl className="flex flex-auto flex-wrap gap-x-2">
|
||||
<dt className="sr-only">Company</dt>
|
||||
<dd className="w-full flex-none text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{role.company}
|
||||
</dd>
|
||||
<dt className="sr-only">Role</dt>
|
||||
<dd className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{role.title}
|
||||
</dd>
|
||||
<dt className="sr-only">Date</dt>
|
||||
<dd
|
||||
className="ml-auto text-xs text-zinc-400 dark:text-zinc-500"
|
||||
aria-label={`${startLabel} until ${endLabel}`}
|
||||
>
|
||||
<time dateTime={startDate}>{startLabel}</time>{' '}
|
||||
<span aria-hidden="true">—</span>{' '}
|
||||
<time dateTime={endDate}>{endLabel}</time>
|
||||
</dd>
|
||||
</dl>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function Resume() {
|
||||
let resume: Array<Role> = [
|
||||
{
|
||||
company: 'Sonr',
|
||||
title: 'Founder & CEO',
|
||||
logo: logoPlanetaria,
|
||||
start: '2020',
|
||||
end: {
|
||||
label: 'Present',
|
||||
dateTime: new Date().getFullYear().toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
company: 'W3C Working Group',
|
||||
title: 'Member - DIDs, WebAuthn, WASM',
|
||||
logo: logoAirbnb,
|
||||
start: '2021',
|
||||
end: {
|
||||
label: 'Present',
|
||||
dateTime: new Date().getFullYear().toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
company: 'Open Source',
|
||||
title: '4th Most GitHub Contributions Worldwide',
|
||||
logo: logoFacebook,
|
||||
start: '2021',
|
||||
end: '2023',
|
||||
},
|
||||
{
|
||||
company: 'Early iOS Developer',
|
||||
title: 'One of the first 5,000 App Store apps',
|
||||
logo: logoStarbucks,
|
||||
start: '2008',
|
||||
end: '2012',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border border-zinc-100 p-6 dark:border-zinc-700/40">
|
||||
<h2 className="flex text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
||||
<BriefcaseIcon className="h-6 w-6 flex-none" />
|
||||
<span className="ml-3">Experience</span>
|
||||
</h2>
|
||||
<ol className="mt-6 space-y-4">
|
||||
{resume.map((role, roleIndex) => (
|
||||
<Role key={roleIndex} role={role} />
|
||||
))}
|
||||
</ol>
|
||||
<Button href="#" variant="secondary" className="group mt-6 w-full">
|
||||
Download CV
|
||||
<ArrowDownIcon className="h-4 w-4 stroke-zinc-400 transition group-active:stroke-zinc-600 dark:group-hover:stroke-zinc-50 dark:group-active:stroke-zinc-50" />
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Photos() {
|
||||
let rotations = ['rotate-2', '-rotate-2', 'rotate-2', 'rotate-2', '-rotate-2']
|
||||
|
||||
return (
|
||||
<div className="mt-16 sm:mt-20">
|
||||
<div className="-my-4 flex justify-center gap-5 overflow-hidden py-4 sm:gap-8">
|
||||
{[image1, image2, image3, image4, image5].map((image, imageIndex) => (
|
||||
<div
|
||||
key={image.src}
|
||||
className={clsx(
|
||||
'relative aspect-[9/10] w-44 flex-none overflow-hidden rounded-xl bg-zinc-100 dark:bg-zinc-800 sm:w-72 sm:rounded-2xl',
|
||||
rotations[imageIndex % rotations.length],
|
||||
)}
|
||||
>
|
||||
<Image
|
||||
src={image}
|
||||
alt=""
|
||||
sizes="(min-width: 640px) 18rem, 11rem"
|
||||
className="absolute inset-0 h-full w-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default async function Home() {
|
||||
let articles = (await getAllArticles()).slice(0, 4)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container className="mt-9">
|
||||
<div className="max-w-2xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl">
|
||||
Blockchain pioneer, founder, and decentralized identity architect.
|
||||
</h1>
|
||||
<p className="mt-6 text-base text-zinc-600 dark:text-zinc-400">
|
||||
I\'m Prad Nukala, founder and CEO of Sonr, an IBC-enabled blockchain for decentralized identity.
|
||||
As a W3C Working Group Member for DIDs, WebAuthn, and WASM, I\'m building the future where users
|
||||
control their digital identity and assets. With over 6000+ GitHub contributions, I\'m passionate
|
||||
about making blockchain technology accessible to everyone.
|
||||
</p>
|
||||
<div className="mt-6 flex gap-6">
|
||||
<SocialLink
|
||||
href="https://twitter.com/thisisprad"
|
||||
aria-label="Follow on Twitter"
|
||||
icon={TwitterIcon}
|
||||
/>
|
||||
<SocialLink
|
||||
href="https://medium.com/@prnk28"
|
||||
aria-label="Follow on Medium"
|
||||
icon={MediumIcon}
|
||||
/>
|
||||
<SocialLink
|
||||
href="https://github.com/prnk28"
|
||||
aria-label="Follow on GitHub"
|
||||
icon={GitHubIcon}
|
||||
/>
|
||||
<SocialLink
|
||||
href="https://linkedin.com/in/pradn"
|
||||
aria-label="Follow on LinkedIn"
|
||||
icon={LinkedInIcon}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
<Photos />
|
||||
<Container className="mt-24 md:mt-28">
|
||||
<div className="mx-auto grid max-w-xl grid-cols-1 gap-y-20 lg:max-w-none lg:grid-cols-2">
|
||||
<div className="flex flex-col gap-16">
|
||||
{articles.map((article) => (
|
||||
<Article key={article.slug} article={article} />
|
||||
))}
|
||||
</div>
|
||||
<div className="space-y-10 lg:pl-16 xl:pl-24">
|
||||
<Newsletter />
|
||||
<Resume />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
import { type Metadata } from 'next'
|
||||
import Image from 'next/image'
|
||||
|
||||
import { Card } from '@/components/Card'
|
||||
import { SimpleLayout } from '@/components/SimpleLayout'
|
||||
import logoSuperball from '@/images/logos/superball.svg'
|
||||
import logoVoluntree from '@/images/logos/voluntree.svg'
|
||||
import logoSpacebolt from '@/images/logos/spacebolt.svg'
|
||||
import logoYue from '@/images/logos/yue.svg'
|
||||
import logoSonr from '@/images/logos/sonr.svg'
|
||||
|
||||
const projects = [
|
||||
{
|
||||
name: 'Sonr',
|
||||
created: '2020 - Present',
|
||||
description:
|
||||
'IBC-enabled blockchain for decentralized identity. Features passkey-based accounts, DKLS-MPC wallets, and 600ms wallet generation.',
|
||||
link: { href: 'https://sonr.io', label: 'sonr.io' },
|
||||
logo: logoSonr,
|
||||
},
|
||||
{
|
||||
name: 'DID & WebAuthn Standards',
|
||||
created: '2021 - Present',
|
||||
description:
|
||||
'Contributing to W3C Working Groups for DIDs, WebAuthn, and WASM standards that define the future of digital identity.',
|
||||
link: { href: 'https://www.w3.org/TR/did-core/', label: 'w3.org' },
|
||||
logo: logoYue,
|
||||
},
|
||||
// {
|
||||
// name: 'ML Livestock Detection',
|
||||
// description:
|
||||
// 'Real-time video streaming library, optimized for interstellar transmission.',
|
||||
// link: { href: '#', label: 'github.com' },
|
||||
// logo: logoHelioStream,
|
||||
// },
|
||||
// {
|
||||
// name: 'AR Whiteboard',
|
||||
// description:
|
||||
// 'The operating system that powers our Sonr space shuttles.',
|
||||
// link: { href: '#', label: 'github.com' },
|
||||
// logo: logoCosmos,
|
||||
// },
|
||||
{
|
||||
name: 'Spacebolt',
|
||||
created: '2017 - 2018',
|
||||
description:
|
||||
'Unity-based mobile game featuring innovative physics-based gameplay. Part of my journey in mobile development.',
|
||||
link: { href: 'https://github.com/prnk28', label: 'github.com' },
|
||||
logo: logoSpacebolt,
|
||||
},
|
||||
{
|
||||
name: 'VolunTree',
|
||||
created: '2015 - 2016',
|
||||
description:
|
||||
'iOS app for tracking volunteer hours and connecting volunteers with organizations. Simplified community service engagement.',
|
||||
link: { href: 'https://github.com/prnk28', label: 'github.com' },
|
||||
logo: logoVoluntree,
|
||||
},
|
||||
{
|
||||
name: 'Superball',
|
||||
created: '2010 - 2011',
|
||||
description:
|
||||
'One of the first 10,000 apps published on the App Store. A physics-based game that started my journey at age 12.',
|
||||
link: { href: 'https://github.com/prnk28', label: 'github.com' },
|
||||
logo: logoSuperball,
|
||||
},
|
||||
]
|
||||
|
||||
function LinkIcon(props: React.ComponentPropsWithoutRef<'svg'>) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" {...props}>
|
||||
<path
|
||||
d="M15.712 11.823a.75.75 0 1 0 1.06 1.06l-1.06-1.06Zm-4.95 1.768a.75.75 0 0 0 1.06-1.06l-1.06 1.06Zm-2.475-1.414a.75.75 0 1 0-1.06-1.06l1.06 1.06Zm4.95-1.768a.75.75 0 1 0-1.06 1.06l1.06-1.06Zm3.359.53-.884.884 1.06 1.06.885-.883-1.061-1.06Zm-4.95-2.12 1.414-1.415L12 6.344l-1.415 1.413 1.061 1.061Zm0 3.535a2.5 2.5 0 0 1 0-3.536l-1.06-1.06a4 4 0 0 0 0 5.656l1.06-1.06Zm4.95-4.95a2.5 2.5 0 0 1 0 3.535L17.656 12a4 4 0 0 0 0-5.657l-1.06 1.06Zm1.06-1.06a4 4 0 0 0-5.656 0l1.06 1.06a2.5 2.5 0 0 1 3.536 0l1.06-1.06Zm-7.07 7.07.176.177 1.06-1.06-.176-.177-1.06 1.06Zm-3.183-.353.884-.884-1.06-1.06-.884.883 1.06 1.06Zm4.95 2.121-1.414 1.414 1.06 1.06 1.415-1.413-1.06-1.061Zm0-3.536a2.5 2.5 0 0 1 0 3.536l1.06 1.06a4 4 0 0 0 0-5.656l-1.06 1.06Zm-4.95 4.95a2.5 2.5 0 0 1 0-3.535L6.344 12a4 4 0 0 0 0 5.656l1.06-1.06Zm-1.06 1.06a4 4 0 0 0 5.657 0l-1.061-1.06a2.5 2.5 0 0 1-3.535 0l-1.061 1.06Zm7.07-7.07-.176-.177-1.06 1.06.176.178 1.06-1.061Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Projects',
|
||||
description: 'Blockchain innovations and decentralized identity solutions I\'ve built to empower users.',
|
||||
}
|
||||
|
||||
export default function Projects() {
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="Building the infrastructure for a decentralized future."
|
||||
intro="From creating one of the first App Store apps at age 12 to founding Sonr, I\\'ve developed over 10 projects with 1M+ downloads. These represent my journey from mobile development to blockchain innovation, all focused on empowering users with better technology."
|
||||
>
|
||||
<ul
|
||||
role="list"
|
||||
className="grid grid-cols-1 gap-x-12 gap-y-16 sm:grid-cols-2 lg:grid-cols-3"
|
||||
>
|
||||
{projects.map((project) => (
|
||||
<Card as="li" key={project.name}>
|
||||
<div className="relative z-10 flex h-12 w-12 items-center justify-center rounded-full bg-white shadow-md shadow-zinc-800/5 ring-1 ring-zinc-900/5 dark:border dark:border-zinc-700/50 dark:bg-zinc-800 dark:ring-0">
|
||||
<Image
|
||||
src={project.logo}
|
||||
alt=""
|
||||
className="h-8 w-8 rounded-full"
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
<h2 className="mt-6 text-base font-semibold text-zinc-800 dark:text-zinc-100">
|
||||
<Card.Link href={project.link.href}>{project.name}</Card.Link>
|
||||
</h2>
|
||||
<Card.Description>{project.description}</Card.Description>
|
||||
<p className="relative z-10 mt-6 flex text-sm font-medium text-zinc-400 transition group-hover:text-teal-500 dark:text-zinc-200">
|
||||
<LinkIcon className="h-6 w-6 flex-none" />
|
||||
<span className="ml-2">{project.link.label}</span>
|
||||
</p>
|
||||
</Card>
|
||||
))}
|
||||
</ul>
|
||||
</SimpleLayout>
|
||||
)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useEffect, useRef } from 'react'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { ThemeProvider, useTheme } from 'next-themes'
|
||||
|
||||
function usePrevious<T>(value: T) {
|
||||
let ref = useRef<T>()
|
||||
|
||||
useEffect(() => {
|
||||
ref.current = value
|
||||
}, [value])
|
||||
|
||||
return ref.current
|
||||
}
|
||||
|
||||
function ThemeWatcher() {
|
||||
let { resolvedTheme, setTheme } = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
let media = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
|
||||
function onMediaChange() {
|
||||
let systemTheme = media.matches ? 'dark' : 'light'
|
||||
if (resolvedTheme === systemTheme) {
|
||||
setTheme('system')
|
||||
}
|
||||
}
|
||||
|
||||
onMediaChange()
|
||||
media.addEventListener('change', onMediaChange)
|
||||
|
||||
return () => {
|
||||
media.removeEventListener('change', onMediaChange)
|
||||
}
|
||||
}, [resolvedTheme, setTheme])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const AppContext = createContext<{ previousPathname?: string }>({})
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
let pathname = usePathname()
|
||||
let previousPathname = usePrevious(pathname)
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={{ previousPathname }}>
|
||||
<ThemeProvider attribute="class" disableTransitionOnChange>
|
||||
<ThemeWatcher />
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</AppContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
import { type Metadata } from 'next'
|
||||
|
||||
import { Card } from '@/components/Card'
|
||||
import { Section } from '@/components/Section'
|
||||
import { SimpleLayout } from '@/components/SimpleLayout'
|
||||
|
||||
function SpeakingSection({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Section>) {
|
||||
return (
|
||||
<Section {...props}>
|
||||
<div className="space-y-16">{children}</div>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
function Appearance({
|
||||
title,
|
||||
description,
|
||||
event,
|
||||
cta,
|
||||
href,
|
||||
}: {
|
||||
title: string
|
||||
description: string
|
||||
event: string
|
||||
cta: string
|
||||
href: string
|
||||
}) {
|
||||
return (
|
||||
<Card as="article">
|
||||
<Card.Title as="h3" href={href}>
|
||||
{title}
|
||||
</Card.Title>
|
||||
<Card.Eyebrow decorate>{event}</Card.Eyebrow>
|
||||
<Card.Description>{description}</Card.Description>
|
||||
<Card.Cta>{cta}</Card.Cta>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Speaking',
|
||||
description:
|
||||
'Speaking at prestigious institutions and conferences about blockchain, decentralized identity, and the future of the web.',
|
||||
}
|
||||
|
||||
export default function Speaking() {
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="Sharing the vision of decentralized identity with the world."
|
||||
intro="From guest lecturing at MIT alongside Vitalik Buterin to speaking at blockchain conferences, I\'m passionate about educating developers and entrepreneurs about the future of digital identity and how blockchain technology can empower users."
|
||||
>
|
||||
<div className="space-y-20">
|
||||
<SpeakingSection title="Guest Lectures & Conferences">
|
||||
<Appearance
|
||||
href="#"
|
||||
title="The Future of Decentralized Identity"
|
||||
description="Guest lecture at MIT Sloan School alongside Vitalik Buterin, Alex Mashinsky, Anthony Pompliano, and Yat Siu on blockchain innovation and decentralized identity."
|
||||
event="MIT Sloan School of Management, 2023"
|
||||
cta="Learn more"
|
||||
/>
|
||||
<Appearance
|
||||
href="#"
|
||||
title="Building IBC-Enabled Blockchains for Identity"
|
||||
description="Technical deep-dive into Sonr\'s architecture, showcasing how we leverage IBC for cross-chain identity management and our innovative approach to wallet generation."
|
||||
event="Cosmos Ecosystem Conference, 2023"
|
||||
cta="Watch video"
|
||||
/>
|
||||
<Appearance
|
||||
href="#"
|
||||
title="WebAuthn and the Future of Authentication"
|
||||
description="Presenting our work with W3C standards for DIDs and WebAuthn, demonstrating how passkey-based authentication can replace traditional passwords."
|
||||
event="W3C Workshop on Web Authentication, 2022"
|
||||
cta="View presentation"
|
||||
/>
|
||||
</SpeakingSection>
|
||||
<SpeakingSection title="Podcasts & Interviews">
|
||||
<Appearance
|
||||
href="#"
|
||||
title="From App Store Pioneer to Blockchain Innovator"
|
||||
description="My journey from creating one of the first 5,000 iOS apps at age 12 to founding Sonr and becoming the 4th most active GitHub contributor worldwide."
|
||||
event="The Blockchain Founders Podcast, 2023"
|
||||
cta="Listen to podcast"
|
||||
/>
|
||||
<Appearance
|
||||
href="#"
|
||||
title="Raising $4.7M to Build Decentralized Identity"
|
||||
description="How we raised funding for Sonr, onboarded 120+ first-time blockchain developers, and our vision for making blockchain accessible to everyone."
|
||||
event="Web3 Startup Stories, 2022"
|
||||
cta="Listen to podcast"
|
||||
/>
|
||||
<Appearance
|
||||
href="#"
|
||||
title="The Technical Architecture of Self-Sovereign Identity"
|
||||
description="Deep technical discussion on DID documents, IPFS integration, UCAN tokens, and our approach to eliminating seed phrases with DKLS-MPC wallets."
|
||||
event="Decentralized Tech Talks, 2022"
|
||||
cta="Listen to podcast"
|
||||
/>
|
||||
</SpeakingSection>
|
||||
</div>
|
||||
</SimpleLayout>
|
||||
)
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { type Metadata } from 'next'
|
||||
|
||||
import { SimpleLayout } from '@/components/SimpleLayout'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'You\'re subscribed',
|
||||
description: 'Thanks for subscribing to my newsletter.',
|
||||
}
|
||||
|
||||
export default function ThankYou() {
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="Thanks for subscribing."
|
||||
intro="I\'ll send you an email any time I publish a new blog post, release a new project, or have anything interesting to share that I think you\'d want to hear about. You can unsubscribe at any time, no hard feelings."
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
import { Card } from '@/components/Card'
|
||||
import { Section } from '@/components/Section'
|
||||
import { SimpleLayout } from '@/components/SimpleLayout'
|
||||
|
||||
function ToolsSection({
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentPropsWithoutRef<typeof Section>) {
|
||||
return (
|
||||
<Section {...props}>
|
||||
<ul role="list" className="space-y-16">
|
||||
{children}
|
||||
</ul>
|
||||
</Section>
|
||||
)
|
||||
}
|
||||
|
||||
function Tool({
|
||||
title,
|
||||
href,
|
||||
children,
|
||||
}: {
|
||||
title: string
|
||||
href?: string
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<Card as="li">
|
||||
<Card.Title as="h3" href={href}>
|
||||
{title}
|
||||
</Card.Title>
|
||||
<Card.Description>{children}</Card.Description>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: 'Uses',
|
||||
description: 'Tools and technologies I use for blockchain development, building Sonr, and staying productive.',
|
||||
}
|
||||
|
||||
export default function Uses() {
|
||||
return (
|
||||
<SimpleLayout
|
||||
title="Tools for building the future of decentralized identity."
|
||||
intro="From blockchain development to managing a startup, here are the tools and technologies that power my work at Sonr and help me maintain my position as one of the world\'s most active open-source contributors."
|
||||
>
|
||||
<div className="space-y-20">
|
||||
<ToolsSection title="Workstation">
|
||||
<Tool title='16" MacBook Pro, M2 Max, 96GB RAM'>
|
||||
When you\'re compiling blockchain nodes and running multiple test networks locally,
|
||||
RAM is everything. This machine handles everything I throw at it, from Rust compilation
|
||||
to running full Cosmos SDK nodes.
|
||||
</Tool>
|
||||
<Tool title="LG UltraFine 5K Display (x2)">
|
||||
Dual 5K displays give me the screen real estate I need for coding, monitoring
|
||||
blockchain metrics, and keeping up with the constant stream of GitHub notifications
|
||||
from maintaining one of the world\'s most active contribution streaks.
|
||||
</Tool>
|
||||
<Tool title="Keychron Q1 with Gateron Brown Switches">
|
||||
Mechanical keyboards aren\'t just for gaming. The tactile feedback helps me maintain
|
||||
accuracy during long coding sessions, crucial when every character matters in smart contracts.
|
||||
</Tool>
|
||||
<Tool title="Herman Miller Embody Chair">
|
||||
After years of 12+ hour coding days, investing in proper ergonomics isn\'t optional.
|
||||
This chair has been perfect for those long sessions building Sonr\'s infrastructure.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
<ToolsSection title="Development Tools">
|
||||
<Tool title="VS Code with Rust Analyzer">
|
||||
Essential for Cosmos SDK development. The Rust analyzer extension is particularly
|
||||
crucial for blockchain development where type safety can prevent costly errors.
|
||||
</Tool>
|
||||
<Tool title="Warp Terminal">
|
||||
The AI-powered terminal that understands context. Perfect for managing multiple
|
||||
blockchain nodes, SSH sessions, and the complex command sequences involved in
|
||||
Cosmos SDK development.
|
||||
</Tool>
|
||||
<Tool title="Docker & Kubernetes">
|
||||
Running local testnets and managing blockchain infrastructure requires serious
|
||||
containerization. Docker for local development, K8s for production deployments.
|
||||
</Tool>
|
||||
<Tool title="Lens IDE">
|
||||
The best Kubernetes IDE I\'ve found. Essential for managing Sonr\'s infrastructure
|
||||
across multiple environments and monitoring our validator nodes.
|
||||
</Tool>
|
||||
<Tool title="Tendermint/CometBFT Tools">
|
||||
The entire Cosmos SDK toolkit is essential for building IBC-enabled chains.
|
||||
From Ignite CLI to CosmWasm, these tools form the backbone of Sonr\'s development.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
<ToolsSection title="Blockchain & Web3 Tools">
|
||||
<Tool title="Keplr Wallet">
|
||||
The premier wallet for Cosmos ecosystem development. Essential for testing
|
||||
IBC transactions and interacting with Sonr\'s testnet.
|
||||
</Tool>
|
||||
<Tool title="Remix IDE">
|
||||
While Sonr uses Cosmos SDK, understanding EVM is crucial. Remix helps me
|
||||
stay current with Ethereum development and test cross-chain scenarios.
|
||||
</Tool>
|
||||
<Tool title="IPFS Desktop">
|
||||
Since Sonr integrates IPFS for distributed storage, having a local IPFS
|
||||
node is essential for development and testing.
|
||||
</Tool>
|
||||
<Tool title="Postman">
|
||||
API testing is crucial when building blockchain infrastructure. Postman
|
||||
helps me test our REST and gRPC endpoints thoroughly.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
<ToolsSection title="Design & Documentation">
|
||||
<Tool title="Figma">
|
||||
Designing intuitive blockchain UX requires constant iteration. Figma helps
|
||||
us prototype wallet interfaces and visualize complex identity flows.
|
||||
</Tool>
|
||||
<Tool title="Excalidraw">
|
||||
Perfect for sketching blockchain architectures and explaining complex
|
||||
technical concepts. Most of our technical documentation starts here.
|
||||
</Tool>
|
||||
<Tool title="Notion">
|
||||
Our entire company knowledge base lives in Notion. From technical specs
|
||||
to meeting notes, it\'s our single source of truth.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
<ToolsSection title="Productivity & Communication">
|
||||
<Tool title="Raycast">
|
||||
More than just an app launcher - it\'s my command center. Custom scripts
|
||||
for blockchain operations, quick access to documentation, and AI features
|
||||
for code generation.
|
||||
</Tool>
|
||||
<Tool title="Linear">
|
||||
Issue tracking designed for modern software teams. Perfect for managing
|
||||
Sonr\'s development across multiple repositories and coordinating with our
|
||||
distributed team.
|
||||
</Tool>
|
||||
<Tool title="Cal.com">
|
||||
Open-source scheduling that respects privacy. Important when you\'re building
|
||||
privacy-first technology and need to practice what you preach.
|
||||
</Tool>
|
||||
<Tool title="Discord">
|
||||
Where the blockchain community lives. Essential for staying connected with
|
||||
the Cosmos ecosystem and supporting our developer community.
|
||||
</Tool>
|
||||
<Tool title="GitHub Copilot">
|
||||
AI pair programming at its finest. Particularly helpful when working with
|
||||
new Cosmos SDK modules or implementing complex cryptographic functions.
|
||||
</Tool>
|
||||
</ToolsSection>
|
||||
</div>
|
||||
</SimpleLayout>
|
||||
)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 839 KiB |
@@ -1,13 +0,0 @@
|
||||
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="28" height="28" rx="14" fill="#FF5A5F" />
|
||||
<g clip-path="url(#a)">
|
||||
<path
|
||||
d="M14.001 18.183c-.902-1.131-1.432-2.122-1.609-2.971-.175-.685-.106-1.232.194-1.643.318-.474.792-.704 1.414-.704.622 0 1.096.23 1.414.708.297.407.372.955.19 1.644-.194.865-.723 1.856-1.608 2.972l.005-.006Zm6.4.76c-.123.831-.689 1.52-1.466 1.856-1.502.653-2.989-.389-4.261-1.803 2.104-2.634 2.493-4.685 1.59-6.012-.53-.76-1.289-1.13-2.263-1.13-1.963 0-3.042 1.66-2.618 3.588.247 1.043.901 2.229 1.945 3.555-.654.723-1.274 1.237-1.822 1.555-.424.23-.83.372-1.218.406-1.786.266-3.186-1.467-2.55-3.253.088-.23.263-.654.563-1.308l.017-.035c.976-2.119 2.161-4.527 3.523-7.197l.035-.088.387-.744c.3-.548.423-.793.9-1.095.231-.14.514-.21.831-.21.636 0 1.132.372 1.344.671.106.16.23.372.388.636l.372.726.054.106c1.36 2.669 2.547 5.072 3.52 7.196l.016.016.356.814.212.509c.162.409.196.815.142 1.239l.004.002Zm.814-1.593c-.124-.389-.337-.847-.6-1.396v-.02a235.28 235.28 0 0 0-3.538-7.23l-.074-.108C16.212 6.974 15.646 6 14.001 6c-1.627 0-2.317 1.13-3.023 2.599l-.054.106a221.31 221.31 0 0 0-3.536 7.232v.035l-.372.813c-.14.336-.212.512-.23.565-.9 2.477.955 4.65 3.201 4.65.018 0 .088 0 .177-.018h.248c1.166-.142 2.37-.883 3.59-2.211 1.218 1.326 2.422 2.069 3.587 2.211h.248c.089.018.16.018.177.018 2.246.002 4.101-2.174 3.201-4.65Z"
|
||||
fill="#fff" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" transform="translate(6 6)" d="M0 0h16v16H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,12 +0,0 @@
|
||||
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="16" cy="16" r="16" fill="url(#a)"/>
|
||||
<rect x="16" y="8" width="8" height="8" rx="2" fill="#fff" fill-opacity=".4"/>
|
||||
<rect x="12" y="12" width="8" height="8" rx="2" fill="#fff" fill-opacity=".5"/>
|
||||
<rect x="8" y="16" width="8" height="8" rx="2" fill="#fff"/>
|
||||
<defs>
|
||||
<radialGradient id="a" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 18.5 -18.5 0 16 13.5)">
|
||||
<stop offset=".169" stop-color="#22D3EE"/>
|
||||
<stop offset="1" stop-color="#7451FF"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 625 B |
@@ -1,25 +0,0 @@
|
||||
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#a)">
|
||||
<rect width="32" height="32" rx="16" fill="#001120"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 13a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1h-2Zm-3 1a3 3 0 0 1 3-3h2a3 3 0 0 1 3 3v4a3 3 0 0 1-3 3h-2a3 3 0 0 1-3-3v-4Zm10-.257A2.743 2.743 0 0 1 19.743 11H22a3 3 0 0 1 3 3 1 1 0 1 1-2 0 1 1 0 0 0-1-1h-2.257a.743.743 0 0 0-.235 1.449l3.616 1.205A2.743 2.743 0 0 1 22.257 21H20a3 3 0 0 1-3-3 1 1 0 1 1 2 0 1 1 0 0 0 1 1h2.257a.743.743 0 0 0 .235-1.449l-3.616-1.205A2.743 2.743 0 0 1 17 13.743Z" fill="url(#b)"/>
|
||||
<path fill="#fff" fill-opacity=".1" d="M0 23h32v1H0z"/>
|
||||
<path fill="#fff" fill-opacity=".1" d="M5 0v32H4V0z"/>
|
||||
<path fill="#fff" fill-opacity=".1" d="M0 8h32v1H0z"/>
|
||||
<path fill="url(#c)" d="M0 23h32v1H0z"/>
|
||||
<path fill="#fff" fill-opacity=".1" d="M28 0v32h-1V0z"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="b" x1="11" y1="12" x2="11" y2="20" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#fff"/>
|
||||
<stop offset="1" stop-color="#E3E8ED"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c" x1="1.5" y1="23.5" x2="30.5" y2="23.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#fff" stop-opacity="0"/>
|
||||
<stop offset=".486" stop-color="#fff"/>
|
||||
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<clipPath id="a">
|
||||
<rect width="32" height="32" rx="16" fill="#fff"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,18 +0,0 @@
|
||||
<svg width="28" height="28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#a)">
|
||||
<rect width="28" height="28" rx="14" fill="#fff" />
|
||||
<g clip-path="url(#b)">
|
||||
<path
|
||||
d="M29 14.09c0-8.283-6.716-15-15-15-8.284 0-15 6.717-15 15 0 7.488 5.485 13.693 12.656 14.818v-10.48H7.847V14.09h3.81v-3.303c0-3.759 2.24-5.836 5.666-5.836 1.64 0 3.357.294 3.357.294v3.69h-1.892c-1.864 0-2.445 1.157-2.445 2.343v2.813h4.16l-.666 4.337h-3.494V28.91C23.515 27.783 29 21.577 29 14.09Z"
|
||||
fill="#1877F2" />
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<rect width="28" height="28" rx="14" fill="#fff" />
|
||||
</clipPath>
|
||||
<clipPath id="b">
|
||||
<path fill="#fff" transform="translate(-1 -1)" d="M0 0h30v30H0z" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 781 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" rx="16" fill="#F43F5E" />
|
||||
<path d="M9 11a2 2 0 0 1 2-2h2v12a2 2 0 0 1-2 2H9V11ZM19 11a2 2 0 0 1 2-2h2v12a2 2 0 0 1-2 2h-2V11Z" fill="#fff" />
|
||||
<path d="M15.447 16.106A2 2 0 0 1 17.237 15H21v2h-6l.447-.894Z" fill="#fff" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 339 B |
@@ -1,21 +0,0 @@
|
||||
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#a)">
|
||||
<rect width="32" height="32" rx="16" fill="#0085FF" />
|
||||
<path fill="#fff" fill-opacity=".2" d="M0 26h32v1H0z" />
|
||||
<path fill="#fff" fill-opacity=".2" d="M8 0v32H7V0zM16.5 0v32h-1V0z" />
|
||||
<path fill="#fff" fill-opacity=".2" d="M0 5h32v1H0zM0 15.5h32v1H0z" />
|
||||
<path fill="#fff" fill-opacity=".2" d="M25 0v32h-1V0z" />
|
||||
<path
|
||||
d="M13 21v-5.485c0-2.959-.228-4.866 1.24-7.435l.892-1.56a1 1 0 0 1 1.736 0l.892 1.56C19.228 10.65 19 12.556 19 15.515V21l3.293 3.293c.63.63.184 1.707-.707 1.707H10.414c-.89 0-1.337-1.077-.707-1.707L13 21Z"
|
||||
fill="#fff" />
|
||||
<path
|
||||
d="M13 26v-5m0 5h6m-6 0h-2.586c-.89 0-1.337-1.077-.707-1.707L13 21m0 0v-5.485c0-2.959-.228-4.866 1.24-7.435l.892-1.56a1 1 0 0 1 1.736 0l.892 1.56C19.228 10.65 19 12.556 19 15.515V21m0 5h2.586c.89 0 1.337-1.077.707-1.707L19 21m0 5v-5"
|
||||
stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M16 13v2" stroke="#0085FF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<rect width="32" height="32" rx="16" fill="#fff" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,24 +0,0 @@
|
||||
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#a)">
|
||||
<rect width="32" height="32" rx="16" fill="url(#b)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.755 30.136c.051-4.308.484-8.167 1.147-10.985.355-1.509.766-2.677 1.196-3.45.466-.838.801-.951.902-.951.1 0 .436.113.902.95.43.774.841 1.942 1.196 3.451.663 2.818 1.096 6.677 1.147 10.985.508-.117 1.007-.26 1.494-.428-.074-4.224-.508-8.042-1.18-10.9-.327-1.389-.718-2.584-1.169-3.497 1.498.715 2.887 2.097 4.035 4.048 1.257 2.138 2.179 4.894 2.589 8 .47-.374.916-.775 1.335-1.203-.488-2.878-1.402-5.467-2.631-7.557-.652-1.107-1.402-2.09-2.236-2.901a16.296 16.296 0 0 1 7.851 6.01c.236-.55.439-1.117.606-1.699C26.688 15.892 21.653 13.25 16 13.25c-5.653 0-10.688 2.642-13.939 6.759.167.582.37 1.15.606 1.7a16.295 16.295 0 0 1 7.85-6.011c-.833.811-1.583 1.794-2.235 2.9-1.23 2.091-2.143 4.68-2.631 7.558.42.428.866.83 1.335 1.203.41-3.106 1.332-5.862 2.59-8 1.147-1.95 2.536-3.333 4.034-4.048-.451.913-.842 2.108-1.168 3.496-.673 2.86-1.107 6.677-1.18 10.901.486.168.984.311 1.493.428Zm-.002 1.534a15.857 15.857 0 0 1-1.502-.387c.017 4.623.465 8.825 1.19 11.91.327 1.388.718 2.583 1.169 3.496-1.498-.715-2.887-2.097-4.035-4.048C7.845 39.698 6.75 35.584 6.75 31c0-.643.022-1.276.063-1.899a16.074 16.074 0 0 1-1.41-1.113c-.1.98-.153 1.986-.153 3.012 0 4.804 1.144 9.19 3.032 12.401.652 1.107 1.402 2.09 2.236 2.901C4.24 44.052-.25 38.051-.25 31c0-2.814.715-5.46 1.974-7.768a15.914 15.914 0 0 1-.741-1.699A17.667 17.667 0 0 0-1.75 31c0 9.803 7.947 17.75 17.75 17.75S33.75 40.803 33.75 31c0-3.48-1.002-6.727-2.733-9.467-.214.583-.462 1.15-.74 1.699A16.178 16.178 0 0 1 32.25 31c0 7.05-4.49 13.053-10.768 15.302.834-.811 1.584-1.794 2.236-2.9C25.605 40.19 26.75 35.803 26.75 31c0-1.026-.052-2.032-.152-3.012-.45.396-.92.768-1.411 1.113.041.623.063 1.256.063 1.899 0 4.584-1.095 8.698-2.825 11.64-1.148 1.952-2.537 3.334-4.035 4.049.451-.913.842-2.108 1.168-3.496.726-3.085 1.174-7.287 1.192-11.91-.492.153-.993.282-1.503.387-.04 4.386-.476 8.319-1.149 11.179-.355 1.509-.766 2.677-1.196 3.45-.466.838-.801.951-.902.951-.1 0-.436-.113-.902-.95-.43-.774-.841-1.942-1.196-3.451-.673-2.86-1.108-6.793-1.149-11.179Z" fill="url(#c)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.894 5.553a1 1 0 0 1-.447 1.342l-9.341 3.552c-.494.247.246.494 0 0-.247-.494-.494.247 0 0l8.447-5.341a1 1 0 0 1 1.341.447Z" fill="url(#d)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="b" x1="16" y1="0" x2="16" y2="33" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00172C"/>
|
||||
<stop offset=".803" stop-color="#5900EB"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="d" x1="19" y1="6" x2="11.5" y2="10" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#9969E8"/>
|
||||
<stop offset="1" stop-color="#FFA0D2" stop-opacity=".32"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="c" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 35.75 -57.5 0 16 13)">
|
||||
<stop offset=".14" stop-color="#fff"/>
|
||||
<stop offset=".514" stop-color="#fff" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<clipPath id="a">
|
||||
<rect width="32" height="32" rx="16" fill="#fff"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -1,45 +0,0 @@
|
||||
<svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="512" height="512" fill="#17171B"/>
|
||||
<g filter="url(#filter0_d_2_581)">
|
||||
<g filter="url(#filter1_d_2_581)">
|
||||
<path d="M245.149 203.293L203.339 245.099C197.335 251.104 197.335 260.839 203.339 266.843L245.149 308.649C251.153 314.654 260.889 314.654 266.894 308.649L308.703 266.843C314.708 260.839 314.708 251.104 308.703 245.099L266.894 203.293C260.889 197.289 251.153 197.289 245.149 203.293Z" fill="url(#paint0_linear_2_581)" shape-rendering="crispEdges"/>
|
||||
<path d="M256.012 418.999C245.279 419.037 234.644 416.944 224.727 412.839C214.809 408.735 205.805 402.703 198.238 395.091L189.35 386.204L227.258 348.298L236.147 357.186C241.401 362.438 248.527 365.388 255.956 365.388C263.386 365.388 270.512 362.438 275.766 357.186L357.161 275.797C362.401 270.541 365.344 263.421 365.344 256C365.344 248.578 362.401 241.458 357.161 236.202L316.519 195.608L354.428 157.702L395.136 198.408C410.418 213.724 419 234.476 419 256.111C419 277.746 410.418 298.498 395.136 313.814L313.719 395.202C306.152 402.788 297.155 408.797 287.25 412.881C277.344 416.966 266.727 419.045 256.012 418.999Z" fill="url(#paint1_linear_2_581)" shape-rendering="crispEdges"/>
|
||||
<path d="M157.529 354.408L116.91 313.703C109.33 306.126 103.317 297.131 99.2141 287.23C95.1116 277.329 93 266.717 93 256C93 245.283 95.1116 234.671 99.2141 224.77C103.317 214.869 109.33 205.873 116.91 198.297L198.304 116.908C205.882 109.328 214.878 103.316 224.78 99.2136C234.681 95.1114 245.294 93 256.012 93C266.73 93 277.342 95.1114 287.244 99.2136C297.146 103.316 306.142 109.328 313.719 116.908L322.608 125.796L284.699 163.701L275.811 154.814C270.556 149.562 263.43 146.611 256.001 146.611C248.571 146.611 241.445 149.562 236.191 154.814L154.796 236.202C152.196 238.802 150.133 241.889 148.726 245.285C147.319 248.682 146.595 252.323 146.595 256C146.595 259.676 147.319 263.317 148.726 266.714C150.133 270.111 152.196 273.197 154.796 275.797L195.505 316.502L157.529 354.408Z" fill="url(#paint2_linear_2_581)" shape-rendering="crispEdges"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_2_581" x="85" y="89" width="342" height="342" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feMorphology radius="4" operator="erode" in="SourceAlpha" result="effect1_dropShadow_2_581"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="6"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.086 0 0 0 0 0.1316 0 0 0 0 0.2 0 0 0 0.12 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2_581"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2_581" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_2_581" x="81" y="85" width="350" height="350" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="4"/>
|
||||
<feGaussianBlur stdDeviation="6"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.086 0 0 0 0 0.1316 0 0 0 0 0.2 0 0 0 0.2 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2_581"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2_581" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_2_581" x1="321" y1="127" x2="191" y2="385.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#2BC0F6"/>
|
||||
<stop offset="1" stop-color="#19BFFB" stop-opacity="0.82"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_2_581" x1="321" y1="127" x2="191" y2="385.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#2BC0F6"/>
|
||||
<stop offset="1" stop-color="#19BFFB" stop-opacity="0.82"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_2_581" x1="321" y1="127" x2="191" y2="385.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#2BC0F6"/>
|
||||
<stop offset="1" stop-color="#19BFFB" stop-opacity="0.82"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 2.1 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 279 KiB |
@@ -1,36 +0,0 @@
|
||||
import glob from 'fast-glob'
|
||||
|
||||
interface Article {
|
||||
title: string
|
||||
description: string
|
||||
author: string
|
||||
date: string
|
||||
}
|
||||
|
||||
export interface ArticleWithSlug extends Article {
|
||||
slug: string
|
||||
}
|
||||
|
||||
async function importArticle(
|
||||
articleFilename: string,
|
||||
): Promise<ArticleWithSlug> {
|
||||
let { article } = (await import(`../app/articles/${articleFilename}`)) as {
|
||||
default: React.ComponentType
|
||||
article: Article
|
||||
}
|
||||
|
||||
return {
|
||||
slug: articleFilename.replace(/(\/page)?\.mdx$/, ''),
|
||||
...article,
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllArticles() {
|
||||
let articleFilenames = await glob('*/page.mdx', {
|
||||
cwd: './src/app/articles',
|
||||
})
|
||||
|
||||
let articles = await Promise.all(articleFilenames.map(importArticle))
|
||||
|
||||
return articles.sort((a, z) => +new Date(z.date) - +new Date(a.date))
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export function formatDate(dateString: string) {
|
||||
return new Date(`${dateString}T00:00:00Z`).toLocaleDateString('en-US', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
timeZone: 'UTC',
|
||||
})
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
import { type PluginUtils } from 'tailwindcss/types/config'
|
||||
|
||||
export default function typographyStyles({ theme }: PluginUtils) {
|
||||
return {
|
||||
invert: {
|
||||
css: {
|
||||
'--tw-prose-body': 'var(--tw-prose-invert-body)',
|
||||
'--tw-prose-headings': 'var(--tw-prose-invert-headings)',
|
||||
'--tw-prose-links': 'var(--tw-prose-invert-links)',
|
||||
'--tw-prose-links-hover': 'var(--tw-prose-invert-links-hover)',
|
||||
'--tw-prose-underline': 'var(--tw-prose-invert-underline)',
|
||||
'--tw-prose-underline-hover': 'var(--tw-prose-invert-underline-hover)',
|
||||
'--tw-prose-bold': 'var(--tw-prose-invert-bold)',
|
||||
'--tw-prose-counters': 'var(--tw-prose-invert-counters)',
|
||||
'--tw-prose-bullets': 'var(--tw-prose-invert-bullets)',
|
||||
'--tw-prose-hr': 'var(--tw-prose-invert-hr)',
|
||||
'--tw-prose-quote-borders': 'var(--tw-prose-invert-quote-borders)',
|
||||
'--tw-prose-captions': 'var(--tw-prose-invert-captions)',
|
||||
'--tw-prose-code': 'var(--tw-prose-invert-code)',
|
||||
'--tw-prose-code-bg': 'var(--tw-prose-invert-code-bg)',
|
||||
'--tw-prose-pre-code': 'var(--tw-prose-invert-pre-code)',
|
||||
'--tw-prose-pre-bg': 'var(--tw-prose-invert-pre-bg)',
|
||||
'--tw-prose-pre-border': 'var(--tw-prose-invert-pre-border)',
|
||||
'--tw-prose-th-borders': 'var(--tw-prose-invert-th-borders)',
|
||||
'--tw-prose-td-borders': 'var(--tw-prose-invert-td-borders)',
|
||||
},
|
||||
},
|
||||
DEFAULT: {
|
||||
css: {
|
||||
'--tw-prose-body': theme('colors.zinc.600'),
|
||||
'--tw-prose-headings': theme('colors.zinc.900'),
|
||||
'--tw-prose-links': theme('colors.teal.500'),
|
||||
'--tw-prose-links-hover': theme('colors.teal.600'),
|
||||
'--tw-prose-underline': theme('colors.teal.500 / 0.2'),
|
||||
'--tw-prose-underline-hover': theme('colors.teal.500'),
|
||||
'--tw-prose-bold': theme('colors.zinc.900'),
|
||||
'--tw-prose-counters': theme('colors.zinc.900'),
|
||||
'--tw-prose-bullets': theme('colors.zinc.900'),
|
||||
'--tw-prose-hr': theme('colors.zinc.100'),
|
||||
'--tw-prose-quote-borders': theme('colors.zinc.200'),
|
||||
'--tw-prose-captions': theme('colors.zinc.400'),
|
||||
'--tw-prose-code': theme('colors.zinc.700'),
|
||||
'--tw-prose-code-bg': theme('colors.zinc.300 / 0.2'),
|
||||
'--tw-prose-pre-code': theme('colors.zinc.100'),
|
||||
'--tw-prose-pre-bg': theme('colors.zinc.900'),
|
||||
'--tw-prose-pre-border': 'transparent',
|
||||
'--tw-prose-th-borders': theme('colors.zinc.200'),
|
||||
'--tw-prose-td-borders': theme('colors.zinc.100'),
|
||||
|
||||
'--tw-prose-invert-body': theme('colors.zinc.400'),
|
||||
'--tw-prose-invert-headings': theme('colors.zinc.200'),
|
||||
'--tw-prose-invert-links': theme('colors.teal.400'),
|
||||
'--tw-prose-invert-links-hover': theme('colors.teal.400'),
|
||||
'--tw-prose-invert-underline': theme('colors.teal.400 / 0.3'),
|
||||
'--tw-prose-invert-underline-hover': theme('colors.teal.400'),
|
||||
'--tw-prose-invert-bold': theme('colors.zinc.200'),
|
||||
'--tw-prose-invert-counters': theme('colors.zinc.200'),
|
||||
'--tw-prose-invert-bullets': theme('colors.zinc.200'),
|
||||
'--tw-prose-invert-hr': theme('colors.zinc.700 / 0.4'),
|
||||
'--tw-prose-invert-quote-borders': theme('colors.zinc.500'),
|
||||
'--tw-prose-invert-captions': theme('colors.zinc.500'),
|
||||
'--tw-prose-invert-code': theme('colors.zinc.300'),
|
||||
'--tw-prose-invert-code-bg': theme('colors.zinc.200 / 0.05'),
|
||||
'--tw-prose-invert-pre-code': theme('colors.zinc.100'),
|
||||
'--tw-prose-invert-pre-bg': 'rgb(0 0 0 / 0.4)',
|
||||
'--tw-prose-invert-pre-border': theme('colors.zinc.200 / 0.1'),
|
||||
'--tw-prose-invert-th-borders': theme('colors.zinc.700'),
|
||||
'--tw-prose-invert-td-borders': theme('colors.zinc.800'),
|
||||
|
||||
// Base
|
||||
color: 'var(--tw-prose-body)',
|
||||
lineHeight: theme('lineHeight.7'),
|
||||
'> *': {
|
||||
marginTop: theme('spacing.10'),
|
||||
marginBottom: theme('spacing.10'),
|
||||
},
|
||||
p: {
|
||||
marginTop: theme('spacing.7'),
|
||||
marginBottom: theme('spacing.7'),
|
||||
},
|
||||
|
||||
// Headings
|
||||
'h2, h3': {
|
||||
color: 'var(--tw-prose-headings)',
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
},
|
||||
h2: {
|
||||
fontSize: theme('fontSize.xl')[0],
|
||||
lineHeight: theme('lineHeight.7'),
|
||||
marginTop: theme('spacing.20'),
|
||||
marginBottom: theme('spacing.4'),
|
||||
},
|
||||
h3: {
|
||||
fontSize: theme('fontSize.base')[0],
|
||||
lineHeight: theme('lineHeight.7'),
|
||||
marginTop: theme('spacing.16'),
|
||||
marginBottom: theme('spacing.4'),
|
||||
},
|
||||
':is(h2, h3) + *': {
|
||||
marginTop: 0,
|
||||
},
|
||||
|
||||
// Images
|
||||
img: {
|
||||
borderRadius: theme('borderRadius.3xl'),
|
||||
},
|
||||
|
||||
// Inline elements
|
||||
a: {
|
||||
color: 'var(--tw-prose-links)',
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
textDecoration: 'underline',
|
||||
textDecorationColor: 'var(--tw-prose-underline)',
|
||||
transitionProperty: 'color, text-decoration-color',
|
||||
transitionDuration: theme('transitionDuration.150'),
|
||||
transitionTimingFunction: theme('transitionTimingFunction.in-out'),
|
||||
},
|
||||
'a:hover': {
|
||||
color: 'var(--tw-prose-links-hover)',
|
||||
textDecorationColor: 'var(--tw-prose-underline-hover)',
|
||||
},
|
||||
strong: {
|
||||
color: 'var(--tw-prose-bold)',
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
},
|
||||
code: {
|
||||
display: 'inline-block',
|
||||
color: 'var(--tw-prose-code)',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
backgroundColor: 'var(--tw-prose-code-bg)',
|
||||
borderRadius: theme('borderRadius.lg'),
|
||||
paddingLeft: theme('spacing.1'),
|
||||
paddingRight: theme('spacing.1'),
|
||||
},
|
||||
'a code': {
|
||||
color: 'inherit',
|
||||
},
|
||||
':is(h2, h3) code': {
|
||||
fontWeight: theme('fontWeight.bold'),
|
||||
},
|
||||
|
||||
// Quotes
|
||||
blockquote: {
|
||||
paddingLeft: theme('spacing.6'),
|
||||
borderLeftWidth: theme('borderWidth.2'),
|
||||
borderLeftColor: 'var(--tw-prose-quote-borders)',
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
|
||||
// Figures
|
||||
figcaption: {
|
||||
color: 'var(--tw-prose-captions)',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
lineHeight: theme('lineHeight.6'),
|
||||
marginTop: theme('spacing.3'),
|
||||
},
|
||||
'figcaption > p': {
|
||||
margin: 0,
|
||||
},
|
||||
|
||||
// Lists
|
||||
ul: {
|
||||
listStyleType: 'disc',
|
||||
},
|
||||
ol: {
|
||||
listStyleType: 'decimal',
|
||||
},
|
||||
'ul, ol': {
|
||||
paddingLeft: theme('spacing.6'),
|
||||
},
|
||||
li: {
|
||||
marginTop: theme('spacing.6'),
|
||||
marginBottom: theme('spacing.6'),
|
||||
paddingLeft: theme('spacing[3.5]'),
|
||||
},
|
||||
'li::marker': {
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
},
|
||||
'ol > li::marker': {
|
||||
color: 'var(--tw-prose-counters)',
|
||||
},
|
||||
'ul > li::marker': {
|
||||
color: 'var(--tw-prose-bullets)',
|
||||
},
|
||||
'li :is(ol, ul)': {
|
||||
marginTop: theme('spacing.4'),
|
||||
marginBottom: theme('spacing.4'),
|
||||
},
|
||||
'li :is(li, p)': {
|
||||
marginTop: theme('spacing.3'),
|
||||
marginBottom: theme('spacing.3'),
|
||||
},
|
||||
|
||||
// Code blocks
|
||||
pre: {
|
||||
color: 'var(--tw-prose-pre-code)',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
fontWeight: theme('fontWeight.medium'),
|
||||
backgroundColor: 'var(--tw-prose-pre-bg)',
|
||||
borderRadius: theme('borderRadius.3xl'),
|
||||
padding: theme('spacing.8'),
|
||||
overflowX: 'auto',
|
||||
border: '1px solid',
|
||||
borderColor: 'var(--tw-prose-pre-border)',
|
||||
},
|
||||
'pre code': {
|
||||
display: 'inline',
|
||||
color: 'inherit',
|
||||
fontSize: 'inherit',
|
||||
fontWeight: 'inherit',
|
||||
backgroundColor: 'transparent',
|
||||
borderRadius: 0,
|
||||
padding: 0,
|
||||
},
|
||||
|
||||
// Horizontal rules
|
||||
hr: {
|
||||
marginTop: theme('spacing.20'),
|
||||
marginBottom: theme('spacing.20'),
|
||||
borderTopWidth: '1px',
|
||||
borderColor: 'var(--tw-prose-hr)',
|
||||
'@screen lg': {
|
||||
marginLeft: `calc(${theme('spacing.12')} * -1)`,
|
||||
marginRight: `calc(${theme('spacing.12')} * -1)`,
|
||||
},
|
||||
},
|
||||
|
||||
// Tables
|
||||
table: {
|
||||
width: '100%',
|
||||
tableLayout: 'auto',
|
||||
textAlign: 'left',
|
||||
fontSize: theme('fontSize.sm')[0],
|
||||
},
|
||||
thead: {
|
||||
borderBottomWidth: '1px',
|
||||
borderBottomColor: 'var(--tw-prose-th-borders)',
|
||||
},
|
||||
'thead th': {
|
||||
color: 'var(--tw-prose-headings)',
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
verticalAlign: 'bottom',
|
||||
paddingBottom: theme('spacing.2'),
|
||||
},
|
||||
'thead th:not(:first-child)': {
|
||||
paddingLeft: theme('spacing.2'),
|
||||
},
|
||||
'thead th:not(:last-child)': {
|
||||
paddingRight: theme('spacing.2'),
|
||||
},
|
||||
'tbody tr': {
|
||||
borderBottomWidth: '1px',
|
||||
borderBottomColor: 'var(--tw-prose-td-borders)',
|
||||
},
|
||||
'tbody tr:last-child': {
|
||||
borderBottomWidth: 0,
|
||||
},
|
||||
'tbody td': {
|
||||
verticalAlign: 'baseline',
|
||||
},
|
||||
tfoot: {
|
||||
borderTopWidth: '1px',
|
||||
borderTopColor: 'var(--tw-prose-th-borders)',
|
||||
},
|
||||
'tfoot td': {
|
||||
verticalAlign: 'top',
|
||||
},
|
||||
':is(tbody, tfoot) td': {
|
||||
paddingTop: theme('spacing.2'),
|
||||
paddingBottom: theme('spacing.2'),
|
||||
},
|
||||
':is(tbody, tfoot) td:not(:first-child)': {
|
||||
paddingLeft: theme('spacing.2'),
|
||||
},
|
||||
':is(tbody, tfoot) td:not(:last-child)': {
|
||||
paddingRight: theme('spacing.2'),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||