diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5178fe4..3bbb014 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,14 +6,14 @@ # Note that environment variables can be set in several places # See https://docs.gitlab.com/ci/variables/#cicd-variable-precedence stages: -- test -- secret-detection + - test + - secret-detection sast: - stage: test + stage: test include: -- template: Security/SAST.gitlab-ci.yml -- template: Security/Secret-Detection.gitlab-ci.yml + - template: Security/SAST.gitlab-ci.yml + - template: Security/Secret-Detection.gitlab-ci.yml variables: - SECRET_DETECTION_ENABLED: 'true' + SECRET_DETECTION_ENABLED: 'true' secret_detection: - stage: secret-detection + stage: secret-detection diff --git a/.prettierrc b/.prettierrc index 919a858..6de8911 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,7 @@ { - "tabWidth": 4, - "singleQuote": true, - "useTabs": true -} \ No newline at end of file + "tabWidth": 4, + "singleQuote": true, + "useTabs": true, + "plugins": ["prettier-plugin-tailwindcss"], + "tailwindStylesheet": "./app/globals.css" +} diff --git a/AGENTS.md b/AGENTS.md index 8bd0e39..caeace2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,5 @@ + # This is NOT the Next.js you know This version has breaking changes β€” APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. diff --git a/README.md b/README.md index ce6c4cf..7a63651 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,23 @@ # Cayden's Website -A professional portfolio and markdown-powered blog built with **Next.js 16**, **React 19**, **TypeScript**, and **Tailwind CSS 4**. +A developer portfolio built with **Next.js 16**, **React 19**, **TypeScript**, and **Tailwind CSS 4**, featuring a full-page React Bits `GradientWaves` ("Aurora") background behind sharp, translucent `1px` panels. -This site serves as a digital portfolio for Cayden, a Web Developer & Focus-Driven Leader, highlighting expertise in Clinical UI/UX, EHR systems, and large-scale logistics. It features a custom Nord-inspired design and a dynamic blog system. - -## πŸš€ Features - -- **Modern Tech Stack**: Built with React 19 and Next.js 16 using the App Router. -- **Tailwind CSS 4**: Modern, high-performance styling with the Nord color palette. -- **Markdown Blog**: Fully dynamic blog system powered by `remark` and `gray-matter`. -- **Responsive Design**: Elegant mobile-first navigation and layout. -- **Optimized Typography**: Custom Google Fonts (Cormorant Garamond & Poppins) via `next/font`. -- **Type-Safe**: Full TypeScript implementation for robust development. - -## πŸ› οΈ Tech Stack - -- **Framework**: [Next.js 16](https://nextjs.org/) (App Router) -- **Library**: [React 19](https://react.dev/) -- **Styling**: [Tailwind CSS 4](https://tailwindcss.com/) -- **Language**: [TypeScript](https://www.typescriptlang.org/) -- **Content**: Markdown with [Remark](https://github.com/remarkjs/remark) & [Gray-matter](https://github.com/jonschlinkert/gray-matter) -- **Icons**: [Heroicons](https://heroicons.com/) -- **Date Handling**: [Moment.js](https://momentjs.com/) - -## πŸ“‚ Project Structure +## Structure ```text -β”œβ”€β”€ app/ # Next.js App Router (pages, layouts, globals) -β”‚ β”œβ”€β”€ blog/ # Blog listing and dynamic article pages -β”‚ β”œβ”€β”€ contact/ # Contact page -β”‚ └── layout.tsx # Root layout with fonts and navigation -β”œβ”€β”€ articles/ # Markdown content for blog posts -β”œβ”€β”€ components/ # Reusable UI and Layout components -β”‚ β”œβ”€β”€ layout/ # Header, Footer, etc. -β”‚ └── ui/ # Generic UI elements -β”œβ”€β”€ lib/ # Utility functions and data fetching (markdown parser) -β”œβ”€β”€ public/ # Static assets (images, icons) -β”œβ”€β”€ types/ # TypeScript interface definitions -└── package.json # Dependencies and scripts +app/ Root layout, global styles, home page +components/ + layout/ Header, Footer, PageBackground + sections/ Hero, Projects, Feats, Contact + ui/ Reveal, SectionHeading + GradientWaves.tsx Vendored from React Bits +lib/site.ts All editable content and types ``` -## 🏁 Getting Started - -First, install the dependencies: +## Development ```bash -npm install -# or bun install +bun dev # http://localhost:3000 +bun run format ``` - -Then, run the development server: - -```bash -npm run dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -## πŸ“ Writing Articles - -To add a new blog post, create a `.md` file in the `/articles` directory with the following frontmatter: - -```markdown ---- -title: "Your Title" -date: "YYYY-MM-DD" -category: "Category Name" -description: "Brief summary of the article" ---- - -Your content here... -``` - -## πŸ“œ License - -This project is private and intended for portfolio demonstration. diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx deleted file mode 100644 index 48baa30..0000000 --- a/app/blog/[slug]/page.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import Link from "next/link" -import {ArrowLeftIcon} from "@heroicons/react/24/solid" -import {getArticleData, getSortedArticles} from "@/lib/articles"; - -export async function generateStaticParams() { - const articles = getSortedArticles(); - - return articles.map((article) => ({ - slug: article.id, - })); -} - -const Article = async ({params}: { params: Promise<{ slug: string }> }) => { - const {slug} = await params; - const articleData = await getArticleData(slug); - - return ( -
-
- - -

blog homepage

- -

{articleData.date.toString()}

-
-
-
- ) -} - -export default Article; \ No newline at end of file diff --git a/app/blog/page.tsx b/app/blog/page.tsx deleted file mode 100644 index f5a63bb..0000000 --- a/app/blog/page.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import {getCategorizedArticles} from "@/lib/articles"; -import ArticleItemList from "@/components/ArticleListItem"; - -export default async function BlogPage() { - const articles = getCategorizedArticles() - - return ( - <> -
-
-

Blog & thoughts

-
-
- {articles != null && Object.keys(articles).map((article) => ( - - ))} -
-
- - ); -} \ No newline at end of file diff --git a/app/contact/page.tsx b/app/contact/page.tsx deleted file mode 100644 index db573b3..0000000 --- a/app/contact/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default async function Contact() { - return ( -
-

Contact page, wip.

-
- ) -} \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index 419ac72..e128722 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,84 +1,74 @@ -@import "tailwindcss"; +@import 'tailwindcss'; @theme inline { - --font-cormorantGaramond: "Cormorant_Garamond", "sans-serif"; - --font-poppins: "Poppins", "sans-serif"; - - /* Nord: Polar Night */ - --color-nord-0: #2e3440; - --color-nord-1: #3b4252; - --color-nord-2: #4D535F; - --color-nord-3: #434c5e; - - /* Nord: Snow Storm */ - --color-nord-4: #d8dee9; - --color-nord-5: #e5e9f0; - --color-nord-6: #eceff4; - - /* Nord: Frost */ - --color-nord-7: #8fbcbb; - --color-nord-8: #88c0d0; - --color-nord-9: #81a1c1; - --color-nord-10: #5e81ac; - - /* Nord: Aurora */ - --color-nord-11: #bf616a; - --color-nord-12: #d08770; - --color-nord-13: #ebcb8b; - --color-nord-14: #a3be8c; - --color-nord-15: #b48ead; - - /* Optional semantic aliases */ - --color-polar-night-dark: #2e3440; - --color-polar-night: #3b4252; - --color-polar-night-light: #434c5e; - --color-polar-night-lighter: #4c566a; - - --color-snow-storm-dark: #d8dee9; - --color-snow-storm: #e5e9f0; - --color-snow-storm-light: #eceff4; - - --color-frost-green: #8fbcbb; - --color-frost-cyan: #88c0d0; - --color-frost-blue: #81a1c1; - --color-frost-deep-blue: #5e81ac; - - --color-aurora-red: #bf616a; - --color-aurora-orange: #d08770; - --color-aurora-yellow: #ebcb8b; - --color-aurora-green: #a3be8c; - --color-aurora-purple: #b48ead; + --font-display: + var(--font-space-grotesk), ui-sans-serif, system-ui, sans-serif; + --font-sans: var(--font-inter), ui-sans-serif, system-ui, sans-serif; + --font-mono: + var(--font-jetbrains-mono), ui-monospace, SFMono-Regular, monospace; } -.article { - @apply flex flex-col gap-3 font-poppins text-lg tracking-wide mb-20 +@theme { + --color-obsidian: #040807; + --color-aurora: #34d399; + --color-crest: #d1fae5; + --color-ink: #f2faf6; } -.article > h1 { - @apply text-4xl font-cormorantGaramond text-center tracking-tight; +@utility container-page { + @apply mx-auto max-w-5xl px-6; } -.article > h2 { - @apply text-3xl font-cormorantGaramond text-center tracking-tight; +@utility panel { + @apply border border-white/12 bg-obsidian/55 backdrop-blur-md; } -.article > ol { - @apply list-decimal flex flex-col mx-10 gap-2; +@utility btn-primary { + @apply border border-aurora bg-aurora px-5 py-2.5 text-obsidian transition-colors hover:border-crest hover:bg-crest; } -.article > ul { - @apply list-disc flex flex-col mx-10 gap-2; +@utility btn-ghost { + @apply border border-white/20 bg-obsidian/45 px-5 py-2.5 text-ink backdrop-blur-md transition-colors hover:border-aurora hover:text-aurora; } -.article > pre { - @apply bg-neutral-950 text-neutral-50 p-5 overflow-scroll; +html { + scroll-behavior: smooth; + scroll-padding-top: 4rem; } -.article > blockquote { - @apply pl-5 italic border-l-2 border-neutral-600; +::selection { + background: var(--color-aurora); + color: var(--color-obsidian); } -.article > p > img { - @apply w-1/2 mx-auto; +:focus-visible { + outline: 1px solid var(--color-aurora); + outline-offset: 2px; } +@media (scripting: enabled) { + .reveal { + opacity: 0; + transform: translateY(10px); + transition: + opacity 0.5s cubic-bezier(0.16, 1, 0.3, 1), + transform 0.5s cubic-bezier(0.16, 1, 0.3, 1); + transition-delay: var(--reveal-delay, 0ms); + } + + .reveal[data-visible='true'] { + opacity: 1; + transform: none; + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + .reveal { + opacity: 1; + transform: none; + transition: none; + } +} diff --git a/app/layout.tsx b/app/layout.tsx index 263b99c..70fbcf3 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,42 +1,53 @@ -import React from "react"; -import type {Metadata} from "next"; -import {Cormorant_Garamond, Poppins} from "next/font/google"; -import "./globals.css"; -import Header from "@/components/layout/layout-header"; +import type { ReactNode } from 'react'; +import type { Metadata, Viewport } from 'next'; +import { Inter, JetBrains_Mono, Space_Grotesk } from 'next/font/google'; +import './globals.css'; +import Header from '@/components/layout/layout-header'; +import Footer from '@/components/layout/layout-footer'; +import PageBackground from '@/components/layout/page-background'; -const cormorantGaramond = Cormorant_Garamond({ - subsets: ['latin'], - variable: "--font-cormorantGaramond", - weight: ['400'] -}) +const spaceGrotesk = Space_Grotesk({ + subsets: ['latin'], + variable: '--font-space-grotesk', +}); -const poppins = Poppins({ - subsets: ['latin'], - variable: "--font-poppins", - weight: ['400', '600'] -}) +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', +}); +const jetbrainsMono = JetBrains_Mono({ + subsets: ['latin'], + variable: '--font-jetbrains-mono', + weight: ['400', '500'], +}); export const metadata: Metadata = { - title: "sh ~./caydencodes.sh", - description: "Come and see what I'm all about.", + title: 'Cayden β€” Web Developer', + description: + 'Web developer building clinical interfaces and fleet software.', }; -export default async function RootLayout({ - children, - }: Readonly<{ - children: React.ReactNode; +export const viewport: Viewport = { + themeColor: '#040807', +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: ReactNode; }>) { - return ( - - -
- {children} - - - ); + return ( + + + +
+
{children}
+