diff --git a/ocp-docs/next-env.d.ts b/ocp-docs/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/ocp-docs/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/ocp-docs/next.config.mjs b/ocp-docs/next.config.mjs new file mode 100644 index 0000000..ccb9cd7 --- /dev/null +++ b/ocp-docs/next.config.mjs @@ -0,0 +1,12 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: 'export', + images: { + unoptimized: true, + }, + compiler: { + styledComponents: true, + }, +}; + +export default nextConfig; \ No newline at end of file diff --git a/ocp-docs/package.json b/ocp-docs/package.json new file mode 100644 index 0000000..a5435fa --- /dev/null +++ b/ocp-docs/package.json @@ -0,0 +1,34 @@ +{ + "name": "ocp-docs", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "clsx": "^2.1.1", + "framer-motion": "^11.2.10", + "lucide-react": "^0.395.0", + "next": "14.2.3", + "next-mdx-remote": "^4.4.1", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "styled-components": "^6.1.11", + "tailwind-merge": "^2.3.0" + }, + "devDependencies": { + "@tailwindcss/typography": "^0.5.19", + "@types/node": "^20.14.2", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "autoprefixer": "^10.4.19", + "eslint": "^8.57.0", + "eslint-config-next": "14.2.3", + "postcss": "^8.4.38", + "tailwindcss": "^3.4.4", + "typescript": "^5.4.5" + } +} diff --git a/ocp-docs/src/app/docs/[slug]/page.tsx b/ocp-docs/src/app/docs/[slug]/page.tsx new file mode 100644 index 0000000..332d7c9 --- /dev/null +++ b/ocp-docs/src/app/docs/[slug]/page.tsx @@ -0,0 +1,43 @@ +import { getAllDocSlugs, getDocBySlug } from "@/lib/docs"; +import { MDXRemote } from "next-mdx-remote/rsc"; +import { notFound } from "next/navigation"; +import GlassPanel from "@/components/GlassPanel"; + +export async function generateStaticParams() { + const slugs = await getAllDocSlugs(); + return slugs.map((slug) => ({ + slug: slug, + })); +} + +export default async function DocPage({ params }: { params: { slug: string } }) { + const source = await getDocBySlug(params.slug); + + if (!source) { + notFound(); + } + + return ( +
+
+ + +
+ + + +
+
+
+ ); +} diff --git a/ocp-docs/src/app/layout.tsx b/ocp-docs/src/app/layout.tsx new file mode 100644 index 0000000..3ad864b --- /dev/null +++ b/ocp-docs/src/app/layout.tsx @@ -0,0 +1,34 @@ +import type { Metadata } from "next"; +import { Inter, Montserrat } from "next/font/google"; +import "../styles/globals.css"; +import ThemeRegistry from "@/components/ThemeRegistry"; +import GridBackground from "@/components/GridBackground"; +import Header from "@/components/Header"; + +const inter = Inter({ subsets: ["latin"], variable: "--font-inter" }); +const montserrat = Montserrat({ subsets: ["latin"], variable: "--font-montserrat" }); + +export const metadata: Metadata = { + title: "Open Commerce Protocol | Documentation", + description: "The open source standard for agentic commerce and universal commerce protocol transactions.", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + +
+
+ {children} +
+ + + + ); +} \ No newline at end of file diff --git a/ocp-docs/src/app/page.tsx b/ocp-docs/src/app/page.tsx new file mode 100644 index 0000000..5ab9b3c --- /dev/null +++ b/ocp-docs/src/app/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function Home() { + redirect("/docs/home"); +} \ No newline at end of file diff --git a/ocp-docs/src/components/GlassPanel.tsx b/ocp-docs/src/components/GlassPanel.tsx new file mode 100644 index 0000000..8de1b7d --- /dev/null +++ b/ocp-docs/src/components/GlassPanel.tsx @@ -0,0 +1,20 @@ +'use client'; + +import React from 'react'; +import { motion } from 'framer-motion'; + +export default function GlassPanel({ children, className = '' }: { children: React.ReactNode, className?: string }) { + return ( + +
+
+ {children} +
+ + ); +} \ No newline at end of file diff --git a/ocp-docs/src/components/GridBackground.tsx b/ocp-docs/src/components/GridBackground.tsx new file mode 100644 index 0000000..f8cf086 --- /dev/null +++ b/ocp-docs/src/components/GridBackground.tsx @@ -0,0 +1,43 @@ +'use client'; + +import React, { useEffect } from 'react'; +import { motion, useMotionValue, useSpring } from 'framer-motion'; + +export default function GridBackground() { + const mouseX = useMotionValue(0); + const mouseY = useMotionValue(0); + + const springX = useSpring(mouseX, { stiffness: 50, damping: 20 }); + const springY = useSpring(mouseY, { stiffness: 50, damping: 20 }); + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + mouseX.set(e.clientX); + mouseY.set(e.clientY); + }; + + window.addEventListener('mousemove', handleMouseMove); + return () => window.removeEventListener('mousemove', handleMouseMove); + }, [mouseX, mouseY]); + + return ( +
+
+ +
+ ); +} \ No newline at end of file diff --git a/ocp-docs/src/components/Header.tsx b/ocp-docs/src/components/Header.tsx new file mode 100644 index 0000000..0ce712b --- /dev/null +++ b/ocp-docs/src/components/Header.tsx @@ -0,0 +1,99 @@ +'use client'; + +import React, { useState, useEffect } from 'react'; +import Link from 'next/link'; +import { motion, useScroll, useTransform } from 'framer-motion'; +import { Search, Github, Menu, ChevronDown } from 'lucide-react'; + +export default function Header() { + const [isScrolled, setIsScrolled] = useState(false); + const { scrollY } = useScroll(); + + const headerBg = useTransform( + scrollY, + [0, 50], + ['rgba(4, 7, 17, 0)', 'rgba(4, 7, 17, 0.8)'] + ); + + const headerBlur = useTransform( + scrollY, + [0, 50], + ['blur(0px)', 'blur(12px)'] + ); + + useEffect(() => { + const updateScroll = () => { + setIsScrolled(window.scrollY > 20); + }; + window.addEventListener('scroll', updateScroll); + return () => window.removeEventListener('scroll', updateScroll); + }, []); + + return ( + +
+ +
+ O +
+ + OCP Docs + + + + + +
+
+
+ +
+ +
+ ⌘K +
+
+ + +
+
+
+ ); +} + +function NavLink({ href, children }: { href: string; children: React.ReactNode }) { + return ( + + {children} + + + ); +} \ No newline at end of file diff --git a/ocp-docs/src/components/MDXComponents.tsx b/ocp-docs/src/components/MDXComponents.tsx new file mode 100644 index 0000000..824f3bc --- /dev/null +++ b/ocp-docs/src/components/MDXComponents.tsx @@ -0,0 +1,10 @@ +'use client'; + +import React from 'react'; + +export const mdxComponents = { + h1: (props: any) =>

, + h2: (props: any) =>

, + p: (props: any) =>

, + a: (props: any) => , +}; diff --git a/ocp-docs/src/components/RemoteMdx.tsx b/ocp-docs/src/components/RemoteMdx.tsx new file mode 100644 index 0000000..1526ac2 --- /dev/null +++ b/ocp-docs/src/components/RemoteMdx.tsx @@ -0,0 +1,8 @@ +'use client'; + +import { MDXRemote, MDXRemoteProps } from "next-mdx-remote"; +import { mdxComponents } from "./MDXComponents"; + +export default function RemoteMdx({ source }: { source: any }) { + return ; +} diff --git a/ocp-docs/src/components/ThemeRegistry.tsx b/ocp-docs/src/components/ThemeRegistry.tsx new file mode 100644 index 0000000..e671afb --- /dev/null +++ b/ocp-docs/src/components/ThemeRegistry.tsx @@ -0,0 +1,13 @@ +'use client'; + +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import darkTheme from '@/theme/dark'; + +export default function ThemeRegistry({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/ocp-docs/src/components/ui-kit/Button.jsx b/ocp-docs/src/components/ui-kit/Button.jsx new file mode 100644 index 0000000..6b69c16 --- /dev/null +++ b/ocp-docs/src/components/ui-kit/Button.jsx @@ -0,0 +1,226 @@ +/** + * Button Component + * + * Customizable button with multiple variants and sizes + */ + +import React from 'react'; +import styled, { css } from 'styled-components'; + +const getVariantStyles = (variant, theme) => { + const variants = { + primary: css` + background: ${theme.colors.primary}; + color: ${theme.colors.textInverse}; + border: none; + + &:hover:not(:disabled) { + background: ${theme.colors.primaryHover}; + } + + &:active:not(:disabled) { + background: ${theme.colors.primaryActive}; + } + `, + + secondary: css` + background: ${theme.colors.secondary}; + color: ${theme.colors.textInverse}; + border: none; + + &:hover:not(:disabled) { + background: ${theme.colors.secondaryHover}; + } + + &:active:not(:disabled) { + background: ${theme.colors.secondaryActive}; + } + `, + + outline: css` + background: transparent; + color: ${theme.colors.primary}; + border: 2px solid ${theme.colors.primary}; + + &:hover:not(:disabled) { + background: ${theme.colors.primaryLight}; + } + + &:active:not(:disabled) { + background: ${theme.colors.primaryLight}; + opacity: 0.8; + } + `, + + ghost: css` + background: transparent; + color: ${theme.colors.primary}; + border: none; + + &:hover:not(:disabled) { + background: ${theme.colors.surfaceHover}; + } + + &:active:not(:disabled) { + background: ${theme.colors.surfaceActive}; + } + `, + + danger: css` + background: ${theme.colors.error}; + color: ${theme.colors.textInverse}; + border: none; + + &:hover:not(:disabled) { + background: ${theme.colors.error}; + opacity: 0.9; + } + + &:active:not(:disabled) { + opacity: 0.8; + } + `, + + success: css` + background: ${theme.colors.success}; + color: ${theme.colors.textInverse}; + border: none; + + &:hover:not(:disabled) { + background: ${theme.colors.success}; + opacity: 0.9; + } + + &:active:not(:disabled) { + opacity: 0.8; + } + ` + }; + + return variants[variant] || variants.primary; +}; + +const getSizeStyles = (size, theme) => { + const sizes = { + small: css` + padding: ${theme.components.button.padding.small}; + font-size: ${theme.typography.fontSize.sm}; + height: 32px; + `, + + medium: css` + padding: ${theme.components.button.padding.medium}; + font-size: ${theme.typography.fontSize.md}; + height: 40px; + `, + + large: css` + padding: ${theme.components.button.padding.large}; + font-size: ${theme.typography.fontSize.lg}; + height: 48px; + ` + }; + + return sizes[size] || sizes.medium; +}; + +const StyledButton = styled.button` + display: inline-flex; + align-items: center; + justify-content: center; + gap: ${props => props.theme.spacing[2]}; + border-radius: ${props => props.theme.components.button.borderRadius}; + font-weight: ${props => props.theme.typography.fontWeight.semibold}; + cursor: pointer; + transition: all ${props => props.theme.transitions.duration.fast} ${props => props.theme.transitions.timing.easeOut}; + white-space: nowrap; + user-select: none; + + ${props => getVariantStyles(props.variant, props.theme)} + ${props => getSizeStyles(props.size, props.theme)} + + ${props => props.fullWidth && css` + width: 100%; + `} + + ${props => props.loading && css` + position: relative; + color: transparent; + pointer-events: none; + + &::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + border: 2px solid currentColor; + border-right-color: transparent; + border-radius: 50%; + animation: spin 0.6s linear infinite; + } + + @keyframes spin { + to { transform: rotate(360deg); } + } + `} + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + &:focus-visible { + outline: 2px solid ${props => props.theme.colors.borderFocus}; + outline-offset: 2px; + } +`; + +const IconWrapper = styled.span` + display: inline-flex; + align-items: center; + justify-content: center; + + svg { + width: 20px; + height: 20px; + } +`; + +const Button = ({ + children, + variant = 'primary', + size = 'medium', + icon, + iconPosition = 'left', + fullWidth = false, + loading = false, + disabled = false, + onClick, + type = 'button', + ariaLabel, + className +}) => { + return ( + + {icon && iconPosition === 'left' && ( + {icon} + )} + {children} + {icon && iconPosition === 'right' && ( + {icon} + )} + + ); +}; + +export default Button; diff --git a/ocp-docs/src/components/ui-kit/WalletCard.jsx b/ocp-docs/src/components/ui-kit/WalletCard.jsx new file mode 100644 index 0000000..e038a66 --- /dev/null +++ b/ocp-docs/src/components/ui-kit/WalletCard.jsx @@ -0,0 +1,128 @@ +/** + * WalletCard Component + * + * Displays wallet balance in a visually appealing card format + */ + +import React from 'react'; +import styled from 'styled-components'; + +const CardContainer = styled.div` + background: ${props => props.gradient || props.theme.components.walletCard.background}; + border-radius: ${props => props.theme.components.walletCard.borderRadius}; + padding: ${props => props.theme.components.walletCard.padding}; + color: ${props => props.theme.components.walletCard.textColor}; + box-shadow: ${props => props.theme.components.walletCard.shadow}; + min-height: 200px; + position: relative; + overflow: hidden; + transition: transform ${props => props.theme.transitions.duration.normal} ${props => props.theme.transitions.timing.easeOut}; + + &:hover { + transform: translateY(-4px); + box-shadow: ${props => props.theme.shadows.cardHover}; + } + + @media (max-width: ${props => props.theme.breakpoints.tablet}) { + padding: ${props => props.theme.spacing[5]}; + min-height: 180px; + } +`; + +const CardPattern = styled.div` + position: absolute; + top: 0; + right: 0; + width: 200px; + height: 200px; + opacity: 0.1; + background: ${props => { + if (props.pattern === 'dots') { + return 'radial-gradient(circle, white 1px, transparent 1px)'; + } else if (props.pattern === 'waves') { + return `url("data:image/svg+xml,%3Csvg width='100' height='100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 50 Q 25 0, 50 50 T 100 50' stroke='white' fill='none'/%3E%3C/svg%3E")`; + } + return 'none'; + }}; + background-size: ${props => props.pattern === 'dots' ? '20px 20px' : 'cover'}; +`; + +const BalanceLabel = styled.div` + font-size: ${props => props.theme.typography.fontSize.sm}; + opacity: 0.8; + margin-bottom: ${props => props.theme.spacing[2]}; + letter-spacing: 0.5px; + text-transform: uppercase; +`; + +const BalanceAmount = styled.div` + font-size: ${props => props.theme.typography.fontSize['4xl']}; + font-weight: ${props => props.theme.typography.fontWeight.bold}; + margin-bottom: ${props => props.theme.spacing[4]}; + line-height: 1; + + @media (max-width: ${props => props.theme.breakpoints.tablet}) { + font-size: ${props => props.theme.typography.fontSize['3xl']}; + } +`; + +const CardDetails = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-top: auto; + opacity: 0.9; +`; + +const CardNumber = styled.div` + font-family: ${props => props.theme.typography.fontFamily.monospace}; + font-size: ${props => props.theme.typography.fontSize.md}; + letter-spacing: 2px; +`; + +const CardExpiry = styled.div` + font-size: ${props => props.theme.typography.fontSize.sm}; +`; + +const WalletCard = ({ + balance = 0, + currency = 'USD', + cardNumber = '•••• 1234', + expiryDate, + gradient, + pattern = 'dots', + showPattern = true, + showDetails = true, + className, + onClick +}) => { + const formatBalance = (amount) => { + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: currency, + minimumFractionDigits: 2 + }).format(amount); + }; + + return ( + + {showPattern && } + + Available Balance + {formatBalance(balance)} + + {showDetails && ( + + {cardNumber} + {expiryDate && {expiryDate}} + + )} + + ); +}; + +export default WalletCard; diff --git a/ocp-docs/src/lib/docs.ts b/ocp-docs/src/lib/docs.ts new file mode 100644 index 0000000..0f57baf --- /dev/null +++ b/ocp-docs/src/lib/docs.ts @@ -0,0 +1,21 @@ +import fs from 'fs'; +import path from 'path'; + +const CONTENT_PATH = path.join(process.cwd(), '../docs/product-hub/content'); + +export async function getDocBySlug(slug: string) { + const filePath = path.join(CONTENT_PATH, `${slug}.md`); + if (!fs.existsSync(filePath)) { + return null; + } + const source = fs.readFileSync(filePath, 'utf8'); + return source; +} + +export async function getAllDocSlugs() { + if (!fs.existsSync(CONTENT_PATH)) return []; + const files = fs.readdirSync(CONTENT_PATH); + return files + .filter((file) => file.endsWith('.md')) + .map((file) => file.replace(/\.md$/, '')); +} diff --git a/ocp-docs/src/styles/globals.css b/ocp-docs/src/styles/globals.css new file mode 100644 index 0000000..09171f6 --- /dev/null +++ b/ocp-docs/src/styles/globals.css @@ -0,0 +1,20 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 4, 7, 17; + --background-end-rgb: 10, 15, 30; +} + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); + min-height: 100vh; +} \ No newline at end of file diff --git a/ocp-docs/src/theme/dark.js b/ocp-docs/src/theme/dark.js new file mode 100644 index 0000000..66d1971 --- /dev/null +++ b/ocp-docs/src/theme/dark.js @@ -0,0 +1,147 @@ +/** + * Dark Theme + * + * Dark mode theme for Open Wallet UI Kit + */ + +import tokens from './tokens'; + +const darkTheme = { + name: 'dark', + + // Colors + colors: { + // Brand (adjusted for dark mode) + primary: tokens.colors.primary[400], + primaryHover: tokens.colors.primary[300], + primaryActive: tokens.colors.primary[200], + primaryLight: tokens.colors.primary[900], + + secondary: '#BB86FC', // Material Dark Purple + secondaryHover: '#C89FFC', + secondaryActive: '#D5B8FC', + secondaryLight: tokens.colors.secondary[900], + + // Semantic (adjusted for dark mode) + success: tokens.colors.success[400], + successLight: tokens.colors.success[900], + warning: tokens.colors.warning[400], + warningLight: tokens.colors.warning[900], + error: tokens.colors.error[400], + errorLight: tokens.colors.error[900], + info: tokens.colors.info[400], + infoLight: tokens.colors.info[900], + + // Surface Colors (Dark) + background: '#121212', + backgroundSecondary: '#1E1E1E', + surface: '#1E1E1E', + surfaceHover: '#2C2C2C', + surfaceActive: '#383838', + + // Text Colors (Light on dark) + textPrimary: '#FFFFFF', + textSecondary: '#B3B3B3', + textDisabled: '#666666', + textInverse: '#000000', + + // Border Colors + border: '#2C2C2C', + borderHover: '#383838', + borderFocus: '#BB86FC', + + // Card specific + cardBackground: '#1E1E1E', + cardBorder: '#2C2C2C', + cardShadow: 'rgba(0, 0, 0, 0.3)', + + // Gradient presets (dark mode versions) + gradient: { + primary: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + card: 'linear-gradient(135deg, #2E3440 0%, #3B4252 100%)' + } + }, + + // Typography + typography: tokens.typography, + + // Spacing + spacing: tokens.spacing, + + // Border Radius + borderRadius: tokens.borderRadius, + + // Shadows (enhanced for dark mode) + shadows: { + none: 'none', + xs: '0 1px 2px 0 rgba(0, 0, 0, 0.4)', + sm: '0 2px 4px 0 rgba(0, 0, 0, 0.5)', + md: '0 4px 6px -1px rgba(0, 0, 0, 0.6), 0 2px 4px -1px rgba(0, 0, 0, 0.5)', + lg: '0 10px 15px -3px rgba(0, 0, 0, 0.7), 0 4px 6px -2px rgba(0, 0, 0, 0.5)', + xl: '0 20px 25px -5px rgba(0, 0, 0, 0.8), 0 10px 10px -5px rgba(0, 0, 0, 0.6)', + '2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.9)', + inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.5)', + card: '0 4px 20px rgba(0, 0, 0, 0.4)', + cardHover: '0 8px 30px rgba(0, 0, 0, 0.5)' + }, + + // Transitions + transitions: tokens.transitions, + + // Breakpoints + breakpoints: tokens.breakpoints, + + // Z-Index + zIndex: tokens.zIndex, + + // Opacity + opacity: tokens.opacity, + + // Component Specific (Dark Mode) + components: { + // Wallet Card + walletCard: { + background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + textColor: '#FFFFFF', + borderRadius: tokens.borderRadius['2xl'], + padding: tokens.spacing[6], + shadow: '0 4px 20px rgba(0, 0, 0, 0.4)' + }, + + // Button + button: { + borderRadius: tokens.borderRadius.button, + padding: { + small: `${tokens.spacing[2]} ${tokens.spacing[4]}`, + medium: `${tokens.spacing[3]} ${tokens.spacing[6]}`, + large: `${tokens.spacing[4]} ${tokens.spacing[8]}` + } + }, + + // Input + input: { + borderRadius: tokens.borderRadius.input, + padding: tokens.spacing[3], + borderColor: '#2C2C2C', + focusBorderColor: '#BB86FC', + background: '#1E1E1E' + }, + + // Transaction Item + transaction: { + padding: tokens.spacing[4], + borderRadius: tokens.borderRadius.md, + hoverBackground: '#2C2C2C' + }, + + // Modal + modal: { + background: '#1E1E1E', + backdropColor: 'rgba(0, 0, 0, 0.8)', + borderRadius: tokens.borderRadius.xl, + shadow: '0 25px 50px -12px rgba(0, 0, 0, 0.9)' + } + } +}; + +export default darkTheme; diff --git a/ocp-docs/src/theme/light.js b/ocp-docs/src/theme/light.js new file mode 100644 index 0000000..73ae331 --- /dev/null +++ b/ocp-docs/src/theme/light.js @@ -0,0 +1,135 @@ +/** + * Light Theme + * + * Default light theme for Open Wallet UI Kit + */ + +import tokens from './tokens'; + +const lightTheme = { + name: 'light', + + // Colors + colors: { + // Brand + primary: tokens.colors.primary[500], + primaryHover: tokens.colors.primary[600], + primaryActive: tokens.colors.primary[700], + primaryLight: tokens.colors.primary[100], + + secondary: tokens.colors.secondary[500], + secondaryHover: tokens.colors.secondary[600], + secondaryActive: tokens.colors.secondary[700], + secondaryLight: tokens.colors.secondary[100], + + // Semantic + success: tokens.colors.success[500], + successLight: tokens.colors.success[100], + warning: tokens.colors.warning[500], + warningLight: tokens.colors.warning[100], + error: tokens.colors.error[500], + errorLight: tokens.colors.error[100], + info: tokens.colors.info[500], + infoLight: tokens.colors.info[100], + + // Surface Colors + background: tokens.colors.neutral[0], + backgroundSecondary: tokens.colors.neutral[50], + surface: tokens.colors.neutral[0], + surfaceHover: tokens.colors.neutral[50], + surfaceActive: tokens.colors.neutral[100], + + // Text Colors + textPrimary: tokens.colors.neutral[900], + textSecondary: tokens.colors.neutral[600], + textDisabled: tokens.colors.neutral[400], + textInverse: tokens.colors.neutral[0], + + // Border Colors + border: tokens.colors.neutral[200], + borderHover: tokens.colors.neutral[300], + borderFocus: tokens.colors.primary[500], + + // Card specific + cardBackground: tokens.colors.neutral[0], + cardBorder: tokens.colors.neutral[200], + cardShadow: 'rgba(0, 0, 0, 0.08)', + + // Gradient presets + gradient: { + primary: tokens.colors.gradients.primary, + card: tokens.colors.gradients.premium + } + }, + + // Typography + typography: tokens.typography, + + // Spacing + spacing: tokens.spacing, + + // Border Radius + borderRadius: tokens.borderRadius, + + // Shadows + shadows: tokens.shadows, + + // Transitions + transitions: tokens.transitions, + + // Breakpoints + breakpoints: tokens.breakpoints, + + // Z-Index + zIndex: tokens.zIndex, + + // Opacity + opacity: tokens.opacity, + + // Component Specific + components: { + // Wallet Card + walletCard: { + background: tokens.colors.gradients.primary, + textColor: tokens.colors.neutral[0], + borderRadius: tokens.borderRadius['2xl'], + padding: tokens.spacing[6], + shadow: tokens.shadows.card + }, + + // Button + button: { + borderRadius: tokens.borderRadius.button, + padding: { + small: `${tokens.spacing[2]} ${tokens.spacing[4]}`, + medium: `${tokens.spacing[3]} ${tokens.spacing[6]}`, + large: `${tokens.spacing[4]} ${tokens.spacing[8]}` + } + }, + + // Input + input: { + borderRadius: tokens.borderRadius.input, + padding: tokens.spacing[3], + borderColor: tokens.colors.neutral[300], + focusBorderColor: tokens.colors.primary[500] + }, + + // Transaction Item + transaction: { + padding: tokens.spacing[4], + borderRadius: tokens.borderRadius.md, + hoverBackground: tokens.colors.neutral[50] + }, + + // Modal + modal: { + background: tokens.colors.neutral[0], + backdropColor: 'rgba(0, 0, 0, 0.5)', + borderRadius: tokens.borderRadius.xl, + shadow: tokens.shadows['2xl'] + } + } +}; + +export default lightTheme; diff --git a/ocp-docs/src/theme/tokens.js b/ocp-docs/src/theme/tokens.js new file mode 100644 index 0000000..2bb1f48 --- /dev/null +++ b/ocp-docs/src/theme/tokens.js @@ -0,0 +1,285 @@ +/** + * Design Tokens + * + * Core design system tokens for Open Wallet UI Kit + * These tokens define the visual foundation of the wallet interface + */ + +const tokens = { + // Color Palette + colors: { + // Brand Colors + primary: { + 50: '#E3F2FD', + 100: '#BBDEFB', + 200: '#90CAF9', + 300: '#64B5F6', + 400: '#42A5F5', + 500: '#007AFF', // Primary + 600: '#1E88E5', + 700: '#1976D2', + 800: '#1565C0', + 900: '#0D47A1' + }, + + secondary: { + 50: '#F3E5F5', + 100: '#E1BEE7', + 200: '#CE93D8', + 300: '#BA68C8', + 400: '#AB47BC', + 500: '#5856D6', // Secondary + 600: '#8E24AA', + 700: '#7B1FA2', + 800: '#6A1B9A', + 900: '#4A148C' + }, + + // Semantic Colors + success: { + 50: '#E8F5E9', + 100: '#C8E6C9', + 200: '#A5D6A7', + 300: '#81C784', + 400: '#66BB6A', + 500: '#34C759', // Success + 600: '#43A047', + 700: '#388E3C', + 800: '#2E7D32', + 900: '#1B5E20' + }, + + warning: { + 50: '#FFF3E0', + 100: '#FFE0B2', + 200: '#FFCC80', + 300: '#FFB74D', + 400: '#FFA726', + 500: '#FF9500', // Warning + 600: '#FB8C00', + 700: '#F57C00', + 800: '#EF6C00', + 900: '#E65100' + }, + + error: { + 50: '#FFEBEE', + 100: '#FFCDD2', + 200: '#EF9A9A', + 300: '#E57373', + 400: '#EF5350', + 500: '#FF3B30', // Error + 600: '#E53935', + 700: '#D32F2F', + 800: '#C62828', + 900: '#B71C1C' + }, + + info: { + 50: '#E1F5FE', + 100: '#B3E5FC', + 200: '#81D4FA', + 300: '#4FC3F7', + 400: '#29B6F6', + 500: '#5AC8FA', // Info + 600: '#039BE5', + 700: '#0288D1', + 800: '#0277BD', + 900: '#01579B' + }, + + // Neutral Colors (Light Theme) + neutral: { + 0: '#FFFFFF', + 50: '#FAFAFA', + 100: '#F5F5F5', + 200: '#EEEEEE', + 300: '#E0E0E0', + 400: '#BDBDBD', + 500: '#9E9E9E', + 600: '#757575', + 700: '#616161', + 800: '#424242', + 900: '#212121', + 1000: '#000000' + }, + + // Gradients + gradients: { + primary: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + sunset: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)', + ocean: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)', + forest: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)', + fire: 'linear-gradient(135deg, #fa709a 0%, #fee140 100%)', + premium: 'linear-gradient(135deg, #fad0c4 0%, #ffd1ff 100%)', + midnight: 'linear-gradient(135deg, #000428 0%, #004e92 100%)', + gold: 'linear-gradient(135deg, #f2994a 0%, #f2c94c 100%)' + } + }, + + // Typography + typography: { + fontFamily: { + primary: '-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif', + secondary: '"Inter", -apple-system, BlinkMacSystemFont, sans-serif', + monospace: '"SF Mono", "Monaco", "Cascadia Code", "Roboto Mono", monospace', + display: '"Poppins", -apple-system, BlinkMacSystemFont, sans-serif' + }, + + fontSize: { + xs: '12px', + sm: '14px', + md: '16px', + lg: '18px', + xl: '20px', + '2xl': '24px', + '3xl': '30px', + '4xl': '36px', + '5xl': '48px', + '6xl': '60px', + '7xl': '72px' + }, + + fontWeight: { + thin: 100, + light: 300, + regular: 400, + medium: 500, + semibold: 600, + bold: 700, + extrabold: 800, + black: 900 + }, + + lineHeight: { + none: 1, + tight: 1.25, + snug: 1.375, + normal: 1.5, + relaxed: 1.625, + loose: 2 + }, + + letterSpacing: { + tighter: '-0.05em', + tight: '-0.025em', + normal: '0', + wide: '0.025em', + wider: '0.05em', + widest: '0.1em' + } + }, + + // Spacing Scale + spacing: { + 0: '0', + 1: '4px', + 2: '8px', + 3: '12px', + 4: '16px', + 5: '20px', + 6: '24px', + 7: '28px', + 8: '32px', + 9: '36px', + 10: '40px', + 12: '48px', + 16: '64px', + 20: '80px', + 24: '96px', + 32: '128px' + }, + + // Border Radius + borderRadius: { + none: '0', + sm: '4px', + md: '8px', + lg: '12px', + xl: '16px', + '2xl': '20px', + '3xl': '24px', + full: '9999px', + // Card specific + card: '16px', + button: '8px', + input: '8px' + }, + + // Shadows + shadows: { + none: 'none', + xs: '0 1px 2px 0 rgba(0, 0, 0, 0.05)', + sm: '0 2px 4px 0 rgba(0, 0, 0, 0.06)', + md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', + lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', + xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', + '2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)', + // Card specific + card: '0 4px 20px rgba(0, 0, 0, 0.08)', + cardHover: '0 8px 30px rgba(0, 0, 0, 0.12)' + }, + + // Animation & Transitions + transitions: { + duration: { + fast: '150ms', + normal: '250ms', + slow: '350ms', + slower: '500ms' + }, + + timing: { + linear: 'linear', + easeIn: 'cubic-bezier(0.4, 0, 1, 1)', + easeOut: 'cubic-bezier(0, 0, 0.2, 1)', + easeInOut: 'cubic-bezier(0.4, 0, 0.2, 1)', + spring: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)' + } + }, + + // Breakpoints + breakpoints: { + mobile: '320px', + mobileLg: '480px', + tablet: '768px', + desktop: '1024px', + desktopLg: '1280px', + wide: '1440px', + ultraWide: '1920px' + }, + + // Z-Index Scale + zIndex: { + hide: -1, + base: 0, + dropdown: 1000, + sticky: 1100, + fixed: 1200, + modalBackdrop: 1300, + modal: 1400, + popover: 1500, + toast: 1600, + tooltip: 1700 + }, + + // Opacity Scale + opacity: { + 0: '0', + 5: '0.05', + 10: '0.1', + 20: '0.2', + 30: '0.3', + 40: '0.4', + 50: '0.5', + 60: '0.6', + 70: '0.7', + 80: '0.8', + 90: '0.9', + 95: '0.95', + 100: '1' + } +}; + +export default tokens; diff --git a/ocp-docs/tailwind.config.js b/ocp-docs/tailwind.config.js new file mode 100644 index 0000000..42dde9a --- /dev/null +++ b/ocp-docs/tailwind.config.js @@ -0,0 +1,35 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + colors: { + primary: { + DEFAULT: "#3b82f6", + 50: '#eff6ff', + 100: '#dbeafe', + 200: '#bfdbfe', + 300: '#93c5fd', + 400: '#60a5fa', + 500: '#3b82f6', + 600: '#2563eb', + 700: '#1d4ed8', + 800: '#1e40af', + 900: '#1e3a8a', + }, + accent: "#6366f1", + }, + fontFamily: { + sans: ['var(--font-inter)'], + montserrat: ['var(--font-montserrat)'], + }, + }, + }, + plugins: [ + require('@tailwindcss/typography'), + ], +}; diff --git a/ocp-docs/tsconfig.json b/ocp-docs/tsconfig.json new file mode 100644 index 0000000..56e0fa9 --- /dev/null +++ b/ocp-docs/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} \ No newline at end of file